@chakra-ui/react#SimpleGrid TypeScript Examples

The following examples show how to use @chakra-ui/react#SimpleGrid. 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: DimensionPanel.tsx    From openchakra with MIT License 6 votes vote down vote up
DimensionPanel = () => {
  const { setValueFromEvent } = useForm()
  const overflow = usePropsSelector('overflow')

  return (
    <>
      <SimpleGrid columns={2} spacing={1}>
        <TextControl hasColumn label="Width" name="width" />
        <TextControl hasColumn label="Height" name="height" />
      </SimpleGrid>

      <SimpleGrid columns={2} spacing={1}>
        <TextControl hasColumn label="Min W" name="minWidth" />
        <TextControl hasColumn label="Min H" name="minHeight" />

        <TextControl hasColumn label="Max W" name="maxWidth" />
        <TextControl hasColumn label="Max H" name="maxHeight" />
      </SimpleGrid>

      <FormControl label="Overflow">
        <Select
          size="sm"
          value={overflow || ''}
          onChange={setValueFromEvent}
          name="overflow"
        >
          <option>visible</option>
          <option>hidden</option>
          <option>scroll</option>
        </Select>
      </FormControl>
    </>
  )
}
Example #2
Source File: index.tsx    From ksana.in with Apache License 2.0 6 votes vote down vote up
export function Features() {
  return (
    <Container maxW={'5xl'} mx="auto" as="section" mt="16">
      <Stack p={4} spacing="16">
        <Heading textAlign="center" as="h3">
          Fitur Kunci Ksana.in
        </Heading>
        <SimpleGrid columns={{ base: 1, md: 3 }} spacing={10}>
          <Feature
            icon={<Icon as={FcLink} w={10} h={10} />}
            title={'Mempercantik Tautan'}
            text={'Tidak perlu lagi mengingat tautan yang panjang, pesan tautan dambaanmu sekarang'}
          />
          <Feature
            icon={<Icon as={FcTreeStructure} w={10} h={10} />}
            title={'Bagikan Tautan'}
            text={
              'Sangat mudah membagikan tautan ke berbagai sosial media dan pesan instan, langsung dari halaman dashboard'
            }
          />
          <Feature
            icon={<Icon as={FcBullish} w={10} h={10} />}
            title={'Pantau Statistik'}
            text={'Pantau jumlah pengguna yang mengunjungi tautanmu dengan mudah'}
          />
        </SimpleGrid>
      </Stack>
    </Container>
  )
}
Example #3
Source File: offline-data.tsx    From portfolio with MIT License 6 votes vote down vote up
OfflineData = () => {
  return (
    <PageSlideFade>
      <StaggerChildren>
        <SimpleGrid columns={[2, 2, 3]} spacing={4} mt={12}>
          {repositories.map((repo, index) => (
            <MotionBox whileHover={{ y: -5 }} key={index}>
              <RepositoryCard
                key={index}
                title={repo.title}
                description={repo.description}
                cover={repo.cover}
                blurHash={repo.blurHash}
                technologies={repo.technologies}
                url={repo.url}
                live={repo.live}
                stars={repo.stars}
                fork={repo.fork}
              />
            </MotionBox>
          ))}
        </SimpleGrid>
      </StaggerChildren>
    </PageSlideFade>
  );
}
Example #4
Source File: projects.tsx    From portfolio with MIT License 6 votes vote down vote up
Projects: React.FC<ProjectsProps> = ({ projects }) => {
  return (
    <VStack align="start" spacing={8}>
      <Header underlineColor={ORANGE} mt={0} mb={0}>
        Projects
      </Header>
      <AnimateSharedLayout>
        <SimpleGrid columns={1} spacing={4} mt={5} w="100%">
          {projects.map((project, index) => (
            <MotionBox whileHover={{ y: -5 }} key={index}>
              <ProjectCard
                key={project.id}
                title={project.title}
                description={project.desc}
                blurHash={project.blurHash}
                logo={project.logo}
                link={project.link}
                technologies={project.technologies}
              />
            </MotionBox>
          ))}
        </SimpleGrid>
      </AnimateSharedLayout>
    </VStack>
  );
}
Example #5
Source File: index.tsx    From ksana.in with Apache License 2.0 6 votes vote down vote up
export default function BlogIndex({ data }: IBlogProps) {
  return (
    <Layout>
      <MetaHead
        title="Beranda Blog | Ksana.in"
        description="Ksana.in adalah layanan pemendek tautan / URL yang gratis dan mudah untuk digunakan, buatan asli anak Indonesia"
      />

      <VStack spacing={4} textAlign="center" as="section" mt="32">
        <VStack spacing={4} textAlign="center">
          <Heading
            as="h1"
            fontWeight={700}
            fontSize={{ base: '3xl', sm: '4xl', md: '5xl' }}
            lineHeight={'110%'}
            color="orange.400"
          >
            Blog Ksana.in
          </Heading>
        </VStack>
      </VStack>

      <Container maxW={'4xl'} mx="auto" as="section" mt="8">
        <SimpleGrid columns={{ base: 1, md: 2 }} spacing={8} py={4}>
          {data &&
            data.length > 0 &&
            data.map((post: IPost) => <BlogCardPost key={post.slug} post={post} />)}
        </SimpleGrid>
      </Container>
    </Layout>
  )
}
Example #6
Source File: repositories-list.tsx    From notebook with MIT License 5 votes vote down vote up
RepositoriesList = () => {
  return (
    <>
      <AnimatePage>
        <Box minH={"50vh"}>
          <Flex p="2" justifyContent="center">
            <Heading
              as="h1"
              size="xl"
              bgGradient="linear(to-l, #7928CA, #FF0080)"
              bgClip="text"
              _focus={{ boxShadow: "none", outline: "none" }}
              _hover={{
                textDecoration: "none",
                bgGradient: "linear(to-r, red.500, yellow.500)"
              }}
            >
              Repositories
            </Heading>
          </Flex>
          {/* <SlideFade in={true} offsetY="50vh"> */}
          <SimpleGrid
            columns={[1, 2, 2, 3]}
            mt="40px"
            gridGap="10px"
            position="relative"
            overflow="hidden"
          >
            {repositoriesList.map((repo, index) => (
              <motion.div whileHover={{ y: -10 }} key={index}>
                <RepositoriesListItem repo={repo} />
              </motion.div>
            ))}
          </SimpleGrid>
          {/* </SlideFade> */}
        </Box>
      </AnimatePage>
    </>
  );
}
Example #7
Source File: live-data.tsx    From portfolio with MIT License 5 votes vote down vote up
LiveData = () => {
  const { get, loading, error, data } = useFetch("https://api.github.com");
  const [repos, setRepos] = useState([]);
  const [isLargerThan720] = useMediaQuery("(min-width: 720px)");
  const [isLargerThan982] = useMediaQuery("(min-width: 982px)");

  let columnWidth = 390;
  if (isLargerThan982) {
    columnWidth = 390;
  } else {
    if (isLargerThan720) {
      columnWidth = 300;
    } else {
      columnWidth = "100%";
    }
  }

  useEffect(() => {
    get("/users/MA-Ahmad/repos").then(res => {
      setRepos(
        res?.sort((a, b) => b.stargazers_count - a.stargazers_count).slice(0, 8)
      );
    });
  }, []);

  return (
    <PageSlideFade>
      {loading ? (
        <SimpleGrid columns={[1, 1, 2]} spacing={4} mt={4}>
          <CardSkeleton />
        </SimpleGrid>
      ) : (
        <Box mt={4}>
          <StackGrid columnWidth={columnWidth}>
            {repos?.map((repo, index) => (
              <RepositoryCard
                title={repo.name}
                description={repo.description}
                language={repo.language}
                url={repo.svn_url}
                created_at={repo.created_at}
                stargazers_count={repo.stargazers_count}
                forks_count={repo.forks_count}
              />
            ))}
          </StackGrid>
        </Box>
      )}
    </PageSlideFade>
  );
}
Example #8
Source File: App.tsx    From engine with MIT License 5 votes vote down vote up
App: view = ({
  data = observe.structure.data,
  viewsCount = observe.structure.count.views,
  producersCount = observe.structure.count.producers,
}) => {
  if (!data || !viewsCount || !producersCount) {
    return;
  }

  return (
    <ChakraProvider>
      <SimpleGrid columns={2}>
        <Box bg="gray.100" h="100vh">
          <Tabs>
            <TabList position="relative">
              <Tab>State</Tab>
              <Tab>
                Views <Tag>{viewsCount}</Tag>
              </Tab>
              <Tab>
                Producers <Tag>{producersCount}</Tag>
              </Tab>
              <Tab>Stats</Tab>
            </TabList>
            <TabPanels>
              <TabPanel pr="0">
                <List>
                  <Box overflowY="scroll" h="92vh">
                    <StateTree data={data} />
                  </Box>
                </List>
              </TabPanel>
              <TabPanel pr="0">
                <ViewsTab />
              </TabPanel>
              <TabPanel pr="0">
                <ProducersTab />
              </TabPanel>
              <TabPanel pr="0">
                <StatsTab />
              </TabPanel>
            </TabPanels>
          </Tabs>
        </Box>
        <Box bg="gray.200" borderLeft="solid 1px" borderColor="gray.300">
          <EditElement />
          <ElementDescription />
        </Box>
      </SimpleGrid>
    </ChakraProvider>
  );
}
Example #9
Source File: ColorPicker.tsx    From dope-monorepo with GNU General Public License v3.0 5 votes vote down vote up
ColorPicker = ({ colors, selectedColor, changeCallback }: ColorPickerProps) => {
  const [color, setColor] = useState(selectedColor ?? colors[0]);

  const handleColorInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    handleColorChange(e.target.value);
  };

  const handleColorChange = (color: string) => {
    setColor(color);
    if (typeof changeCallback === 'function') changeCallback(color);
  };

  return (
    <>
      <Popover variant="picker">
        <PopoverTrigger>
          <Button
            aria-label={color}
            background={color}
            height="64px"
            width="64px"
            padding={0}
            minWidth="unset"
            borderRadius={3}
          ></Button>
        </PopoverTrigger>
        <PopoverContent width="196px">
          <PopoverArrow bg={color} />
          <PopoverCloseButton color="white" />
          <PopoverHeader
            height="100px"
            backgroundColor={color}
            borderTopLeftRadius={5}
            borderTopRightRadius={5}
            color="white"
          >
            <Center height="100%">{color}</Center>
          </PopoverHeader>
          <PopoverBody height="96px">
            <SimpleGrid columns={5} spacing={2}>
              {colors.map(c => (
                <Button
                  key={c}
                  aria-label={c}
                  background={c}
                  height="32px"
                  width="32px"
                  padding={0}
                  minWidth="unset"
                  borderRadius={3}
                  _hover={{ background: c }}
                  onClick={() => {
                    handleColorChange(c);
                  }}
                ></Button>
              ))}
            </SimpleGrid>
            <Input
              borderRadius={3}
              marginTop={3}
              placeholder="red.100"
              size="sm"
              value={color}
              onChange={handleColorInputChange}
            />
          </PopoverBody>
        </PopoverContent>
      </Popover>
    </>
  );
}
Example #10
Source File: Controls.tsx    From dope-monorepo with GNU General Public License v3.0 5 votes vote down vote up
Controls = () => {
    const [, forceUpdate] = React.useReducer((i) => i + 1, 0);
    const [ selectedKey, setSelectedKey ] = React.useState<number>();
    const controlsManager = ControlsManager.getInstance();
    
    useEffect(() => {
        const onKeyDown = (e: KeyboardEvent) => {
            if (!selectedKey) return;
            
            controlsManager.setKey(selectedKey, e.keyCode);
            setSelectedKey(undefined);
            forceUpdate();
            console.log(selectedKey);
        }

        document.addEventListener('keydown', onKeyDown);
        return () => {
            document.removeEventListener('keydown', onKeyDown);
        }
    }, [selectedKey]);

    return (
        <div>
            <Text fontSize="2xl" paddingBottom="0px">
                Player
            </Text>
            <SimpleGrid columns={2} spacing={5} paddingBottom="2">
                {
                    Object.keys(controlsManager.playerKeys).map((key, i) => 
                        <Key key={i} keyName={key.charAt(0).toUpperCase() + key.slice(1)} keyCode={(controlsManager.playerKeys as any)[key]} selectedKey={selectedKey} onSelect={setSelectedKey} />)
                }
            </SimpleGrid>
            <Text fontSize="2xl" paddingBottom="0px">
                Misc
            </Text>
            <SimpleGrid columns={2} spacing={5}>
                <Key keyName="Chat" keyCode={controlsManager.chatKey} selectedKey={selectedKey} onSelect={setSelectedKey} />
                <Key keyName="Settings" keyCode={controlsManager.settingsKey} selectedKey={selectedKey} onSelect={setSelectedKey} />
            </SimpleGrid>
        </div>
    )
}
Example #11
Source File: Hustlers.tsx    From dope-monorepo with GNU General Public License v3.0 5 votes vote down vote up
Hustlers = () => {
    const [hustlers, setHustlers] = React.useState<any>();

    useEffect(() => {
        if (!(window.ethereum as any)?.selectedAddress) return;

        fetch(`https://api.dopewars.gg/wallets/${ethers.utils.getAddress(
            (window.ethereum as any).selectedAddress,
          )}/hustlers`).then(res => res.json()).then(res => setHustlers(res));
    }, []);
    
    return (
        <div>
            {hustlers ? <SimpleGrid columns={2} spacing={5} paddingBottom="8">
                {
                    hustlers.map((hustler: any, i: number) =>
                        <VStack key={i}>
                            <Text paddingBottom="0px">
                                {hustler.id} {hustler.name ? " - " + hustler.name : ""}
                            </Text>
                            <object width="70%" type="image/svg+xml" data={hustler.svg} />
                            { localStorage.getItem(`gameSelectedHustler_${(window.ethereum as any).selectedAddress}`) !== hustler.id ? <Popover>
                                <PopoverTrigger>
                                    <Button variant="primary">
                                        Set as selected hustler
                                    </Button>
                                </PopoverTrigger>
                                <PopoverContent>
                                    <PopoverArrow />
                                    <PopoverCloseButton />
                                    <PopoverHeader>Are you sure?</PopoverHeader>
                                    <PopoverBody>The game needs to be reloaded in order to modify your selected hustler</PopoverBody>
                                    <PopoverFooter>
                                        <Button variant="primary" onClick={() => {
                                            localStorage.setItem(`gameSelectedHustler_${(window.ethereum as any).selectedAddress}`, hustler.id);
                                            window.location.reload();
                                        }}>
                                            Confirm
                                        </Button>
                                    </PopoverFooter>
                                </PopoverContent>
                            </Popover> : undefined }
                        </VStack>
                    )
                }
            </SimpleGrid> : <Center padding="4">
                    <Spinner size="lg"/>
                </Center>
            }
            <Center>
                <a href="/inventory">
                    <Button variant="primary">
                        Details
                    </Button>
                </a>
            </Center>
        </div>
    )
}
Example #12
Source File: Hero.tsx    From ksana.in with Apache License 2.0 5 votes vote down vote up
export function Hero() {
  const textColor = useColorModeValue('gray.500', 'gray.300')

  return (
    <Box w={'full'}>
      <SimpleGrid columns={{ base: 1, md: 2 }} spacing={10}>
        <Container maxW={'6xl'} as="section" mt="32">
          <VStack spacing={{ base: 8, md: 10 }} px={{ base: 8, md: 10 }}>
            <Heading
              as="h2"
              textAlign="center"
              fontWeight={700}
              fontSize={{ base: '4xl', sm: '5xl', md: '6xl' }}
              lineHeight={'110%'}
            >
              Pemendek tautan yang{' '}
              <Text color="orange.400" as="span">
                mudah
              </Text>{' '}
              dan{' '}
              <Text color="orange.400" as="span">
                gratis
              </Text>
            </Heading>

            <Text
              as="span"
              textAlign="center"
              color={textColor}
              fontSize={{ base: 'lg', sm: 'xl', md: '2xl' }}
              lineHeight={'110%'}
            >
              Percantik tautanmu, jadikan agar mudah diingat, bagikan ke orang lain dengan percaya
              diri
            </Text>

            <Button
              size="lg"
              rounded="full"
              px={6}
              color={'white'}
              bg="orange.400"
              _hover={{
                bg: 'orange.500'
              }}
              as={'a'}
              href={login}
              leftIcon={<HiPlay />}
            >
              Coba sekarang
            </Button>
          </VStack>
        </Container>

        <Flex as="section" mt={{ base: 0, md: 20 }} justifyContent="center">
          <Image
            width={400}
            height={400}
            src={'/images/illustrations/ill_by_manypixels.svg'}
            alt="Women with Internet"
            priority={true}
          />
        </Flex>
      </SimpleGrid>
    </Box>
  )
}
Example #13
Source File: Items.tsx    From ksana.in with Apache License 2.0 4 votes vote down vote up
export function Items({ user, isFormVisible, onShowForm }: IUrlListProps) {
  const { data, isLoading = true } = useUrls(user?.id || '')
  const [searchText, setSearchText] = useState<string>('')
  const [view, setView] = useState<string>(VIEW.LIST)
  const [filteredData, setFilteredData] = useState<IUrl[]>(data)

  useEffect(() => {
    if (!isLoading) {
      if (searchText === '') {
        setFilteredData(data)
      } else if (searchText && searchText.length > 1) {
        const foundData = data.filter((d: IUrl) => {
          const containUrl = d.real_url.toLowerCase().includes(searchText.toLowerCase())
          const containSlug = d.slug.toLowerCase().includes(searchText.toLowerCase())
          return containSlug || containUrl
        })
        setFilteredData(foundData)
      }
    }
  }, [isLoading, searchText, data])

  if (isLoading) {
    return <LoadingSkeleton />
  }

  const handleSearch = (e: ChangeEvent<HTMLInputElement>) => {
    setSearchText(e.target.value)
  }

  const isGrid = view === VIEW.GRID
  const isList = view === VIEW.LIST

  const handleViewGrid = () => {
    setView(VIEW.GRID)
  }

  const handleViewList = () => {
    setView(VIEW.LIST)
  }

  return (
    <>
      {!isLoading && data && data.length > 0 ? (
        <>
          <TotalStats data={data} />
          <SearchInput onChangeSearch={handleSearch} searchText={searchText} />
          <Flex justifyContent="flex-end" alignItems="center">
            <ButtonGroup spacing="2" variant="outline">
              <IconButton
                variant="outline"
                borderColor={'orange.400'}
                color="orange.400"
                _hover={{
                  bg: 'orange.200'
                }}
                _focus={{
                  bg: 'orange.200'
                }}
                _active={{
                  bg: 'orange.400',
                  color: 'white'
                }}
                aria-label="View Grid"
                isActive={isGrid}
                onClick={handleViewGrid}
                icon={<HiViewGrid />}
              />
              <IconButton
                variant="outline"
                borderColor={'orange.400'}
                color="orange.400"
                _hover={{
                  bg: 'orange.200'
                }}
                _focus={{
                  bg: 'orange.200'
                }}
                _active={{
                  bg: 'orange.400',
                  color: 'white'
                }}
                aria-label="View List"
                isActive={isList}
                onClick={handleViewList}
                icon={<HiViewList />}
              />
            </ButtonGroup>
          </Flex>
          {filteredData.length > 0 ? (
            <SimpleGrid columns={isGrid ? 2 : 1} spacing={2}>
              {filteredData.map((urlItem: IUrl) => (
                <Item data={urlItem} user={user} key={urlItem.id} />
              ))}
            </SimpleGrid>
          ) : (
            <ErrorDataNotFound
              useCta={false}
              title="Tidak menemukan apapun nih, coba kata kunci lain!"
            />
          )}
        </>
      ) : (
        <>{!isFormVisible ? <ErrorDataNotFound useCta ctaAction={onShowForm} /> : null}</>
      )}
    </>
  )
}
Example #14
Source File: Music.tsx    From dope-monorepo with GNU General Public License v3.0 4 votes vote down vote up
Music = (props: {
    musicManager: MusicManager
}) => {
    const [, forceUpdate] = React.useReducer((i) => i + 1, 0);
    const [ songProgress, setSongProgress ] = React.useState(props.musicManager.currentSong?.song.seek);

    React.useEffect(() => {
        let intervalId: NodeJS.Timer;
        if (props.musicManager.currentSong)
            intervalId = setInterval(() => {
                setSongProgress(props.musicManager.currentSong!.song.seek);
            });
        
        return () => {
            clearInterval(intervalId);
        }
    }, []);

    console.log(props.musicManager.soundManager.volume);

    return (
        <div>
            <HStack style={{
                alignItems: "stretch",
                marginBlock: "1rem",
            }}>
                {
                    props.musicManager.currentSong && 
                    <Container style={{
                        padding: "0.5rem",
                        minWidth: "60%",
                        borderRadius: "7px",
                        backgroundColor: "rgba(255,255,255,0.5)",
                    }}>
                        <HStack>
                            <div style={{
                                width: "70%"
                            }}>
                                <div>
                                    <Text fontWeight="bold" paddingBottom="0px">
                                        Currently playing
                                    </Text>
                                    <Text paddingBottom="0px">
                                        {props.musicManager.currentSong.name}
                                    </Text>
                                    <Slider 
                                        value={songProgress} 
                                        onChange={(v) => {
                                            props.musicManager.currentSong?.song.setSeek(v);
                                        }} 
                                        max={props.musicManager.currentSong!.song.duration}
                                        width="100%"
                                    >
                                        <SliderTrack>
                                            <SliderFilledTrack />
                                        </SliderTrack>
                                        <SliderThumb style={{
                                            boxShadow: "none"
                                        }} />
                                    </Slider>
                                </div>
                                {
                                    props.musicManager.upcomingSong && <div>
                                    <Text fontWeight="bold" paddingBottom="0px">
                                        Upcoming
                                    </Text>
                                    <Text>
                                        {props.musicManager.upcomingSong.name}
                                    </Text>
                                </div>
                                }
                            </div>
                            <Spacer />
                            <VStack>
                                {
                                    <Button variant="primary" onClick={() => {
                                        props.musicManager.currentSong!.song.isPaused ? 
                                            props.musicManager.currentSong!.song.resume() : 
                                                props.musicManager.currentSong!.song.pause();
                                        forceUpdate();
                                    }}>
                                        {
                                            props.musicManager.currentSong.song.isPaused ? "Resume" : "Pause"
                                        }
                                    </Button>
                                }
                                <Button variant="primary" onClick={() => {
                                    props.musicManager.shuffle(undefined, undefined, false);
                                    forceUpdate();
                                }}>
                                    Skip
                                </Button>
                            </VStack>
                        </HStack>
                    </Container>
                }
                <Container style={{
                    padding: "0.5rem",
                    borderRadius: "7px",
                    minHeight: "100%",
                    backgroundColor: "rgba(255,255,255,0.5)",
                }}>
                    <div>
                        <div>
                            <Text fontWeight="bold" paddingBottom="0px">
                                Volume
                            </Text>
                            <Slider
                                defaultValue={props.musicManager.soundManager.volume * 100}
                                onChange={(v) => props.musicManager.soundManager.setVolume(v / 100)}
                                width="70%"
                            >
                                <SliderTrack>
                                    <SliderFilledTrack />
                                </SliderTrack>
                                <SliderThumb />
                            </Slider>
                        </div>
                        <div>
                            <Text fontWeight="bold" paddingBottom="0px">
                                Rate
                            </Text>
                            <Slider
                                defaultValue={props.musicManager.soundManager.rate * 100}
                                onChange={(v) => props.musicManager.soundManager.setRate(v / 100)}
                                width="70%"
                                max={200}
                            >
                                <SliderTrack>
                                    <SliderFilledTrack />
                                </SliderTrack>
                                <SliderThumb />
                            </Slider>
                        </div>
                        <div>
                            <Text fontWeight="bold" paddingBottom="0px">
                                Detune
                            </Text>
                            <Slider
                                defaultValue={props.musicManager.soundManager.detune}
                                onChange={(v) => props.musicManager.soundManager.setDetune(v)}
                                width="70%"
                                max={1200}
                                min={-1200}
                            >
                                <SliderTrack>
                                    <SliderFilledTrack />
                                </SliderTrack>
                                <SliderThumb />
                            </Slider>
                        </div>
                    </div>
                </Container>
            </HStack>
            
            <SimpleGrid columns={2} spacing={5} paddingBottom="2">
                {
                    props.musicManager.songs.map((song, i) => 
                        <SongComponent key={i} song={song} musicManager={props.musicManager} updateState={forceUpdate} />)
                }
            </SimpleGrid>
        </div>
    )
}
Example #15
Source File: Footer.tsx    From ksana.in with Apache License 2.0 4 votes vote down vote up
export function Footer({ withBacklink }: IFooterProps) {
  const boxColor = useColorModeValue('gray.700', 'gray.200')

  return (
    <Box color={boxColor} as="footer" width="100%">
      {withBacklink ? (
        <svg
          className="waves"
          xmlns="http://www.w3.org/2000/svg"
          viewBox="0 24 150 28"
          preserveAspectRatio="none"
          shapeRendering="auto"
        >
          <defs>
            <path
              id="gentle-wave"
              d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z"
            />
          </defs>
          <g className="parallax">
            <use xlinkHref="#gentle-wave" x="5" y="0" fill="rgba(237, 137, 54, 0.18)" />
            <use xlinkHref="#gentle-wave" x="20" y="3" fill="rgba(237, 137, 54, 0.3)" />
            <use xlinkHref="#gentle-wave" x="48" y="5" fill="rgba(237, 137, 54, 0.4)" />
            <use xlinkHref="#gentle-wave" x="90" y="30" fill="rgba(237, 137, 54, 0.7)" />
          </g>
        </svg>
      ) : null}

      {withBacklink ? (
        <Box width="100%">
          <Container maxW={'5xl'}>
            <SimpleGrid columns={{ base: 1, md: 2 }} spacing={8} py={4}>
              <Stack align={'flex-start'}>
                <Text fontWeight="700" color="orange.400" fontSize={'lg'} mb={2}>
                  Lebih banyak
                </Text>
                <Link href={tentang}>Tentang Ksana.in</Link>
                <Link href={blog}>Blog</Link>
                <Link href={login}>Masuk</Link>
              </Stack>

              <Stack align={'flex-start'}>
                <Text fontWeight="700" color="orange.400" fontSize={'lg'} mb={2}>
                  Kebijakan
                </Text>
                <Link href={kebijakanPrivasi}>Kebijakan Privasi</Link>
                <Link href={ketentuanLayanan}>Ketentuan Layanan</Link>
              </Stack>

              <Stack align={'flex-start'}>
                <Text fontWeight="700" color="orange.400" fontSize={'lg'} mb={2}>
                  Sumber daya
                </Text>
                <Link
                  href="https://github.com/mazipan/ksana.in/issues/new"
                  target="_blank"
                  rel="noopener noreferrer"
                >
                  Laporkan Isu
                </Link>
                <Link href={splitbeeAnalytics} target="_blank" rel="noopener noreferrer">
                  Statistik Ksana.in
                </Link>
                <Link
                  href="https://trakteer.id/mazipan/tip?utm_source=ksana"
                  target="_blank"
                  title="Dukung Ksana.in"
                  rel="noopener noreferrer"
                >
                  Dukung Ksana.in
                </Link>
              </Stack>

              <Stack align={'flex-start'}>
                <Text fontWeight="700" color="orange.400" fontSize={'lg'} mb={2}>
                  Karya lain
                </Text>

                <Link
                  href="https://www.baca-quran.id/?utm_source=ksana"
                  target="_blank"
                  title="Cek Baca-Quran.id"
                  rel="noopener noreferrer"
                >
                  Baca-Quran.id
                </Link>
                <Link
                  href="https://pramuka.online/?utm_source=ksana"
                  target="_blank"
                  title="Cek Pramuka.Online"
                  rel="noopener noreferrer"
                >
                  Pramuka.Online
                </Link>
              </Stack>
            </SimpleGrid>
          </Container>
        </Box>
      ) : null}

      <Box bg="orange.400" width="100%">
        <Container maxW={'5xl'}>
          <Flex
            as={Stack}
            py={4}
            alignItems="center"
            direction={{ base: 'column', md: 'row' }}
            spacing={4}
            justify={{ md: 'space-between' }}
            align={{ md: 'center' }}
          >
            <Text>
              © 2021{' '}
              <Link href={'/'} textDecoration="underline">
                {BRAND}
              </Link>{' '}
              dibuat oleh{' '}
              <Link
                textDecoration="underline"
                href={'https://mazipan.space/'}
                target="_blank"
                rel="noopener noreferrer"
              >
                Irfan Maulana
              </Link>
            </Text>
          </Flex>
        </Container>
      </Box>
    </Box>
  )
}
Example #16
Source File: tentang.tsx    From ksana.in with Apache License 2.0 4 votes vote down vote up
function About() {
  const colorText = useColorModeValue('gray.500', 'gray.300')
  const bgBox = useColorModeValue('white', 'gray.800')

  return (
    <Layout>
      <MetaHead
        title="Tentang Kami | Ksana.in"
        description="Ksana.in adalah layanan pemendek tautan / URL yang gratis dan mudah untuk digunakan, buatan asli anak Indonesia"
      />
      <VStack spacing={2} textAlign="center" as="section" mt="32">
        <Heading
          as="h1"
          fontWeight={700}
          fontSize={{ base: '3xl', sm: '4xl', md: '5xl' }}
          lineHeight={'110%'}
          color="orange.400"
        >
          Tentang Kami
        </Heading>
        <Image width={200} height={122} src={'/images/orange/ksana.svg'} alt="Ksana.in" />
      </VStack>
      <Container maxW={'4xl'} mx="auto" as="section" mt="8">
        <VStack spacing={4}>
          <Text color={colorText}>
            {BRAND} adalah layanan pemendek tautan / URL yang gratis dan mudah untuk digunakan.
            Layanan ini diinisiasi oleh Irfan Maulana dalam rangka mempelajari layanan baru dari
            Supabase.io, membuat sesuatu projek nyata untuk bisa mengimplementasikan langsung apa
            yang memang sedang ingin dipelajari.
          </Text>
          <Text color={colorText}>
            {BRAND} tidak bisa dibuat tanpa beberapa layanan dan alat bantu berikut:
          </Text>
          <SimpleGrid columns={{ base: 2, md: 4 }} spacing={2}>
            {tools.map((t: ITools) => (
              <Box
                key={t.title}
                bg={bgBox}
                boxShadow={'2xl'}
                rounded={'md'}
                overflow={'hidden'}
                p={6}
              >
                <Link
                  href={t.url}
                  target="_blank"
                  rel="noopener noreferrer"
                  color="orange.400"
                  fontSize={{ base: 'lg', md: 'xl' }}
                  fontWeight="bold"
                >
                  {t.title}
                </Link>
              </Box>
            ))}
          </SimpleGrid>
          <Text color={colorText}>
            {BRAND} dibuat secara terbuka agar bisa dijadikan bahan pembelajaran bersama, semua kode
            dan assets tersedia gratis untuk semua pembelajar
          </Text>
          <HStack
            w="80%"
            bg={bgBox}
            boxShadow={'2xl'}
            rounded={'md'}
            overflow={'hidden'}
            p={6}
            spacing={4}
            justifyContent="space-between"
            wrap="wrap"
          >
            <Link
              href={github}
              target="_blank"
              rel="noopener noreferrer"
              color="orange.400"
              fontSize={{ base: 'lg', md: 'xl' }}
              fontWeight="bold"
            >
              ksana.in/gh
            </Link>
            <ImageChakra
              src={'https://img.shields.io/github/stars/mazipan/ksana.in?style=social'}
            />
          </HStack>
          <Text color={colorText}>
            Untuk mendukung saya dan {BRAND} terus berkreasi membuat kode terbuka lainnya, kalian
            bisa mengirimkan dana untuk membeli kopi melalui{' '}
            <Link
              target="_blank"
              rel="noopener noreferrer"
              color="orange.400"
              fontWeight="bold"
              href="https://trakteer.id/mazipan/tip?utm_source=ksana"
            >
              Trakteer.id
            </Link>
          </Text>
          <Text color={colorText} mt="16">
            Dari pembuat {BRAND}
            {', '}
            <Link
              target="_blank"
              rel="noopener noreferrer"
              color="orange.400"
              fontWeight="bold"
              href="https://mazipan.space"
            >
              Irfan Maulana
            </Link>
          </Text>
        </VStack>
      </Container>
    </Layout>
  )
}
Example #17
Source File: CustomPropsPanel.tsx    From openchakra with MIT License 4 votes vote down vote up
CustomPropsPanel = () => {
  const dispatch = useDispatch()
  const inputRef = useRef<HTMLInputElement>(null)

  const activePropsRef = useInspectorState()
  const { props, id } = useSelector(getSelectedComponent)
  const { setValue } = useForm()

  const [quickProps, setQuickProps] = useState('')
  const [hasError, setError] = useState(false)

  const onDelete = (propsName: string) => {
    dispatch.components.deleteProps({
      id,
      name: propsName,
    })
  }

  const activeProps = activePropsRef || []
  const customProps = Object.keys(props).filter(
    propsName => !activeProps.includes(propsName),
  )

  return (
    <>
      <form
        onSubmit={(event: FormEvent) => {
          event.preventDefault()

          const [name, value] = quickProps.split(SEPARATOR)

          if (name && value) {
            setValue(name, value)
            setQuickProps('')
            setError(false)
          } else {
            setError(true)
          }
        }}
      >
        <InputGroup mb={3} size="sm">
          <InputRightElement
            children={<Box as={IoIosFlash} color="gray.300" />}
          />
          <Input
            ref={inputRef}
            isInvalid={hasError}
            value={quickProps}
            placeholder={`props${SEPARATOR}value`}
            onChange={(event: ChangeEvent<HTMLInputElement>) =>
              setQuickProps(event.target.value)
            }
          />
        </InputGroup>
      </form>

      {customProps.map((propsName, i) => (
        <Flex
          key={propsName}
          alignItems="center"
          px={2}
          bg={i % 2 === 0 ? 'white' : 'gray.50'}
          fontSize="xs"
          justifyContent="space-between"
        >
          <SimpleGrid width="100%" columns={2} spacing={1}>
            <Box fontWeight="bold">{propsName}</Box>
            <Box>{props[propsName]}</Box>
          </SimpleGrid>

          <ButtonGroup display="flex" size="xs" isAttached>
            <IconButton
              onClick={() => {
                setQuickProps(`${propsName}=`)
                if (inputRef.current) {
                  inputRef.current.focus()
                }
              }}
              variant="ghost"
              size="xs"
              aria-label="edit"
              icon={<EditIcon path="" />}
            />
            <IconButton
              onClick={() => onDelete(propsName)}
              variant="ghost"
              size="xs"
              aria-label="delete"
              icon={<SmallCloseIcon path="" />}
            />
          </ButtonGroup>
        </Flex>
      ))}
    </>
  )
}
Example #18
Source File: PaddingPanel.tsx    From openchakra with MIT License 4 votes vote down vote up
PaddingPanel = ({ type }: PaddingPanelPropsType) => {
  const { setValueFromEvent } = useForm()

  const all = usePropsSelector(ATTRIBUTES[type].all)
  const left = usePropsSelector(ATTRIBUTES[type].left)
  const right = usePropsSelector(ATTRIBUTES[type].right)
  const bottom = usePropsSelector(ATTRIBUTES[type].bottom)
  const top = usePropsSelector(ATTRIBUTES[type].top)

  return (
    <Box mb={4}>
      <FormControl>
        <FormLabel fontSize="xs" htmlFor="width" textTransform="capitalize">
          {type}
        </FormLabel>

        <InputGroup size="sm">
          <Input
            mb={1}
            placeholder="All"
            size="sm"
            type="text"
            name={ATTRIBUTES[type].all}
            value={all || ''}
            onChange={setValueFromEvent}
          />
        </InputGroup>

        <SimpleGrid columns={2} spacing={1}>
          <InputGroup size="sm">
            <InputLeftElement
              children={
                <ArrowBackIcon path="" fontSize="md" color="gray.300" />
              }
            />
            <Input
              placeholder="left"
              size="sm"
              type="text"
              name={ATTRIBUTES[type].left}
              value={left || ''}
              onChange={setValueFromEvent}
              autoComplete="off"
            />
          </InputGroup>

          <InputGroup size="sm">
            <InputLeftElement
              children={
                <ArrowForwardIcon path="" fontSize="md" color="gray.300" />
              }
            />
            <Input
              placeholder="right"
              size="sm"
              type="text"
              value={right || ''}
              name={ATTRIBUTES[type].right}
              onChange={setValueFromEvent}
              autoComplete="off"
            />
          </InputGroup>

          <InputGroup size="sm">
            <InputLeftElement
              children={<ArrowUpIcon path="" fontSize="md" color="gray.300" />}
            />
            <Input
              placeholder="top"
              size="sm"
              type="text"
              value={top || ''}
              name={ATTRIBUTES[type].top}
              onChange={setValueFromEvent}
              autoComplete="off"
            />
          </InputGroup>

          <InputGroup size="sm">
            <InputLeftElement
              children={
                <ChevronDownIcon path="" fontSize="md" color="gray.300" />
              }
            />
            <Input
              placeholder="bottom"
              size="sm"
              type="text"
              value={bottom || ''}
              name={ATTRIBUTES[type].bottom}
              onChange={setValueFromEvent}
              autoComplete="off"
            />
          </InputGroup>
        </SimpleGrid>
      </FormControl>
    </Box>
  )
}
Example #19
Source File: ConnectButton.tsx    From eth-dapps-nextjs-boiletplate with MIT License 4 votes vote down vote up
export default function ConnectButton({ handleOpenModal }: Props) {
  const { globalState, dispatch } = useContext(globalContext)
  const [ etherBalance, setEtherBalance ] = useState(0)
  const [ loading, setLoading ] = useState(false)
  const [ walletConnectLoading, setWalletConnectLoading ] = useState(false)

  async function handleConnectWallet(wallet: string)  {
    wallet === 'WalletConnect' ? setWalletConnectLoading(true) : setLoading(true)
    try {
      const { account, web3 } = wallet === 'WalletConnect' ? 
        await handleWalletConnect(dispatch):
        await handleInjectedProvider(dispatch)
      const balance = await web3.eth.getBalance(account)
      console.log('balance', balance)
      setEtherBalance(parseInt(balance)/1e18)
    } catch (error) {
      console.error(error)  
    } finally {
      wallet === 'WalletConnect' ? setWalletConnectLoading(false) : setLoading(false)
    }
  }
  // console.log('globalState', globalState)
  return globalState.account ? (
    <Box
      mt={3}
      display="flex"
      alignItems="center"
      background="gray.700"
      borderRadius="xl"
      py="0"
    >
      <Box px="3">
        <Text color="white" fontSize="md">
          {etherBalance.toFixed(3)} ETH
        </Text>
      </Box>
      <Button
        onClick={handleOpenModal}
        bg="gray.800"
        border="1px solid transparent"
        _hover={{
          border: "1px",
          borderStyle: "solid",
          borderColor: "blue.400",
          backgroundColor: "gray.700",
        }}
        borderRadius="xl"
        m="1px"
        px={3}
        height="38px"
      >
        <Text color="white" fontSize="md" fontWeight="medium" mr="2">
          {globalState.account &&
            `${globalState.account.slice(0, 6)}...${globalState.account.slice(
              globalState.account.length - 4,
              globalState.account.length
            )}`}
        </Text>
        <Identicon />
      </Button>
    </Box>
  ) : (
    <SimpleGrid mt={3} columns={2} spacing={3}>
      <Button
        isLoading={loading}
        spinner={<BeatLoader size={8} color="white" />}
        onClick={() => handleConnectWallet('MetaMask')}
        bg="blue.800"
        color="blue.300"
        fontSize="lg"
        fontWeight="medium"
        borderRadius="xl"
        border="1px solid transparent"
        _hover={{
          borderColor: "blue.700",
          color: "blue.400",
        }}
        _active={{
          backgroundColor: "blue.800",
          borderColor: "blue.700",
        }}
      >
        Connect with MetaMask
      </Button>
      <Button
        isLoading={walletConnectLoading}
        spinner={<BeatLoader size={8} color="white" />}
        onClick={() => handleConnectWallet('WalletConnect')}
        bg="gray.800"
        color="gray.300"
        fontSize="lg"
        fontWeight="medium"
        borderRadius="xl"
        border="1px solid transparent"
        _hover={{
          borderColor: "gray.700",
          color: "gray.400",
        }}
        _active={{
          backgroundColor: "gray.800",
          borderColor: "gray.700",
        }}
      >
        Connect with WalletConnect
      </Button>
    </SimpleGrid>
  );
}
Example #20
Source File: index.tsx    From ledokku with MIT License 4 votes vote down vote up
function Home() {
  const context = useDocusaurusContext();
  const { siteConfig = {} } = context;

  return (
    <ChakraProvider>
      <Layout>
        <Container py={20} mb={20} alignContent={'center'} maxW={'4xl'}>
          <SimpleGrid
            minChildWidth={300}
            columns={[4, 2, 2, 2]}
            gap={{ md: 6, xs: 2, sm: 2 }}
            pt={4}
            as="main"
          >
            <Box>
              <Heading
                color="gray.700"
                fontWeight={'extrabold'}
                fontFamily={'sans-serif'}
                fontSize={'6xl'}
                lineHeight={'110%'}
              >
                Ledokku
              </Heading>
              <Heading
                color="grey.600"
                fontWeight={'bold'}
                fontFamily={'sans-serif'}
                fontSize={{ base: 'xl', md: 'xl', sm: 'xl', xs: 'l' }}
                lineHeight={'110%'}
                mb={6}
                px={1}
              >
                Take control of your app deployments
              </Heading>
              <Divider orientation="horizontal" />
              <SimpleGrid mb={10} gap={{ xl: 4, l: 4, sm: 4, xs: 1 }}>
                <Heading color="gray.600">Deploy from git</Heading>
                <Heading color="gray.500">Link with databases</Heading>
                <Heading color="gray.400">Open source</Heading>
                <Heading color="gray.300">Save money</Heading>
                <Heading color="gray.200">Based on Dokku</Heading>

                <SimpleGrid mt={6} columns={16}>
                  <Image h={6} w={6} src="img/js.png" />
                  <Image h={6} w={6} src="img/ruby.png" />
                  <Image h={6} w={6} src="img/golang.png" />
                  <Image h={6} w={6} src="img/python.png" />
                  <Image h={6} w={6} src="img/php.png" />
                  <Image h={6} w={6} src="img/java.png" />
                  <Image
                    h={6}
                    w={6}
                    src="https://cdn.svgporn.com/logos/scala.svg"
                  />
                  <Image
                    h={6}
                    w={6}
                    src="https://cdn.svgporn.com/logos/clojure.svg"
                  />
                </SimpleGrid>
              </SimpleGrid>
              <SimpleGrid mt={12}>
                <Link href="/docs/getting-started">
                  <Button
                    colorScheme={'white'}
                    w={'50%'}
                    bg={'gray.900'}
                    _hover={{
                      bg: 'gray.500',
                    }}
                  >
                    Get started
                  </Button>
                </Link>
              </SimpleGrid>
            </Box>
            <Box mt={6}>
              <Box w={{ md: 450, sm: 300, xs: 300 }} boxShadow="lg">
                <Image src="img/dashboardLanding.png" />
              </Box>
              <Box
                mt={-16}
                ml={6}
                mr={6}
                w={{ md: 400, sm: 250, xs: 250 }}
                boxShadow="lg"
              >
                <Image src="img/terminal.png" />
              </Box>
            </Box>
          </SimpleGrid>
        </Container>
      </Layout>
    </ChakraProvider>
  );
}
Example #21
Source File: create-database.tsx    From ledokku with MIT License 4 votes vote down vote up
CreateDatabase = () => {
  const location = useLocation();
  const history = useHistory();
  const toast = useToast();

  const { data: dataDb } = useDatabaseQuery();
  const [arrayOfCreateDbLogs, setArrayofCreateDbLogs] = useState<RealTimeLog[]>(
    []
  );
  const [isTerminalVisible, setIsTerminalVisible] = useState(false);
  const [createDatabaseMutation] = useCreateDatabaseMutation();
  const [
    isDbCreationSuccess,
    setIsDbCreationSuccess,
  ] = useState<DbCreationStatus>();

  useCreateDatabaseLogsSubscription({
    onSubscriptionData: (data) => {
      const logsExist = data.subscriptionData.data?.createDatabaseLogs;

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

  const createDatabaseSchema = yup.object({
    type: yup
      .string()
      .oneOf(['POSTGRESQL', 'MYSQL', 'MONGODB', 'REDIS'])
      .required(),
    name: yup.string().when('type', (type: DatabaseTypes) => {
      return yup
        .string()
        .required('Database name is required')
        .matches(/^[a-z0-9-]+$/)
        .test(
          'Name already exists',
          `You already have created ${type} database with this name`,
          (name) =>
            !dataDb?.databases.find(
              (db) => db.name === name && type === db.type
            )
        );
    }),
  });

  const [
    isDokkuPluginInstalled,
    { data, loading, error: isDokkuPluginInstalledError },
  ] = useIsPluginInstalledLazyQuery({
    // we poll every 5 sec
    pollInterval: 5000,
  });
  const formik = useFormik<{ name: string; type: DatabaseTypes }>({
    initialValues: {
      name: location.state ? (location.state as string) : '',
      type: 'POSTGRESQL',
    },
    validateOnChange: true,
    validationSchema: createDatabaseSchema,
    onSubmit: async (values) => {
      try {
        await createDatabaseMutation({
          variables: {
            input: { name: values.name, type: values.type },
          },
        });
        setIsTerminalVisible(true);

        trackGoal(trackingGoals.createDatabase, 0);
      } catch (error) {
        toast.error(error.message);
      }
    },
  });

  const isPluginInstalled = data?.isPluginInstalled.isPluginInstalled;

  const handleNext = () => {
    setIsTerminalVisible(false);
    const dbId = arrayOfCreateDbLogs[arrayOfCreateDbLogs.length - 1].message;
    history.push(`database/${dbId}`);
  };

  // Effect for checking whether plugin is installed
  useEffect(() => {
    isDokkuPluginInstalled({
      variables: {
        pluginName: dbTypeToDokkuPlugin(formik.values.type),
      },
    });
  }, [formik.values.type, isPluginInstalled, isDokkuPluginInstalled]);

  // Effect for db creation
  useEffect(() => {
    isDbCreationSuccess === DbCreationStatus.FAILURE
      ? toast.error('Failed to create database')
      : isDbCreationSuccess === DbCreationStatus.SUCCESS &&
        toast.success('Database created successfully');
  }, [isDbCreationSuccess, toast]);

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

      <Container maxW="5xl" my="4">
        <Heading as="h2" size="md">
          Create a new database
        </Heading>
        <Box mt="12">
          {isTerminalVisible ? (
            <>
              <Text mb="2">
                Creating <b>{formik.values.type}</b> database{' '}
                <b>{formik.values.name}</b>
              </Text>
              <Text mb="2" color="gray.500">
                Creating database usually takes a couple of minutes. Breathe in,
                breathe out, logs are about to appear below:
              </Text>
              <Terminal>
                {arrayOfCreateDbLogs.map((log) => (
                  <Text key={arrayOfCreateDbLogs.indexOf(log)} size="small">
                    {log.message}
                  </Text>
                ))}
              </Terminal>

              {!!isDbCreationSuccess &&
              isDbCreationSuccess === DbCreationStatus.SUCCESS ? (
                <Box mt="12" display="flex" justifyContent="flex-end">
                  <Button
                    onClick={() => handleNext()}
                    rightIcon={<FiArrowRight size={20} />}
                  >
                    Next
                  </Button>
                </Box>
              ) : !!isDbCreationSuccess &&
                isDbCreationSuccess === DbCreationStatus.FAILURE ? (
                <Box mt="12" display="flex" justifyContent="flex-end">
                  <Button
                    onClick={() => {
                      setIsTerminalVisible(false);
                      formik.resetForm();
                    }}
                    rightIcon={<FiArrowLeft size={20} />}
                  >
                    Back
                  </Button>
                </Box>
              ) : null}
            </>
          ) : (
            <Box mt="8">
              <form onSubmit={formik.handleSubmit}>
                <Box mt="12">
                  {loading && (
                    <Center>
                      <Spinner />
                    </Center>
                  )}
                  {isDokkuPluginInstalledError ? (
                    <Alert
                      status="error"
                      variant="top-accent"
                      flexDirection="column"
                      alignItems="flex-start"
                      borderBottomRadius="base"
                      boxShadow="md"
                    >
                      <AlertTitle mr={2}>Request failed</AlertTitle>
                      <AlertDescription>
                        {isDokkuPluginInstalledError.message}
                      </AlertDescription>
                    </Alert>
                  ) : null}
                  {data?.isPluginInstalled.isPluginInstalled === false &&
                    !loading && (
                      <>
                        <Text mt="3">
                          Before creating a{' '}
                          <b>{formik.values.type.toLowerCase()}</b> database,
                          you will need to run this command on your dokku
                          server.
                        </Text>
                        <Terminal>{`sudo dokku plugin:install https://github.com/dokku/dokku-${dbTypeToDokkuPlugin(
                          formik.values.type
                        )}.git ${dbTypeToDokkuPlugin(
                          formik.values.type
                        )}`}</Terminal>
                        <Text mt="3">
                          Couple of seconds later you will be able to proceed
                          further.
                        </Text>
                      </>
                    )}
                  {data?.isPluginInstalled.isPluginInstalled === true &&
                    !loading && (
                      <SimpleGrid columns={{ sm: 1, md: 3 }}>
                        <FormControl
                          id="name"
                          isInvalid={Boolean(
                            formik.errors.name && formik.touched.name
                          )}
                        >
                          <FormLabel>Database name</FormLabel>
                          <Input
                            autoComplete="off"
                            id="name"
                            name="name"
                            value={formik.values.name}
                            onChange={formik.handleChange}
                            onBlur={formik.handleBlur}
                          />
                          <FormErrorMessage>
                            {formik.errors.name}
                          </FormErrorMessage>
                        </FormControl>
                      </SimpleGrid>
                    )}
                </Box>

                <Box mt="12">
                  <Text mb="2">Choose your database</Text>
                  <Grid
                    templateColumns={{
                      base: 'repeat(2, minmax(0, 1fr))',
                      md: 'repeat(4, minmax(0, 1fr))',
                    }}
                    gap="4"
                  >
                    <DatabaseBox
                      selected={formik.values.type === 'POSTGRESQL'}
                      label="PostgreSQL"
                      icon={<PostgreSQLIcon size={40} />}
                      onClick={() => formik.setFieldValue('type', 'POSTGRESQL')}
                    />
                    <DatabaseBox
                      selected={formik.values.type === 'MYSQL'}
                      label="MySQL"
                      icon={<MySQLIcon size={40} />}
                      onClick={() => formik.setFieldValue('type', 'MYSQL')}
                    />
                    <DatabaseBox
                      selected={formik.values.type === 'MONGODB'}
                      label="Mongo"
                      icon={<MongoIcon size={40} />}
                      onClick={() => formik.setFieldValue('type', 'MONGODB')}
                    />
                    <DatabaseBox
                      selected={formik.values.type === 'REDIS'}
                      label="Redis"
                      icon={<RedisIcon size={40} />}
                      onClick={() => formik.setFieldValue('type', 'REDIS')}
                    />
                  </Grid>
                </Box>

                <Box mt="12" display="flex" justifyContent="flex-end">
                  <Button
                    isLoading={formik.isSubmitting}
                    disabled={
                      data?.isPluginInstalled.isPluginInstalled === false ||
                      !formik.values.name ||
                      !!formik.errors.name ||
                      !dataDb?.databases
                    }
                    rightIcon={<FiArrowRight size={20} />}
                    type="submit"
                  >
                    Create
                  </Button>
                </Box>
              </form>
            </Box>
          )}
        </Box>
      </Container>
    </>
  );
}
Example #22
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 #23
Source File: FcmLayout.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
FcmLayout = (): JSX.Element => {
    const dispatch = useAppDispatch();
    const alertRef = useRef(null);
    const confirmationActions: NodeJS.Dict<any> = {
        clearConfiguration: {
            message: (
                'Are you sure you want to clear your FCM Configuration?<br /><br />' +
                'Doing so will prevent notifications from being delivered until ' +
                'your configuration is re-loaded'
            ),
            func: async () => {
                const success = await clearFcmConfiguration();
                if (success) {
                    dispatch(setConfig({ name: 'fcm_client', 'value': null }));
                    dispatch(setConfig({ name: 'fcm_server', 'value': null }));
                }
            }
        }
    };

    const serverLoaded = (useAppSelector(state => state.config.fcm_server !== null) ?? false);
    const clientLoaded = (useAppSelector(state => state.config.fcm_client !== null) ?? false);
    const [isDragging, setDragging] = useBoolean();
    const [errors, setErrors] = useState([] as Array<ErrorItem>);
    const [requiresConfirmation, setRequiresConfirmation] = useState(null as string | null);
    const alertOpen = errors.length > 0;
    

    const onDrop = async (e: React.DragEvent<HTMLDivElement>) => {
        e.preventDefault();

        dragCounter = 0;
        setDragging.off();
        
        // I'm not sure why, but we need to copy the file data _before_ we read it using the file reader.
        // If we do not, the data transfer file list gets set to empty after reading the first file.
        const listCopy: Array<Blob> = [];
        for (let i = 0; i < e.dataTransfer.files.length; i++) {
            listCopy.push(e.dataTransfer.files.item(i) as Blob);
        }

        // Actually read the files
        const errors: Array<ErrorItem> = [];
        for (let i = 0; i < listCopy.length; i++) {
            try {
                const fileStr = await readFile(listCopy[i]);
                const validClient = isValidClientConfig(fileStr);
                const validServer = isValidServerConfig(fileStr);
                const jsonData = JSON.parse(fileStr);

                if (validClient) {
                    const test = isValidFirebaseUrl(jsonData);
                    if (test) {
                        await saveFcmClient(jsonData);
                        dispatch(setConfig({ name: 'fcm_client', 'value': jsonData }));
                    } else {
                        throw new Error(
                            'Your Firebase setup does not have a real-time database enabled. ' +
                            'Please enable the real-time database in your Firebase Console.'
                        );
                    }
                } else if (validServer) {
                    await saveFcmServer(jsonData);
                    dispatch(setConfig({ name: 'fcm_server', 'value': jsonData }));
                } else {
                    throw new Error('Invalid Google FCM File!');
                }
            } catch (ex: any) {
                errors.push({ id: String(i), message: ex?.message ?? String(ex) });
            }
        }

        if (errors.length > 0) {
            setErrors(errors);
        }
    };

    const onDragEnter = (e: React.DragEvent<HTMLDivElement>) => {
        e.preventDefault();
        if (dragCounter === 0) {
            setDragging.on();
        }

        dragCounter += 1;
    };

    const onDragOver = (e: React.DragEvent<HTMLDivElement>) => {
        e.stopPropagation();
        e.preventDefault();
    };

    const onDragLeave = () => {
        dragCounter -= 1;
        if (dragCounter === 0) {
            setDragging.off();
        }
    };

    const closeAlert = () => {
        setErrors([]);
    };

    const confirm = (confirmationType: string | null) => {
        setRequiresConfirmation(confirmationType);
    };

    return (
        <Box
            p={3}
            borderRadius={10}
            onDragEnter={(e) => onDragEnter(e)}
            onDragLeave={() => onDragLeave()}
            onDragOver={(e) => onDragOver(e)}
            onDrop={(e) => onDrop(e)}
        >
            <Stack direction='column' p={5}>
                <Text fontSize='2xl'>Controls</Text>
                <Divider orientation='horizontal' />
                <Flex flexDirection="row" justifyContent="flex-start">
                    <Menu>
                        <MenuButton
                            as={Button}
                            rightIcon={<BsChevronDown />}
                            width="12em"
                            mr={5}
                        >
                            Manage
                        </MenuButton>
                        <MenuList>
                            <MenuItem icon={<FiTrash />} onClick={() => confirm('clearConfiguration')}>
                                Clear Configuration
                            </MenuItem>
                        </MenuList>
                    </Menu>
                </Flex>
            </Stack>
            <Stack direction='column' p={5}>
                <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                    <Text fontSize='2xl'>Configuration</Text>
                    <Popover trigger='hover'>
                        <PopoverTrigger>
                            <Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
                                <AiOutlineInfoCircle />
                            </Box>
                        </PopoverTrigger>
                        <PopoverContent>
                            <PopoverArrow />
                            <PopoverCloseButton />
                            <PopoverHeader>Information</PopoverHeader>
                            <PopoverBody>
                                <Text>
                                    Drag and drop your JSON configuration files from your Google Firebase Console. If you
                                    do not have these configuration files. Please go to
                                    <span style={{ color: baseTheme.colors.brand.primary }}>
                                        <Link href='https://bluebubbles.app/install' color='brand.primary' target='_blank'> Our Website </Link>
                                    </span>
                                    to learn how.
                                </Text>
                                <Text>
                                    These configurations enable the BlueBubbles server to send notifications and other
                                    messages to all of the clients via Google FCM. Google Play Services is required
                                    for Android Devices.
                                </Text>
                            </PopoverBody>
                        </PopoverContent>
                    </Popover>
                </Flex>
                <Divider orientation='horizontal' />
                <Spacer />

                <SimpleGrid columns={2} spacing={5}>
                    <DropZone
                        text="Drag n' Drop Google Services JSON"
                        loadedText="Google Services JSON Successfully Loaded!"
                        isDragging={isDragging}
                        isLoaded={clientLoaded}
                    />
                    <DropZone
                        text="Drag n' Drop Admin SDK JSON"
                        loadedText="Admin SDK JSON Successfully Loaded!"
                        isDragging={isDragging}
                        isLoaded={serverLoaded}
                    />
                </SimpleGrid>
            </Stack>

            <ErrorDialog
                errors={errors}
                modalRef={alertRef}
                onClose={() => closeAlert()}
                isOpen={alertOpen}
            />

            <ConfirmationDialog
                modalRef={alertRef}
                onClose={() => confirm(null)}
                body={confirmationActions[requiresConfirmation as string]?.message}
                onAccept={() => {
                    if (hasKey(confirmationActions, requiresConfirmation as string)) {
                        confirmationActions[requiresConfirmation as string].func();
                    }
                }}
                isOpen={requiresConfirmation !== null}
            />
        </Box>
    );
}
Example #24
Source File: tech-stack.tsx    From portfolio with MIT License 4 votes vote down vote up
TechStack = () => {
  const [skillsList, setSkillsList] = useState([]);

  React.useEffect(() => {
    setSkillsList(skills);
  }, []);

  const filterSkills = tab => {
    console.log(skills.filter(skill => skill.type === tab));
    if (tab.length) setSkillsList(skills.filter(skill => skill.type === tab));
    else setSkillsList(skills);
  };

  return (
    <PageSlideFade>
      <VStack spacing={8}>
        <Section>
          <VStack>
            <Header mt={0} mb={1}>
              Tech Stack
            </Header>
            <Text
              fontSize={"xl"}
              color={useColorModeValue("gray.500", "gray.200")}
              maxW="lg"
              textAlign="center"
            >
              A list of my favorite tools and technologies that I use on a
              regular basis.
            </Text>
          </VStack>
        </Section>
        <Section>
          <Tabs
            variant="soft-rounded"
            colorScheme="blue"
            align="center"
            w="100%"
          >
            <TabList display="flex" flexWrap="wrap">
              <Tab
                bg={useColorModeValue("gray.100", "gray.800")}
                color={useColorModeValue("gray.600", "gray.500")}
                _selected={{
                  color: "green.800",
                  bg: "green.100"
                }}
                mr={2}
                mt={2}
                onClick={() => filterSkills("")}
              >
                <HStack spacing={1}>
                  <Icon as={AiTwotoneThunderbolt} weight="fill" />
                  <Text>All</Text>
                </HStack>
              </Tab>
              <Tab
                bg={useColorModeValue("gray.100", "gray.800")}
                color={useColorModeValue("gray.500", "gray.500")}
                _selected={{
                  color: useColorModeValue("gray.100", "gray.800"),
                  bg: useColorModeValue("gray.900", "gray.100")
                }}
                mr={2}
                mt={2}
                onClick={() => filterSkills("development")}
              >
                <HStack spacing={1}>
                  <Icon as={BiDesktop} weight="fill" />
                  <Text>Web Development</Text>
                </HStack>
              </Tab>
              <Tab
                bg={useColorModeValue("gray.100", "gray.800")}
                color={useColorModeValue("gray.600", "gray.500")}
                _selected={{
                  color: "green.800",
                  bg: "green.100"
                }}
                mr={2}
                mt={2}
                onClick={() => filterSkills("design")}
              >
                <HStack spacing={1}>
                  <Icon as={GiSpiderWeb} weight="fill" />
                  <Text>Web Design</Text>
                </HStack>
              </Tab>
              <Tab
                bg={useColorModeValue("gray.100", "gray.800")}
                color={useColorModeValue("gray.600", "gray.500")}
                _selected={{
                  color: "red.800",
                  bg: "red.100"
                }}
                mr={2}
                mt={2}
                onClick={() => filterSkills("devops")}
              >
                <HStack spacing={1}>
                  <Icon as={AiOutlineCloudServer} weight="fill" />
                  <Text>Devops</Text>
                </HStack>
              </Tab>
            </TabList>
            <TabPanels minHeight={"45vh"}>
              <TabPanel px={0}>
                <MotionBox
                  variants={container}
                  initial="hidden"
                  animate="visible"
                >
                  <SimpleGrid columns={[1, 1, 2]} spacing={4} mt={8}>
                    {skillsList.map((tool, index) => (
                      <SkillCard
                        key={index}
                        name={tool.name}
                        description={tool.description}
                        image={tool.image}
                        platform={"web"}
                        link={tool.link}
                      />
                    ))}
                  </SimpleGrid>
                </MotionBox>
              </TabPanel>
              <TabPanel px={0}>
                <MotionBox
                  variants={container}
                  initial="hidden"
                  animate="visible"
                >
                  <SimpleGrid columns={[1, 2]} spacing={4} mt={8}>
                    {skillsList.map((tool, index) => (
                      <SkillCard
                        key={index}
                        name={tool.name}
                        description={tool.description}
                        image={tool.image}
                        platform={"web"}
                        link={tool.link}
                      />
                    ))}
                  </SimpleGrid>
                </MotionBox>
              </TabPanel>
              <TabPanel px={0}>
                <MotionBox
                  variants={container}
                  initial="hidden"
                  animate="visible"
                >
                  <SimpleGrid columns={[1, 2]} spacing={4} mt={8}>
                    {skillsList.map((tool, index) => (
                      <SkillCard
                        key={index}
                        name={tool.name}
                        description={tool.description}
                        image={tool.image}
                        platform={"web"}
                        link={tool.link}
                      />
                    ))}
                  </SimpleGrid>
                </MotionBox>
              </TabPanel>
              <TabPanel px={0}>
                <MotionBox
                  variants={container}
                  initial="hidden"
                  animate="visible"
                >
                  <SimpleGrid columns={[1, 2]} spacing={4} mt={8}>
                    {skillsList.map((tool, index) => (
                      <SkillCard
                        key={index}
                        name={tool.name}
                        description={tool.description}
                        image={tool.image}
                        platform={"web"}
                        link={tool.link}
                      />
                    ))}
                  </SimpleGrid>
                </MotionBox>
              </TabPanel>
            </TabPanels>
          </Tabs>
        </Section>
      </VStack>
    </PageSlideFade>
  );
}
Example #25
Source File: page-footer.tsx    From notebook with MIT License 4 votes vote down vote up
export function PageFooter() {
  return (
    <SimpleGrid
      flexDirection="column-reverse"
      gridTemplateColumns={["1fr", "1fr", "1fr 1fr", "1fr 1fr"]}
      borderTopWidth={2}
      mt="30px"
      borderTopColor="gray.900"
      pt="20px"
    >
      <Box d={["block", "block", "none", "none"]} mb="30px">
        <FooterSignup />
      </Box>
      <Box>
        <SimpleGrid columns={[1, 1, 2, 2]}>
          <Stack mb={["10px", "10px", 0, 0]}>
            <Text as="span">
              <ExternalFooterLink
                href={`mailto:${siteConfig.author.email}`}
                text="Contact us"
              />
            </Text>
            <Text as="span">
              <ExternalFooterLink
                href={siteConfig.repo.url}
                text="Contribute"
              />
            </Text>
            <Text as="span">
              <InternalFooterLink
                href="/projects"
                text="Open source projects"
              />
            </Text>
          </Stack>
          <Stack>
            <Text as="span">
              <Popover placement="top">
                <PopoverTrigger>
                  <Text
                    as="span"
                    _focus={{ outline: "none", boxShadow: "none" }}
                    fontWeight={500}
                    color="gray.500"
                    cursor="pointer"
                    _hover={{ color: "gray.600", textDecoration: "none" }}
                  >
                    Social Accounts
                  </Text>
                </PopoverTrigger>
                <Portal>
                  <PopoverContent>
                    <PopoverArrow />
                    <PopoverCloseButton />
                    <PopoverBody>
                      <Stack
                        as="footer"
                        isInline
                        spacing={[1, 2]}
                        justifyContent="center"
                        alignItems="center"
                      >
                        <ExternalSocialLink
                          href={siteConfig.author.github}
                          icon={<FaGithub />}
                          type="gray"
                          label="Github Account"
                        />
                        <ExternalSocialLink
                          href={siteConfig.author.dev}
                          icon={<FaDev />}
                          type="gray"
                          label="Dev Account"
                        />
                        <ExternalSocialLink
                          href={siteConfig.author.linkedin}
                          icon={<FaLinkedin />}
                          type="linkedin"
                          label="LinkedIn Account"
                        />
                        <ExternalSocialLink
                          href={siteConfig.author.twitter}
                          icon={<FaTwitter />}
                          type="twitter"
                          label="Twitter Account"
                        />
                        <ExternalSocialLink
                          href={siteConfig.author.quora}
                          icon={<FaQuora />}
                          type="red"
                          label="Quora Account"
                        />
                      </Stack>
                    </PopoverBody>
                  </PopoverContent>
                </Portal>
              </Popover>
            </Text>

            <Text as="span">
              <ExternalFooterLink
                href={`mailto:${siteConfig.author.email}`}
                text="Sponsor"
              />
            </Text>
            <Text as="span">
              <ExternalFooterLink
                href={"/#faqs"}
                isExternal={false}
                text="FAQs"
              />
            </Text>
          </Stack>
        </SimpleGrid>
        <Text mt="20px" color="gray.500">
          Made with ? by{" "}
          <ChakraLink
            _focus={{ boxShadow: "none", outline: "none" }}
            target="_blank"
            href={siteConfig.author.github}
            fontWeight={600}
            color={"gray.400"}
            bgClip="text"
            bgGradient="linear(to-l, #7928CA,#FF0080)"
            _hover={{
              bgGradient: "linear(to-r, red.500, yellow.500)"
            }}
          >
            Muhammad Ahmad
          </ChakraLink>{" "}
        </Text>
      </Box>
      <Box d={["none", "none", "block", "block"]}>
        <FooterSignup />
      </Box>
    </SimpleGrid>
  );
}
Example #26
Source File: NotificationsWalkthrough.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
NotificationsWalkthrough = (): JSX.Element => {
    const dispatch = useAppDispatch();
    const alertRef = useRef(null);

    const serverLoaded = (useAppSelector(state => state.config.fcm_server !== null) ?? false);
    const clientLoaded = (useAppSelector(state => state.config.fcm_client !== null) ?? false);
    const [isDragging, setDragging] = useBoolean();
    const [errors, setErrors] = useState([] as Array<ErrorItem>);
    const alertOpen = errors.length > 0;

    const onDrop = async (e: React.DragEvent<HTMLDivElement>) => {
        e.preventDefault();

        dragCounter = 0;
        setDragging.off();
        
        // I'm not sure why, but we need to copy the file data _before_ we read it using the file reader.
        // If we do not, the data transfer file list gets set to empty after reading the first file.
        const listCopy: Array<Blob> = [];
        for (let i = 0; i < e.dataTransfer.files.length; i++) {
            listCopy.push(e.dataTransfer.files.item(i) as Blob);
        }

        // Actually read the files
        const errors: Array<ErrorItem> = [];
        for (let i = 0; i < listCopy.length; i++) {
            try {
                const fileStr = await readFile(listCopy[i]);
                const validClient = isValidClientConfig(fileStr);
                const validServer = isValidServerConfig(fileStr);
                const jsonData = JSON.parse(fileStr);

                if (validClient) {
                    const test = isValidFirebaseUrl(jsonData);
                    if (test) {
                        await saveFcmClient(jsonData);
                        dispatch(setConfig({ name: 'fcm_client', 'value': jsonData }));
                    } else {
                        throw new Error(
                            'Your Firebase setup does not have a real-time database enabled. ' +
                            'Please enable the real-time database in your Firebase Console.'
                        );
                    }
                } else if (validServer) {
                    await saveFcmServer(jsonData);
                    dispatch(setConfig({ name: 'fcm_server', 'value': jsonData }));
                } else {
                    throw new Error('Invalid Google FCM File!');
                }
            } catch (ex: any) {
                errors.push({ id: String(i), message: ex?.message ?? String(ex) });
            }
        }

        if (errors.length > 0) {
            setErrors(errors);
        }
    };

    const onDragEnter = (e: React.DragEvent<HTMLDivElement>) => {
        e.preventDefault();
        if (dragCounter === 0) {
            setDragging.on();
        }

        dragCounter += 1;
    };

    const onDragOver = (e: React.DragEvent<HTMLDivElement>) => {
        e.stopPropagation();
        e.preventDefault();
    };

    const onDragLeave = () => {
        dragCounter -= 1;
        if (dragCounter === 0) {
            setDragging.off();
        }
    };

    const closeAlert = () => {
        setErrors([]);
    };

    return (
        <SlideFade in={true} offsetY='150px'>
            <Box
                px={5}
                onDragEnter={(e) => onDragEnter(e)}
                onDragLeave={() => onDragLeave()}
                onDragOver={(e) => onDragOver(e)}
                onDrop={(e) => onDrop(e)}    
            >
                <Text fontSize='4xl'>Notifications &amp; Firebase</Text>
                <Text fontSize='md' mt={5}>
                    BlueBubbles utilizes Google FCM (Firebase Cloud Messaging) to deliver notifications to your devices.
                    We do this so the client do not need to hold a connection to the server at all times. As a result,
                    BlueBubbles can deliver notifications even when the app is running in the background. It also means
                    BlueBubbles will use less battery when running in the background.
                </Text>
                <Alert status='info' mt={5}>
                    <AlertIcon />
                    If you do not complete this setup, you will not receive notifications!
                </Alert>
                <Text fontSize='md' mt={5}>
                    The setup with Google FCM is a bit tedious, but it is a "set it and forget it" feature. Follow the
                    instructions here: <Link
                        as='span'
                        href='https://bluebubbles.app/install/'
                        color='brand.primary'
                        target='_blank'>https://bluebubbles.app/install</Link>
                </Text>
                <Text fontSize='3xl' mt={5}>Firebase Configurations</Text>
                
                <SimpleGrid columns={2} spacing={5} mt={5}>
                    <DropZone
                        text="Drag n' Drop google-services.json"
                        loadedText="google-services.json Successfully Loaded!"
                        isDragging={isDragging}
                        isLoaded={serverLoaded}
                    />
                    <DropZone
                        text="Drag n' Drop *-firebase-adminsdk-*.json"
                        loadedText="*-firebase-adminsdk-*.json Successfully Loaded!"
                        isDragging={isDragging}
                        isLoaded={clientLoaded}
                    />
                </SimpleGrid>
            </Box>

            <ErrorDialog
                errors={errors}
                modalRef={alertRef}
                onClose={() => closeAlert()}
                isOpen={alertOpen}
            />
        </SlideFade>
    );
}
Example #27
Source File: HomeLayout.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
HomeLayout = (): JSX.Element => {
    const address = useAppSelector(state => state.config.server_address);
    const fcmClient = useAppSelector(state => state.config.fcm_client);
    const password = useAppSelector(state => state.config.password);
    const port = useAppSelector(state => state.config.socket_port);
    const qrCode = fcmClient ? buildQrData(password, address, fcmClient) : null;

    return (
        <Box p={3} borderRadius={10}>
            <Flex flexDirection="column">
                <Stack direction='column' p={5}>
                    <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                        <Text fontSize='2xl'>Connection Details</Text>
                        <Popover trigger='hover'>
                            <PopoverTrigger>
                                <Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
                                    <AiOutlineInfoCircle />
                                </Box>
                            </PopoverTrigger>
                            <PopoverContent>
                                <PopoverArrow />
                                <PopoverCloseButton />
                                <PopoverHeader>Information</PopoverHeader>
                                <PopoverBody>
                                    <Text>
                                        This page will detail your current connection details. This includes your&nbsp;
                                        server address and your local port.
                                    </Text>
                                    <br />
                                    <UnorderedList>
                                        <ListItem><strong>Server Address:</strong> This is the address that your clients will connect to</ListItem>
                                        <ListItem><strong>Local Port:</strong> This is the port that the HTTP server is running on, 
                                            and the port you will use when port forwarding&nbsp;
                                            for a dynamic DNS
                                        </ListItem>
                                    </UnorderedList>
                                </PopoverBody>
                            </PopoverContent>
                        </Popover>
                    </Flex>
                    <Divider orientation='horizontal' />
                    <Spacer />
                    <Flex flexDirection="row" justifyContent="space-between">
                        <Stack>
                            <Flex flexDirection="row" alignItems='center'>
                                <Text fontSize='md' fontWeight='bold' mr={2}>Server Address: </Text>
                                {(!address) ? (
                                    <SkeletonText noOfLines={1} />
                                ) : (
                                    <Text fontSize='md'>{address}</Text>
                                )}
                                <Tooltip label='Copy Address'>
                                    <IconButton
                                        ml={3}
                                        size='md'
                                        aria-label='Copy Address'
                                        icon={<BiCopy size='22px' />}
                                        onClick={() => copyToClipboard(address)}
                                    />
                                </Tooltip>
                                <Popover placement='bottom' isLazy={true}>
                                    <PopoverTrigger>
                                        <Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }} >
                                            <Tooltip label='Show QR Code'>
                                                <IconButton
                                                    ml={1}
                                                    size='md'
                                                    aria-label='Show QR Code'
                                                    icon={<AiOutlineQrcode size='24px' />}
                                                />
                                            </Tooltip>
                                        </Box>
                                    </PopoverTrigger>
                                    <PopoverContent>
                                        <PopoverArrow />
                                        <PopoverCloseButton />
                                        <PopoverHeader>QR Code</PopoverHeader>
                                        <PopoverBody>
                                            <Flex justifyContent='center' flexDirection='column' alignItems='center'>
                                                <Text>
                                                    Your QR Code contains your server configuration so that clients can connect.
                                                    Your QR Code should remain <strong>private</strong> as it contains sensitive information!
                                                </Text>
                                                <Box border="5px solid" borderColor='white' mt={4} height='266px' width='266px' borderRadius='lg' mb={3}>
                                                    {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
                                                    {/* @ts-ignore: ts2876 */}
                                                    {(qrCode) ? <QRCode value={qrCode as string} /> : null}
                                                </Box>
                                            </Flex>
                                        </PopoverBody>
                                    </PopoverContent>
                                </Popover>
                            </Flex>
                            <Flex flexDirection="row">
                                <Text fontSize='md' fontWeight='bold' mr={2}>Local Port: </Text>
                                {(!port) ? (
                                    <SkeletonText noOfLines={1} />
                                ) : (
                                    <Text fontSize='md'>{port}</Text>
                                )}
                            </Flex>
                        </Stack>
                        <Divider orientation="vertical" />
                    </Flex>
                </Stack>
                <Stack direction='column' p={5}>
                    <Flex flexDirection='row' justifyContent='flex-start' alignItems='center'>
                        <Text fontSize='2xl'>iMessage Highlights</Text>
                        <Popover trigger='hover'>
                            <PopoverTrigger>
                                <Box ml={2} _hover={{ color: 'brand.primary', cursor: 'pointer' }}>
                                    <AiOutlineInfoCircle />
                                </Box>
                            </PopoverTrigger>
                            <PopoverContent>
                                <PopoverArrow />
                                <PopoverCloseButton />
                                <PopoverHeader>Information</PopoverHeader>
                                <PopoverBody>
                                    <Text>
                                        These are just some fun stats that I included to give you a quick "snapshot"
                                        of your iMessage history on the Mac Device. This does not include messages that
                                        are on Apple's servers, only what is local to this device.
                                    </Text>
                                </PopoverBody>
                            </PopoverContent>
                        </Popover>
                    </Flex>
                    <Divider orientation='horizontal' />
                    <Spacer />
                    { /* Delays are so older systems do not freeze when requesting data from the databases */ }
                    <SimpleGrid columns={3} spacing={5}>
                        <TotalMessagesStatBox />
                        <TopGroupStatBox delay={200} />
                        <BestFriendStatBox delay={400} />
                        <DailyMessagesStatBox delay={600} />
                        <TotalPicturesStatBox delay={800} />
                        <TotalVideosStatBox delay={1000} />
                    </SimpleGrid>
                </Stack>
            </Flex>
        </Box>
    );
}