@chakra-ui/react#Container TypeScript Examples

The following examples show how to use @chakra-ui/react#Container. 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: HeaderFooterContainer.tsx    From coindrop with GNU General Public License v3.0 6 votes vote down vote up
HeaderFooterContainer: FC = ({ children }) => {
    const theme = useTheme();
    return (
        <Container
            maxW={theme.breakpoints.xl}
        >
            {children}
        </Container>
    );
}
Example #2
Source File: metrics.tsx    From ledokku with MIT License 6 votes vote down vote up
Metrics = () => {
  return (
    <div>
      <HeaderContainer>
        <Header />
        <HomeHeaderTabNav />
      </HeaderContainer>

      <Container maxW="5xl" mt={10}>
        <Heading as="h2" size="md" py={5}>
          Metrics
        </Heading>
        <Text fontSize="sm" color="gray.400">
          Coming soon
        </Text>
      </Container>
    </div>
  );
}
Example #3
Source File: index.tsx    From ksana.in with Apache License 2.0 6 votes vote down vote up
export function CounterUrls() {
  const { isLoading, urls } = useCounter()

  return (
    <Container maxW={'5xl'} mx="auto" as="section" mt="16">
      <Stack p={4} spacing="16">
        <Heading textAlign="center" as="h3">
          Total Tautan
        </Heading>

        {isLoading && <LoadingSkeleton />}
        {!isLoading && <Counter count={urls} />}
      </Stack>
    </Container>
  )
}
Example #4
Source File: Layout.tsx    From next-crud with MIT License 6 votes vote down vote up
Layout: React.FC<IProps> = ({ title, backRoute, children }) => {
  return (
    <Box height="100vh">
      <Header title={title} backRoute={backRoute} />
      <Container mt={4}>
        <Center>{children}</Center>
      </Container>
    </Box>
  )
}
Example #5
Source File: ContentContainer.tsx    From coindrop with GNU General Public License v3.0 6 votes vote down vote up
ContentContainer: FC<ContentContainerProps> = ({ boxProps, children }) => {
    const theme = useTheme();
    return (
    <Container
        my={24}
        maxW={theme.breakpoints.xl}
        // eslint-disable-next-line react/jsx-props-no-spreading
        {...boxProps}
    >
        {children}
    </Container>
    );
}
Example #6
Source File: activity.tsx    From ledokku with MIT License 6 votes vote down vote up
Activity = () => {
  return (
    <div>
      <HeaderContainer>
        <Header />
        <HomeHeaderTabNav />
      </HeaderContainer>

      <Container maxW="5xl" mt={10}>
        <Heading as="h2" size="md" py={5}>
          Activity
        </Heading>
        <Text fontSize="sm" color="gray.400">
          Coming soon
        </Text>
      </Container>
    </div>
  );
}
Example #7
Source File: IntroStepper.tsx    From dope-monorepo with GNU General Public License v3.0 6 votes vote down vote up
export default function IntroStepper(props: Props) {
    const [ loading, setLoading ] = useState(false);
    
    useEffect(() => {
        props.manager.events.on('game', () => setLoading(true));
    }, []);

    return (
        <ChakraProvider theme={theme}>
            <Center style={{
                height: "100vh",
                backdropFilter: "brightness(50%)",
            }}>
                {loading ? <Spinner size="xl" color="white" /> : <Container style={{
                    padding: "1rem",
                    borderStyle: "solid",
                    boxShadow: "0px 0px 15px rgba(0,0,0,1)",
                    borderColor: "black",
                    borderWidth: "0px",
                    backgroundColor: "white",
                    borderRadius: "7px",
                }}>
                {
                    props.hustlerData?.length > 0 ? 
                        <HasHustler 
                            manager={props.manager} 
                            hustlerData={props.hustlerData}
                        /> : 
                        <NoHustler 
                            manager={props.manager} 
                            hustlerData={props.hustlerData}
                        />
                }
                </Container>}
            </Center>
        </ChakraProvider>
    );
}
Example #8
Source File: TrackPage.tsx    From takeout-app with MIT License 6 votes vote down vote up
TrackViewSkeleton: React.FC = () => {
  return (
    <Container maxW={["auto", "auto", "auto", "1700px"]} px="15px" py="22px">
      <Flex alignItems="top" justifyContent="space-between" direction={["column", "column", "column", "row"]}>
        <Box w="100%">
          <AspectRatio ratio={16 / 9}>
            <Skeleton w="100%" h="100%" />
          </AspectRatio>
        </Box>

        <Box maxW={["auto", "auto", "auto", "400px"]} minH="400px" w="100%" ml="30px"></Box>
      </Flex>
      <Flex alignItems="top" justifyContent="space-between" direction={["column", "column", "column", "row"]} mt="12px">
        <Box w="100%">
          <Skeleton w="100%" h="100px" />
        </Box>
        <Box maxW={["auto", "auto", "auto", "400px"]} w="100%" ml="30px" />
      </Flex>
    </Container>
  );
}
Example #9
Source File: faq.tsx    From coindrop with GNU General Public License v3.0 5 votes vote down vote up
FAQ: FunctionComponent = () => {
    const theme = useTheme();
    const panelBgColor = useColorModeValue("gray.50", undefined);
    return (
        <Box>
            <Box my={6}>
                <Heading as="h1" textAlign="center">
                    FAQ
                </Heading>
                <Text textAlign="center">
                    Frequently Asked Questions
                </Text>
            </Box>
            <Container maxW={theme.breakpoints.lg}>
                <Accordion defaultIndex={-1} allowToggle>
                    {accordionText.map(({title, body}) => (
                        <AccordionItem key={title}>
                            <AccordionButton>
                            <Box flex="1" textAlign="left">
                                {title}
                            </Box>
                            <AccordionIcon />
                            </AccordionButton>
                            <AccordionPanel>
                                <Box
                                    p={4}
                                    bg={panelBgColor}
                                >
                                    {body}
                                </Box>
                            </AccordionPanel>
                        </AccordionItem>
                    ))}
                </Accordion>
            </Container>
            <Text textAlign="center" mt={4} fontSize="sm">
                {"Do you have a question that's not answered here? Send it to "}
                <Link href={`mailto:${coindropEmail}`} isExternal>
                    {coindropEmail}
                </Link>
            </Text>
        </Box>
    );
}
Example #10
Source File: index.tsx    From dope-monorepo with GNU General Public License v3.0 5 votes vote down vote up
export default function Debug(props: DebugData) {
    const handleKey = (e: KeyboardEvent) => {
        if (e.key === 'Escape')
        {
            props.manager.events.emit('close');
            e.stopPropagation();
        }
    }

    useEffect(() => {
        document.addEventListener('keyup', handleKey);

        return () => {
            document.removeEventListener('keyup', handleKey);
        }
    });

    return (
        <ChakraProvider theme={theme}>
            <Container style={{
                display: 'flex',
                overflow: 'auto',
                position: "absolute",
                top: "2%",
                right: "2%",
                width: "40%",
                height: "50%",
                backgroundColor: "rgba(255,255,255,0.8)",
                borderRadius: "10px",
            }}>
                <div style={{
                    position: "relative",
                    padding: "1rem",
                }}>
                    <Tabs>
                        <TabList>
                            <Tab fontSize="0.8rem">Player</Tab>
                            <Tab fontSize="0.8rem">World</Tab>
                            <Tab fontSize="0.8rem">Hustlers</Tab>
                            <Tab fontSize="0.8rem">Item Entities</Tab>
                            <Tab fontSize="0.8rem">Lights</Tab>
                        </TabList>

                        <TabPanels>
                            <TabPanel>
                                <PlayerPanel player={props.player} />
                            </TabPanel>
                            <TabPanel>
                                <WorldPanel map={props.map} />
                            </TabPanel>
                            <TabPanel>
                                <HustlersPanel hustlers={props.hustlers} />
                            </TabPanel>
                            <TabPanel>
                                <ItemEntitiesPanel itemEntities={props.itemEntities} />
                            </TabPanel>
                            <TabPanel>
                                <LightsPanel player={props.player} lights={props.lights} />
                            </TabPanel>
                        </TabPanels>
                    </Tabs>
                </div>
            </Container>
        </ChakraProvider>
    )
}
Example #11
Source File: ControlAttendeeEdit.tsx    From takeout-app with MIT License 5 votes vote down vote up
ControlAttendeeEdit: React.FC<Props> = () => {
  const match = useRouteMatch<{ id: string }>();
  const id = parseInt(match.params.id, 10);
  const { data } = ControlApi.useAttendee(id);
  const [errorAlert, setErrorAlert] = React.useState<JSX.Element | null>(null);
  const [isRequesting, setIsRequesting] = React.useState<boolean>(false);
  const { register, handleSubmit, reset } = useForm<ControlUpdateAttendeeRequestAttendee>({
    defaultValues: {
      name: React.useMemo(() => data?.attendee?.name, [data]),
      is_staff: React.useMemo(() => data?.attendee?.is_staff, [data]),
      is_speaker: React.useMemo(() => data?.attendee?.is_speaker, [data]),
      is_committer: React.useMemo(() => data?.attendee?.is_committer, [data]),
      presentation_slugs: React.useMemo(() => data?.attendee?.presentation_slugs || [], [data]),
    },
  });

  const onSubmit = handleSubmit(async (data) => {
    if (isRequesting) return;
    setIsRequesting(true);
    try {
      await ControlApi.updateAttendee(id, data);
      setErrorAlert(null);
    } catch (e) {
      setErrorAlert(
        <Box my={2}>
          <ErrorAlert error={e} />
        </Box>,
      );
    }
    setIsRequesting(false);
  });

  React.useEffect(() => {
    if (data) reset(data.attendee);
  }, [data]);

  if (!data) return <p>Loading</p>;

  // TODO: link to registration page and support email
  return (
    <>
      {errorAlert}
      <Container mt="20px">
        <form onSubmit={onSubmit}>
          <Text>
            <Link href={data.ticket.admin_url} isExternal textDecoration="underline">
              {data.ticket.reference}
            </Link>
          </Text>
          <FormControl mt={4} id="attendee__name" isRequired>
            <FormLabel>Name</FormLabel>
            <Input {...register("name")} />
          </FormControl>
          <FormControl mt={4} id="attendee__staff" isRequired>
            <FormLabel>Staff</FormLabel>
            <Checkbox {...register("is_staff")} />
          </FormControl>
          <FormControl mt={4} id="attendee__speaker" isRequired>
            <FormLabel>Speaker</FormLabel>
            <Checkbox {...register("is_speaker")} />
          </FormControl>
          <FormControl mt={4} id="attendee__committer" isRequired>
            <FormLabel>Committer</FormLabel>
            <Checkbox {...register("is_committer")} />
          </FormControl>
          <FormControl mt={4} id="attendee__presentation_slugs">
            <FormLabel>Presentation Slugs</FormLabel>
            <Input {...register("presentation_slugs.0")} />
          </FormControl>

          <Button mt={4} size="lg" type="submit" isLoading={isRequesting}>
            Save
          </Button>
        </form>
      </Container>
    </>
  );
}
Example #12
Source File: [slug].tsx    From ksana.in with Apache License 2.0 5 votes vote down vote up
export default function BlogDetail({ post }: IBlogDetail) {
  return (
    <Layout>
      <MetaHead title={`${post.title} | Ksana.in`} description={post.excerpt} />
      <Head>
        <script
          type="application/ld+json"
          dangerouslySetInnerHTML={{
            __html: JSON.stringify(makeBreadcrumbBlogSchema({ title: post.title, slug: post.slug }))
          }}
        ></script>
      </Head>

      <VStack spacing={4} textAlign="center" as="section" mt="32">
        <Breadcrumb>
          <BreadcrumbItem>
            <BreadcrumbLink href="/">Home</BreadcrumbLink>
          </BreadcrumbItem>

          <BreadcrumbItem>
            <BreadcrumbLink href="/blog">Blog</BreadcrumbLink>
          </BreadcrumbItem>

          <BreadcrumbItem isCurrentPage>
            <BreadcrumbLink href={`/blog/${post.slug}`}>{post.title}</BreadcrumbLink>
          </BreadcrumbItem>
        </Breadcrumb>

        <Container maxW={'4xl'} mx="auto" as="section" mt="8">
          <VStack spacing={4} textAlign="center" className="blog-detail">
            <Heading
              as="h1"
              fontWeight={700}
              fontSize={{ base: '3xl', sm: '4xl', md: '5xl' }}
              lineHeight={'110%'}
              color="orange.400"
            >
              {post.title}
            </Heading>
            <Button leftIcon={<HiClock />} colorScheme="gray" variant="solid" size="xs">
              {post.date}
            </Button>
          </VStack>
        </Container>
      </VStack>

      <Container maxW={'4xl'} mx="auto" as="section" mt="8">
        <div className="markdown">
          <div
            dangerouslySetInnerHTML={{
              __html: `${post.content}`
            }}
          ></div>
        </div>
      </Container>
    </Layout>
  )
}
Example #13
Source File: ControlLogin.tsx    From takeout-app with MIT License 5 votes vote down vote up
ControlLogin: React.FC<Props> = () => {
  const history = useHistory();
  const [errorAlert, setErrorAlert] = React.useState<JSX.Element | null>(null);
  const [isRequesting, setIsRequesting] = React.useState<boolean>(false);
  const { register, handleSubmit } = useForm<{
    password: string;
  }>({ defaultValues: { password: "" } });

  const onSubmit = handleSubmit(async (data) => {
    if (isRequesting) return;
    setIsRequesting(true);
    try {
      await ControlApi.createControlSession(data.password);
      setErrorAlert(null);

      history.push("/control");
    } catch (e) {
      setErrorAlert(
        <Box my={2}>
          <ErrorAlert error={e} />
        </Box>,
      );
    }
    setIsRequesting(false);
  });

  // TODO: link to registration page and support email
  return (
    <>
      {errorAlert}
      <Container mt="20px">
        <p>Beep beep, beep boop?</p>
        <form onSubmit={onSubmit}>
          <FormControl mt={4} id="login_password" isRequired>
            <FormLabel>Control Password</FormLabel>
            <Input {...register("password")} type="password" />
          </FormControl>
          <Button mt={4} size="lg" type="submit" isLoading={isRequesting}>
            Take control
          </Button>
        </form>
      </Container>
    </>
  );
}
Example #14
Source File: Header.tsx    From ledokku with MIT License 5 votes vote down vote up
Header = () => {
  const { user, logout } = useAuth();

  return (
    <nav>
      <Container maxW="5xl">
        <Box
          display="flex"
          alignItems="center"
          justifyContent="space-between"
          h={16}
        >
          <Box display="flex" alignItems="center">
            <Heading as="h3" fontSize="medium">
              <Link to="/">Ledokku</Link>
            </Heading>
          </Box>
          <div>
            <Menu placement="bottom-end">
              <MenuButton>
                {user?.avatarUrl && (
                  <Image
                    h={8}
                    w={8}
                    borderRadius="full"
                    src={user.avatarUrl}
                    alt="Avatar"
                  />
                )}
              </MenuButton>
              <MenuList fontSize="sm" color="gray.700">
                <MenuItem as={Link} to="/dashboard">
                  Dashboard
                </MenuItem>
                <MenuDivider />
                <MenuItem
                  as="a"
                  href="https://github.com/ledokku/ledokku"
                  target="_blank"
                  rel="noopener noreferrer"
                >
                  Github
                </MenuItem>
                <MenuDivider />
                <MenuItem onClick={() => logout()}>Logout</MenuItem>
              </MenuList>
            </Menu>
          </div>
        </Box>
      </Container>
    </nav>
  );
}
Example #15
Source File: Post.tsx    From coindrop with GNU General Public License v3.0 4 votes vote down vote up
Post: FunctionComponent<PostTypePostHydrate> = ({
        author,
        datePublished,
        dateModified,
        title,
        description,
        images,
        slug,
        content,
    }) => {
    const { avatar: authorAvatar, handle: authorHandle, url: authorUrl } = authors[author];
    const theme = useTheme();
    return (
        <>
            <NextSeo
                title={`${title} | Coindrop blog`}
                description={description}
            />
            <ArticleJsonLd
                url={`https://coindrop.to/blog/${slug}`}
                title={title}
                images={images}
                datePublished={datePublished}
                dateModified={dateModified}
                authorName={author}
                publisherName="Coindrop"
                publisherLogo="https://coindrop.to/piggy-256.png" // TODO: change this to a valid AMP logo size https://developers.google.com/search/docs/data-types/article
                description={description}
            />
            <article>
                <Heading
                    as="h1"
                    size="2xl"
                    textAlign="center"
                    my={6}
                >
                    {title}
                </Heading>
                <Flex
                    direction={["column", null, "row"]}
                    align={["initial", null, "center"]}
                    alignItems="center"
                    justify="center"
                    mb={4}
                >
                    <Text
                        id="publish-date"
                        mb={[4, null, "auto"]}
                        mt={[0, null, "auto"]}
                        textAlign="center"
                        mr={[null, null, 12]}
                    >
                        {`${dayjs(datePublished).format('dddd, MMMM D, YYYY')} (${dayjs(datePublished).fromNow()})`}
                    </Text>
                    <Flex
                        id="author"
                        align="center"
                        justify="center"
                    >
                        <Avatar name={author} src={authorAvatar} size="sm" />
                            <Flex direction="column" ml={1} align="flex-start">
                                <Text fontSize="sm">
                                    <Text>
                                        {author}
                                    </Text>
                                    <Link href={authorUrl} isExternal>
                                        {authorHandle}
                                    </Link>
                                </Text>
                            </Flex>
                    </Flex>
                </Flex>
                <hr />
                <Container
                    mt={8}
                    maxW={theme.breakpoints.md}
                >
                    {content}
                    {dateModified && (
                        <>
                        <Text
                            id="modified-date"
                            fontSize="sm"
                            textAlign="center"
                        >
                            {`Last updated ${dayjs(dateModified).format('dddd, MMMM D, YYYY')} (${dayjs(dateModified).fromNow()})`}
                        </Text>
                        <Text
                            fontSize="xs"
                            textAlign="center"
                        >
                            <Link
                                href={`https://github.com/remjx/coindrop/commits/master/blog/posts/${slug}/index.mdx`}
                            >
                                <u>View edits</u>
                            </Link>
                        </Text>
                        </>
                    )}
                </Container>
            </article>
        </>
    );
}
Example #16
Source File: TrackView.tsx    From takeout-app with MIT License 4 votes vote down vote up
TrackView: React.FC<Props> = ({ track, streamOptionsState }) => {
  const [streamOptions, setStreamOptions] = streamOptionsState;
  const trackOptionsSelector = (instance: string) => (
    <TrackStreamOptionsSelector track={track} streamOptionsState={streamOptionsState} instance={instance} />
  );

  // Preload candidate speaker images
  React.useEffect(() => {
    if (!track.card_candidate) return;
    if (!track.card_candidate.speakers) return;

    track.card_candidate.speakers.forEach((s) => {
      const i = new Image();
      i.onload = () => console.log("Preloaded", s.avatar_url);
      i.src = s.avatar_url;
    });
  }, [track.card_candidate]);

  // TODO: Chakra 側のブレークポイントの調整
  // TODO: hide chat button
  return (
    <Container maxW={["auto", "auto", "auto", "1700px"]} px="15px" py="22px">
      <Flex alignItems="top" justifyContent="space-between" direction={["column", "column", "column", "row"]}>
        <Box w="100%">
          <React.Suspense
            fallback={
              <AspectRatio ratio={16 / 9}>
                <Skeleton w="100%" h="100%" />
              </AspectRatio>
            }
          >
            <TrackVideo track={track} streamOptions={streamOptionsState[0]} />
          </React.Suspense>
          {streamOptions.caption ? (
            <React.Suspense fallback={<Skeleton w="100%" h="80px" />}>
              <TrackCaption
                track={track}
                onUnsubscribe={() => {
                  setStreamOptions({ ...streamOptions, caption: false });
                }}
              />
            </React.Suspense>
          ) : null}

          <Box display={["flex", "flex", "none", "none"]} justifyContent="end" my={2}>
            <Box w="150px">{trackOptionsSelector("1")}</Box>
          </Box>
        </Box>

        {streamOptions.chat && track.chat ? (
          <Box
            maxW={["auto", "auto", "auto", "400px"]}
            h={["480px", "480px", "480px", "auto"]}
            w="100%"
            ml={["0", "0", "0", "30px"]}
          >
            <React.Suspense fallback={<Skeleton w="100%" h="100%" />}>
              <TrackChat track={track} />
            </React.Suspense>
          </Box>
        ) : null}
      </Flex>

      <Flex alignItems="top" justifyContent="space-between" direction={["column", "column", "column", "row"]} mt="12px">
        <Box w="100%">
          <TrackCardView
            card={track.card}
            nav={
              <HStack alignItems="flex-start" spacing="20px">
                {track.viewerCount ? <TrackViewerCount count={track.viewerCount} /> : null}
                <Box display={["none", "none", "block", "block"]}>{trackOptionsSelector("2")}</Box>
              </HStack>
            }
          />
        </Box>
        <Box maxW={["auto", "auto", "auto", "400px"]} w="100%" ml="30px">
          <AppVersionAlert />
        </Box>
      </Flex>
    </Container>
  );
}
Example #17
Source File: ChatType.tsx    From dope-monorepo with GNU General Public License v3.0 4 votes vote down vote up
export default function ChatType(props: Props) {
  const [ unreadMessages, setUnreadMessages ] = React.useState(0);
  const [ inputText, setInputText ] = React.useState('');
  const [ messages, setMessages ] = React.useState(props.messagesStore);
  const [ canSendMessage, setCanSendMessage ] = React.useState((props.chatMessageBoxes?.length ?? 0) < 3);

  const messagesListRef = React.useRef<HTMLUListElement>(null);

  let state = React.useRef({
    i: -1,
  });

  useEffect(() => {
    props.manager.events.on('chat_message', (message: DataTypes[NetworkEvents.SERVER_PLAYER_CHAT_MESSAGE]) => {
      setMessages(m => [...m, message]);
      const lastMessageEl = messagesListRef.current?.lastElementChild as HTMLLIElement;
      if (lastMessageEl && 
        lastMessageEl?.parentElement?.parentElement && !isVisible(lastMessageEl, lastMessageEl?.parentElement?.parentElement)) 
        setUnreadMessages(u => u + 1);
    });

    // constantly check chatMessageBoxes size and if it's less than 3, set canSendMessage to true
    const interval = setInterval(() => setCanSendMessage((props.chatMessageBoxes?.length ?? 0) < 3));
    return () => clearInterval(interval);
  }, []);

  const handleInputKey = (e: KeyboardEvent) => {
    if (e.key === 'Enter' && canSendMessage) handleSubmit(inputText);
    else if (e.key === 'Escape')
      // send "nothing", chat will get closed & message will not get sent
      handleSubmit('');
    else if (e.key === 'ArrowUp') {
      state.current.i = ++state.current.i % props.precedentMessages.length;
      const precedentMessage = props.precedentMessages[state.current.i];
      if (precedentMessage) setInputText(precedentMessage);
    } else if (e.key === 'ArrowDown') {
      // rolling window, wrap around
      state.current.i = --state.current.i % props.precedentMessages.length;
      if (state.current.i < 0) state.current.i = props.precedentMessages.length - 1;

      const precedentMessage = props.precedentMessages[state.current.i];
      if (precedentMessage) setInputText(precedentMessage);
    }
  };

  const handleSubmit = (content: string) => {
    if (content.length > 150)
      return;

    props.manager.events.emit('chat_submit', content);
  };

  return (
    <ChakraProvider theme={theme}>
      <Container 
      style={{
        position: 'absolute',
        backgroundColor: 'rgba(0,0,0,0.7)',
        borderRadius: '0.5rem',
        boxShadow: '0 0.5rem 1rem rgba(0, 0, 0, 0.7)',
        height: '30%',
        width: '30%',
        left: "1%",
        bottom: "1%",
      }}>
        <Stack style={{
          paddingTop: '1rem',
          height: '95%',
        }}>
          <div style={{
            display: 'flex',
            overflow: 'auto',
            flexDirection: 'column-reverse',
            marginBottom: '-3%',
          }}>
            <List ref={messagesListRef} spacing={-2} style={{
            }}>
              <Text style={{
                color: 'blueviolet',
              }}>
                Welcome to the Dopeverse!
              </Text>
              {messages.map((message, i) => <ListItem key={i}>
                  <HStack style={{
                    opacity: '0.8'
                  }}>
                    <Text style={{
                      color: 'white',
                    }}>
                      {message.author}: {message.message}
                    </Text>
                    <Spacer />
                    <Text style={{
                      color: 'grey',
                      fontSize: '0.6rem',
                    }}>
                      {new Date(message.timestamp).toLocaleString()}
                    </Text>
                  </HStack>
              </ListItem>)}
            </List>
          </div>
          <Spacer />
          <Center>
            <Button 
                  style={{
                    marginRight: '1%',
                    marginTop: '-10%'
                  }}
                  variant="primary"
                  backgroundColor="red.600"
                  hidden={inputText.length <= 150} 
                  onClick={() => setInputText(inputText.substring(0, 150))}
                >
                ❌ Message too long
            </Button>
            <Button 
              style={{
                marginTop: '-10%',
              }}
              variant="primary" 
              hidden={unreadMessages === 0} 
              onClick={(e) => {
                setUnreadMessages(0);
                e.currentTarget.hidden = true;
                if (messagesListRef.current)
                  (messagesListRef.current as HTMLOListElement).lastElementChild?.scrollIntoView({
                    behavior: 'smooth',
                  });
              }}
            >
              ⬇️ New message ({unreadMessages})
            </Button>
          </Center>
          <Center>
            <InputGroup width="90%" size="md">
              <Input
                autoFocus={true}
                focusBorderColor="white"
                onBlur={({ target }) => target.focus()}
                pr="4.5rem"
                placeholder="Message"
                _placeholder={{ color: '#b8b8b8' }}
                textColor="#f5f5f5"
                value={inputText}
                onChange={({ target }) => setInputText(target.value)}
                onKeyDown={handleInputKey}
                style={{
                  backgroundColor: 'rgba(0, 0, 0, 0.3)',
                }}
              />
              <InputRightElement width="4.5rem" style={{ paddingRight: '2%' }}>
                <Button h="1.75rem" size="sm" disabled={!canSendMessage} onClick={() => handleSubmit(inputText)}>
                  Send
                </Button>
              </InputRightElement>
            </InputGroup>
          </Center>
        </Stack>
      </Container>
    </ChakraProvider>
  );
}
Example #18
Source File: AttendeeEdit.tsx    From takeout-app with MIT License 4 votes vote down vote up
AttendeeEdit: React.FC = () => {
  const { data: conferenceData } = Api.useConference();
  const { data: session, error: sessionError } = Api.useSession();
  const history = useHistory();
  const [errorAlert, setErrorAlert] = React.useState<JSX.Element | null>(null);
  const [isRequesting, setIsRequesting] = React.useState<boolean>(false);

  const { register, handleSubmit, reset } = useForm<{
    name: string;
    gravatar_email: string;
  }>({
    defaultValues: {
      name: React.useMemo(() => session?.attendee?.name, [session]),
      gravatar_email: "",
    },
  });

  React.useEffect(() => {
    if (session?.attendee) reset({ name: session.attendee.name, gravatar_email: "" });
  }, [session?.attendee]);

  const onSubmit = handleSubmit(async (data) => {
    //const wasReady = session!.attendee?.is_ready;

    if (isRequesting) return;
    setIsRequesting(true);
    try {
      await Api.updateAttendee(data.name, data.gravatar_email);
      setErrorAlert(null);

      if (conferenceData) {
        history.push(`/tracks/${encodeURIComponent(conferenceData.conference.default_track)}`);
      } else {
        location.href = "/";
      }
    } catch (e) {
      setErrorAlert(
        <Box my={2}>
          <ErrorAlert error={e} />
        </Box>,
      );
    }
    setIsRequesting(false);
  });

  if (!session?.attendee) {
    return (
      <Container maxW={["auto", "auto", "auto", "1000px"]} px="15px" py="22px">
        <VStack>
          {sessionError ? (
            <Box my={2}>
              <ErrorAlert error={sessionError} />
            </Box>
          ) : null}
          <Spinner size="xl" />
        </VStack>
      </Container>
    );
  }

  return (
    <Container maxW={["auto", "auto", "auto", "1000px"]} px="15px" py="22px">
      <VStack justify="start" alignItems="start" spacing="30px">
        <Heading as="h2" color={Colors.main}>
          Settings
        </Heading>
        <HStack spacing="30px">
          <Avatar size="xl" bg={Colors.defaultAvatarBg} src={session.attendee.avatar_url} loading="lazy" />
          <Box maxW="750px">
            <Text mb={2}>
              Confirm your name and avatar used at live chat. These informations may be shared with other attendees once
              submitted.
            </Text>
            <Text>
              Be remember to abide by{" "}
              <Link href="https://rubykaigi.org/2021-takeout/policies" isExternal textDecoration="underline">
                our policies
              </Link>
              .
            </Text>
          </Box>
        </HStack>
        <form onSubmit={onSubmit}>
          <VStack justify="start" alignItems="start" spacing="30px">
            <FormControl id="login_reference" isRequired>
              <FormLabel>Name</FormLabel>
              <FormHelperText my={1}>Feel free to use nicknames, usernames, or handles :)</FormHelperText>
              <Input {...register("name")} maxW="460px" autoFocus />
            </FormControl>

            <FormControl id="login_email">
              <FormLabel>Gravatar Email Address</FormLabel>
              <FormHelperText my={1}>
                We use avatar images registered on{" "}
                <Link href="https://www.gravatar.com" isExternal textDecoration="underline">
                  Gravatar
                </Link>
                . Fill the following field if you desire to choose different email address for your Gravatar image.
              </FormHelperText>
              <Input
                {...register("gravatar_email")}
                type="email"
                maxW="460px"
                placeholder="(leave empty to remain unchanged)"
              />
            </FormControl>

            <Button type="submit" w="160px" h="46px" colorScheme="rk" isLoading={isRequesting}>
              {session.attendee.is_ready ? "Save" : "Continue"}
            </Button>
          </VStack>
        </form>

        {errorAlert}
      </VStack>
    </Container>
  );
}
Example #19
Source File: env.tsx    From ledokku with MIT License 4 votes vote down vote up
Env = () => {
  const { id: appId } = useParams<{ id: string }>();
  const { data, loading /* error */ } = useAppByIdQuery({
    variables: {
      appId,
    },
    ssr: false,
    skip: !appId,
  });

  const {
    data: envVarData,
    loading: envVarLoading,
    error: envVarError,
  } = useEnvVarsQuery({
    variables: {
      appId,
    },
    fetchPolicy: 'network-only',
  });

  if (!data) {
    return null;
  }

  // // TODO display error

  if (loading) {
    // TODO nice loading
    return <p>Loading...</p>;
  }

  const { app } = data;

  if (!app) {
    // TODO nice 404
    return <p>App not found.</p>;
  }

  return (
    <div>
      <HeaderContainer>
        <Header />
        <AppHeaderInfo app={app} />
        <AppHeaderTabNav app={app} />
      </HeaderContainer>

      <Container maxW="5xl" mt={10}>
        <Box py="5">
          <Heading as="h2" size="md">
            Set env variables
          </Heading>
          <Text color="gray.400" fontSize="sm">
            Environment variables change the way your app behaves. They are
            available both at run time and during the application
            build/compilation step for buildpack-based deploys.{' '}
            <Link
              textDecoration="underline"
              href="https://dokku.com/docs/configuration/environment-variables/"
              isExternal
            >
              Read more.
            </Link>
          </Text>
        </Box>

        {!envVarLoading && !envVarError && envVarData?.envVars.envVars && (
          <Box mb="8">
            {envVarData.envVars.envVars.map((envVar) => {
              return (
                <EnvForm
                  key={envVar.key}
                  name={envVar.key}
                  value={envVar.value}
                  appId={appId}
                />
              );
            })}
            <EnvForm
              key="newVar"
              name=""
              value=""
              appId={appId}
              isNewVar={true}
            />
          </Box>
        )}
      </Container>
    </div>
  );
}
Example #20
Source File: PublicPiggybankPage.tsx    From coindrop with GNU General Public License v3.0 4 votes vote down vote up
PublicPiggybankPage: FunctionComponent = () => {
    const { query: { piggybankName } } = useRouter();
    const { piggybankDbData } = useContext(PublicPiggybankDataContext);
    const theme = useTheme();
    const { user } = useUser();
    const { colorMode } = useColorMode();
    const accentColorLevelInitial = getAccentColorLevelInitial(colorMode);
    const accentColorLevelHover = getAccentColorLevelHover(colorMode);
    const {
        name,
        website,
        accentColor = "orange",
        verb,
        owner_uid,
    } = piggybankDbData;
    const accentColorInitial = theme.colors[accentColor][accentColorLevelInitial];
    const accentColorHover = theme.colors[accentColor][accentColorLevelHover];
    const pagePaymentMethodsDataEntries = Object.entries(piggybankDbData.paymentMethods ?? {});
    const preferredAddresses = pagePaymentMethodsDataEntries.filter(([, paymentMethodData]: any) => paymentMethodData.isPreferred);
    const otherAddresses = pagePaymentMethodsDataEntries.filter(([, paymentMethodData]: any) => !paymentMethodData.isPreferred);
    const WrapGroup: FunctionComponent = ({ children }) => (
        <Wrap
            justify="center"
        >
            {children}
        </Wrap>
    );

    type PaymentMethodButtonsFromEntriesProps = {
        entries: PaymentMethodDbObjEntry[]
    }
    const PaymentMethodButtonsFromEntries: FunctionComponent<PaymentMethodButtonsFromEntriesProps> = ({ entries }) => (
        <WrapGroup>
            {entries
            .sort(sortArrayByEntriesKeyAlphabetical)
            .map(([paymentMethodId, paymentMethodData]) => (
                <WrapItem key={paymentMethodId}>
                    <PaymentMethodButton
                        key={paymentMethodId}
                        paymentMethod={paymentMethodId}
                        paymentMethodValue={paymentMethodData.address}
                        isPreferred={paymentMethodData.isPreferred}
                        accentColor={accentColor}
                    />
                </WrapItem>
            ))}
        </WrapGroup>
    );
    const piggybankExists = !!owner_uid;
    const initialSetupComplete = name && accentColor && verb && pagePaymentMethodsDataEntries.length > 0;
    return (
        <>
        <NextSeo
            title={`${name ?? piggybankName}'s Coindrop (coindrop.to/${piggybankName})`}
            description={`Send money to ${name} with no fees`}
        />
        <Container
            maxW={theme.breakpoints.lg}
            mx="auto"
        >
            {user?.id
            && user.id === owner_uid
            && (
                <>
                <DataRefetcher />
                <ManagePiggybankBar
                    editButtonOptions={
                        initialSetupComplete
                        ? ({
                            text: 'Configure',
                            color: undefined,
                            icon: <SettingsIcon />,
                        }) : ({
                            text: 'Set up',
                            color: 'green',
                            icon: <SettingsIcon />,
                        })
                    }
                    initialSetupComplete={initialSetupComplete}
                />
                </>
            )}
            {initialSetupComplete ? (
                <Box
                    mb={6}
                >
                    <Box
                        padding="10px"
                        my={2}
                        mx={3}
                    >
                        <Center>
                            <Avatar />
                        </Center>
                        <Heading textAlign="center">
                            Choose a payment method to
                            {` ${verb} `}
                            {website ? (
                                <Link href={website} target="_blank" rel="noreferrer">
                                    <Heading
                                        as="span"
                                        color={accentColorInitial}
                                        textDecoration="underline"
                                        css={css`
                                            &:hover {
                                                color: ${accentColorHover};
                                            }
                                        `}
                                    >
                                            {name}
                                    </Heading>
                                </Link>
                            ) : (
                                <Heading
                                    as="span"
                                    color={accentColorInitial}
                                >
                                        {name}
                                </Heading>
                            )}
                            :
                        </Heading>
                    </Box>
                    <PaymentMethodButtonsFromEntries
                        entries={preferredAddresses}
                    />
                    <PaymentMethodButtonsFromEntries
                        entries={otherAddresses}
                    />
                    <PoweredByCoindropLink />
                </Box>
            ) : (
                <Heading mt={4} textAlign="center">
                    {piggybankExists ? 'This Coindrop has not been set up yet.' : 'This Coindrop does not exist'}
                    {/* TODO: Include action buttons to log in or landing page */}
                </Heading>
            )}
        </Container>
        </>
    );
}
Example #21
Source File: index.tsx    From ledokku with MIT License 4 votes vote down vote up
App = () => {
  const history = useHistory();
  const toast = useToast();
  const { id: appId } = useParams<{ id: string }>();
  const [isUnlinkModalOpen, setIsUnlinkModalOpen] = useState(false);
  const [isLinkModalOpen, setIsLinkModalOpen] = useState(false);
  const [arrayOfLinkLogs, setArrayOfLinkLogs] = useState<RealTimeLog[]>([]);
  const [arrayOfUnlinkLogs, setArrayOfUnlinkLogs] = useState<RealTimeLog[]>([]);
  const [databaseAboutToUnlink, setdatabaseAboutToUnlink] = useState<string>();
  const [isTerminalVisible, setIsTerminalVisible] = useState(false);
  const [processStatus, setProcessStatus] = useState<
    'running' | 'notStarted' | 'finished'
  >('notStarted');
  const [unlinkLoading, setUnlinkLoading] = useState(false);
  const [linkLoading, setLinkLoading] = useState(false);

  const [selectedDb, setSelectedDb] = useState({
    value: { name: '', id: '', type: '' },
    label: 'Please select database',
  });

  const [
    linkDatabaseMutation,
    {
      data: databaseLinkData,
      loading: databaseLinkLoading,
      error: databaseLinkError,
    },
  ] = useLinkDatabaseMutation();

  const [unlinkDatabaseMutation] = useUnlinkDatabaseMutation();
  useUnlinkDatabaseLogsSubscription({
    onSubscriptionData: (data) => {
      const logsExist = data.subscriptionData.data?.unlinkDatabaseLogs;
      if (logsExist) {
        setArrayOfUnlinkLogs((currentLogs) => {
          return [...currentLogs, logsExist];
        });
        if (
          logsExist.type === 'end:success' ||
          logsExist.type === 'end:failure'
        ) {
          setProcessStatus('finished');
        }
      }
    },
  });

  useLinkDatabaseLogsSubscription({
    onSubscriptionData: (data) => {
      const logsExist = data.subscriptionData.data?.linkDatabaseLogs;
      if (logsExist) {
        setArrayOfLinkLogs((currentLogs) => {
          return [...currentLogs, logsExist];
        });

        if (
          logsExist.type === 'end:success' ||
          logsExist.type === 'end:failure'
        ) {
          setProcessStatus('finished');
        }
      }
    },
  });

  const {
    data: databaseData,
    loading: databaseDataLoading,
  } = useDatabaseQuery();

  const { data, loading, refetch /* error */ } = useAppByIdQuery({
    variables: {
      appId,
    },
    fetchPolicy: 'cache-and-network',
    ssr: false,
    skip: !appId,
  });

  if (!data || !databaseData) {
    return null;
  }

  // // TODO display error

  if (loading || databaseDataLoading) {
    // TODO nice loading
    return <p>Loading...</p>;
  }

  const { databases } = databaseData;

  const { app } = data;

  if (!app) {
    // TODO nice 404
    return <p>App not found.</p>;
  }

  const linkedDatabases = app.databases;
  const linkedIds = linkedDatabases?.map((db) => db.id);
  const notLinkedDatabases = databases.filter((db) => {
    return linkedIds?.indexOf(db.id) === -1;
  });
  // Hacky way to add create new database to link db select
  notLinkedDatabases.length > 0 &&
    notLinkedDatabases.push({ name: 'Create new database' } as any);

  const dbOptions = notLinkedDatabases.map((db) => {
    return {
      value: { name: db.name, id: db.id, type: db.type },
      label: <DatabaseLabel type={db.type} name={db.name} />,
    };
  });

  const handleUnlink = async (databaseId: string, appId: string) => {
    try {
      await unlinkDatabaseMutation({
        variables: {
          input: {
            databaseId,
            appId,
          },
        },
      });
      setIsTerminalVisible(true);
      setUnlinkLoading(true);
    } catch (e) {
      toast.error(e.message);
    }
  };

  const handleConnect = async (databaseId: string, appId: string) => {
    try {
      await linkDatabaseMutation({
        variables: {
          input: {
            databaseId,
            appId,
          },
        },
      });
      setSelectedDb({
        value: { name: '', id: '', type: '' },
        label: 'Please select database',
      });
      setIsTerminalVisible(true);
      setLinkLoading(true);
    } catch (e) {
      toast.error(e.message);
    }
  };

  return (
    <div>
      <HeaderContainer>
        <Header />
        <AppHeaderInfo app={app} />
        <AppHeaderTabNav app={app} />
      </HeaderContainer>

      <Container maxW="5xl" mt={10}>
        <div className="grid sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2 gap-4 mt-10">
          <div>
            <Heading as="h2" size="md" py={5}>
              App info
            </Heading>
            <div className="bg-gray-100 shadow overflow-hidden rounded-lg border-b border-gray-200">
              <Table mt="4" mb="4" variant="simple">
                <Tbody mt="10">
                  <Tr py="4">
                    <Td className="font-semibold" py="3" px="4">
                      App name
                    </Td>
                    <Td py="3" px="4">
                      {app.name}
                    </Td>
                  </Tr>
                  <Tr>
                    <Td className="font-semibold" py="7" px="4">
                      id
                    </Td>
                    <Td w="1/3" py="3" px="4">
                      {app.id}
                    </Td>
                  </Tr>
                  <Tr>
                    <Td className="font-semibold" py="3" px="4">
                      Created at
                    </Td>
                    <Td py="3" px="4">
                      {app.createdAt}
                    </Td>
                  </Tr>
                </Tbody>
              </Table>
            </div>
          </div>
          <div className="w-full">
            <Heading as="h2" size="md" py={5}>
              Databases
            </Heading>
            {databases.length === 0 ? (
              <>
                <div className="mt-4 mb-4">
                  <h2 className="text-gray-400">
                    Currently you haven't created any databases, to do so
                    proceed with the database creation flow
                  </h2>
                </div>
                <RouterLink
                  to={{
                    pathname: '/create-database/',
                    state: app.name,
                  }}
                >
                  <Button width="large" color={'grey'}>
                    Create a database
                  </Button>
                </RouterLink>
              </>
            ) : (
              <>
                {notLinkedDatabases.length !== 0 ? (
                  <div>
                    <Listbox
                      as="div"
                      value={selectedDb}
                      //@ts-ignore
                      onChange={
                        selectedDb.value.name !== 'Create new database'
                          ? setSelectedDb
                          : history.push({
                              pathname: '/create-database',
                              state: app.name,
                            })
                      }
                    >
                      {({ open }) => (
                        <div className="relative w-80">
                          <Listbox.Button className="cursor-default relative w-full rounded-md border border-gray-300 bg-white pl-3 pr-10 py-2 text-left focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition ease-in-out duration-150 sm:text-sm sm:leading-5">
                            <span className="block truncate">
                              {selectedDb.label}
                            </span>
                            <span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
                              <svg
                                className="h-5 w-5 text-gray-400"
                                viewBox="0 0 20 20"
                                fill="none"
                                stroke="currentColor"
                              >
                                <path
                                  d="M7 7l3-3 3 3m0 6l-3 3-3-3"
                                  strokeWidth="1.5"
                                  strokeLinecap="round"
                                  strokeLinejoin="round"
                                />
                              </svg>
                            </span>
                          </Listbox.Button>
                          {open && (
                            <Transition
                              show={open}
                              enter="transition ease-out duration-100"
                              enterFrom="transform opacity-0 scale-95"
                              enterTo="transform opacity-100 scale-100"
                              leave="transition ease-in duration-75"
                              leaveFrom="transform opacity-100 scale-100"
                              leaveTo="transform opacity-0 scale-95"
                              className="absolute mt-1 w-full rounded-md bg-white shadow-lg z-10"
                            >
                              <Listbox.Options
                                static
                                className="max-h-60 rounded-md py-1 text-base leading-6 ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm sm:leading-5"
                              >
                                {dbOptions.map(
                                  (db) =>
                                    db.value.id !== selectedDb.value.id && (
                                      <Listbox.Option
                                        key={dbOptions.indexOf(db)}
                                        value={db as any}
                                      >
                                        {({ active }) => (
                                          <div
                                            className={cx(
                                              'cursor-default select-none relative py-2 px-4',
                                              {
                                                'bg-gray-200': active,
                                                'bg-white text-black': !active,
                                              }
                                            )}
                                          >
                                            {db.label}
                                          </div>
                                        )}
                                      </Listbox.Option>
                                    )
                                )}
                              </Listbox.Options>
                            </Transition>
                          )}
                        </div>
                      )}
                    </Listbox>
                    {databaseLinkError && (
                      <p className="text-red-500 text-sm font-semibold">
                        {databaseLinkError.graphQLErrors[0].message}
                      </p>
                    )}

                    <Button
                      color="grey"
                      width="large"
                      className="mt-2"
                      isLoading={
                        databaseLinkLoading &&
                        !databaseLinkData &&
                        !databaseLinkError
                      }
                      disabled={!selectedDb.value.id || linkLoading}
                      onClick={() => {
                        setIsLinkModalOpen(true);
                      }}
                    >
                      Link database
                    </Button>
                    {isLinkModalOpen && (
                      <Modal>
                        <ModalTitle>Link database</ModalTitle>
                        <ModalDescription>
                          {isTerminalVisible ? (
                            <>
                              <p className="mb-2 ">
                                Linking <b>{selectedDb.value.name}</b> with{' '}
                                <b>{app.name}</b>!
                              </p>
                              <p className="text-gray-500 mb-2">
                                Linking process usually takes a couple of
                                minutes. Breathe in, breathe out, logs are about
                                to appear below:
                              </p>
                              <Terminal className={'w-6/6'}>
                                {arrayOfLinkLogs.map((log) => (
                                  <p
                                    key={arrayOfLinkLogs.indexOf(log)}
                                    className="text-s leading-5"
                                  >
                                    {log.message}
                                  </p>
                                ))}
                              </Terminal>
                            </>
                          ) : (
                            <p>
                              Are you sure, you want to link{' '}
                              <b>{selectedDb.value.name}</b> to{' '}
                              <b>{app.name}</b>?
                            </p>
                          )}
                        </ModalDescription>
                        <ModalButton
                          ctaFn={() => {
                            setProcessStatus('running');
                            handleConnect(selectedDb.value.id, appId);
                          }}
                          ctaText={'Link'}
                          otherButtonText={'Cancel'}
                          isCtaLoading={isTerminalVisible ? false : linkLoading}
                          isCtaDisabled={isTerminalVisible}
                          isOtherButtonDisabled={processStatus === 'running'}
                          closeModal={() => {
                            setIsLinkModalOpen(false);
                            refetch({ appId });
                            setLinkLoading(false);
                            setIsTerminalVisible(false);
                            setProcessStatus('notStarted');
                          }}
                        />
                      </Modal>
                    )}
                  </div>
                ) : (
                  <>
                    <p className="mt-3 mb-3 text-cool-gray-400">
                      All your databases are already linked to this app! If you
                      want to create more databases proceed with create database
                      flow.
                    </p>
                    <div className="ml-80">
                      <Link to="/create-database">
                        <Button
                          color={'grey'}
                          variant="outline"
                          className="text-sm mr-3"
                        >
                          Create database
                        </Button>
                      </Link>
                    </div>
                  </>
                )}
                {!loading && app && app.databases && (
                  <>
                    <h2 className="mb-1 mt-3 font-semibold">
                      {app.databases.length > 0 && 'Linked databases'}
                    </h2>
                    {app.databases.map((database) => (
                      <div
                        key={app.databases?.indexOf(database)}
                        className="flex flex-row justify-start"
                      >
                        <Link
                          to={`/database/${database.id}`}
                          className="py-2 block"
                        >
                          <div className="w-64 flex items-center py-3 px-2 shadow hover:shadow-md transition-shadow duration-100 ease-in-out rounded bg-white">
                            {database.type === 'POSTGRESQL' ? (
                              <>
                                <PostgreSQLIcon size={16} className="mr-1" />
                              </>
                            ) : undefined}
                            {database.type === 'MONGODB' ? (
                              <>
                                <MongoIcon size={16} className="mr-1" />
                              </>
                            ) : undefined}
                            {database.type === 'REDIS' ? (
                              <>
                                <RedisIcon size={16} className="mr-1" />
                              </>
                            ) : undefined}
                            {database.type === 'MYSQL' ? (
                              <>
                                <MySQLIcon size={16} className="mr-1" />
                              </>
                            ) : undefined}
                            {database.name}
                          </div>
                        </Link>
                        <Button
                          width="normal"
                          className="mt-4 ml-2 h-10"
                          color="red"
                          onClick={() => {
                            setIsUnlinkModalOpen(true);
                            setdatabaseAboutToUnlink(database.name);
                          }}
                        >
                          Unlink
                        </Button>

                        {isUnlinkModalOpen && (
                          <Modal>
                            <ModalTitle>Unlink database</ModalTitle>
                            <ModalDescription>
                              {isTerminalVisible ? (
                                <>
                                  <p className="mb-2 ">
                                    Unlinking <b>{app.name}</b>
                                    from <b>{databaseAboutToUnlink}</b>!
                                  </p>
                                  <p className="text-gray-500 mb-2">
                                    Unlinking process usually takes a couple of
                                    minutes. Breathe in, breathe out, logs are
                                    about to appear below:
                                  </p>
                                  <Terminal className={'w-6/6'}>
                                    {arrayOfUnlinkLogs.map((log) => (
                                      <p
                                        key={arrayOfUnlinkLogs.indexOf(log)}
                                        className="text-s leading-5"
                                      >
                                        {log.message}
                                      </p>
                                    ))}
                                  </Terminal>
                                </>
                              ) : (
                                <p>
                                  Are you sure, you want to unlink{' '}
                                  <b>{app.name} </b>
                                  from <b>{databaseAboutToUnlink}</b> ?
                                </p>
                              )}
                            </ModalDescription>
                            <ModalButton
                              ctaFn={() => {
                                setProcessStatus('running');
                                handleUnlink(database.id, appId);
                              }}
                              ctaText={'Unlink'}
                              otherButtonText={'Cancel'}
                              isOtherButtonDisabled={
                                processStatus === 'running'
                              }
                              isCtaLoading={
                                isTerminalVisible ? false : unlinkLoading
                              }
                              isCtaDisabled={isTerminalVisible === true}
                              closeModal={() => {
                                setIsUnlinkModalOpen(false);
                                refetch({ appId });
                                setUnlinkLoading(false);
                                setIsTerminalVisible(false);
                                setdatabaseAboutToUnlink('');
                                setProcessStatus('notStarted');
                              }}
                            />
                          </Modal>
                        )}
                      </div>
                    ))}
                  </>
                )}
              </>
            )}
          </div>
        </div>
      </Container>
    </div>
  );
}
Example #22
Source File: LandingPage.tsx    From coindrop with GNU General Public License v3.0 4 votes vote down vote up
LandingPage: FC<Props> = ({
    headingTextPrimaryPreUnderline,
    headingTextPrimaryUnderline,
    headingTextPrimaryPostUnderline,
    headingTextSecondary,
    headingTextTertiary,
    smartphoneMockupImagePublicPath,
    showSubscriptionPlatforms,
    ShareOptions,
    shareOptionsHeading,
    advertiseOpenSource,
    getStartedText,
    smartphoneMockupImageWidth,
    smartphoneMockupImageHeight,
    createCoindropInputPlaceholder,
    logoSubtitle,
}) => {
    const {
        isOpen: isAuthOpen,
        onOpen: onAuthOpen,
        onClose: onAuthClose,
    } = useDisclosure();
    const router = useRouter();
    const { user } = useUser();
    useEffect(() => {
        if (user) {
            const pendingLoginCreatePiggybankPath = cookies.get('pendingLoginCreatePiggybankPath');
            if (pendingLoginCreatePiggybankPath) {
                router.push('/create');
            } else {
                router.push('/dashboard');
            }
        }
    }, [user]);
    useEffect(() => {
        if (router.query.auth) {
            onAuthOpen();
        } else {
            onAuthClose();
        }
    }, [router.query]);
    return (
        <>
        <AuthModal
            isOpen={isAuthOpen}
        />
        <HeaderFooterContainer>
            <Navbar
                logoSubtitle={logoSubtitle}
            />
        </HeaderFooterContainer>
        <Container
            maxW="100%"
            mx="auto"
            px={4}
            mb={6}
        >
            <Container
                my="3rem"
                maxW="98em" // there is no theme.breakpoints.2xl
            >
                <HeadingTextPrimary
                    textPreUnderline={headingTextPrimaryPreUnderline}
                    textUnderline={headingTextPrimaryUnderline}
                    textPostUnderline={headingTextPrimaryPostUnderline}
                />
                <Text fontSize="lg" textAlign="center" mt={3}>
                    {headingTextSecondary}
                </Text>
                <Text fontSize="lg" textAlign="center" mt={2}>
                    <b>{headingTextTertiary}</b>
                </Text>
                <Center mt={8}>
                    <Image
                        src={smartphoneMockupImagePublicPath}
                        alt="Smartphone mockup"
                        height={smartphoneMockupImageHeight}
                        width={smartphoneMockupImageWidth}
                    />
                </Center>
            </Container>
            <ContentContainer>
                <ContentContainerHeading withThroughline>
                    ➀ Pick a custom URL
                </ContentContainerHeading>
                <Box
                    mt={8}
                >
                    <CreatePiggybankInput
                        createButtonColorScheme="orange"
                        onCancel={null}
                        instanceId="top"
                        buttonText="Check availability"
                        placeholder={createCoindropInputPlaceholder}
                    />
                </Box>
            </ContentContainer>
            <ContentContainer>
                <ContentContainerHeading withThroughline>
                    ➁ Add your payment methods
                </ContentContainerHeading>
                <Flex
                    direction={['column', 'row']}
                    wrap="wrap"
                    maxW="80%"
                    mx="auto"
                >
                    <PaymentMethodContainer title="Digital wallets" paymentMethodCategory="digital-wallet" />
                    <PaymentMethodContainer title="Digital assets" paymentMethodCategory="digital-asset" />
                    {showSubscriptionPlatforms && (
                        <PaymentMethodContainer title="Subscription platforms" paymentMethodCategory="subscription-platform" />
                    )}
                </Flex>
            </ContentContainer>
            <ContentContainer>
                <ContentContainerHeading withThroughline>
                    ➂ {shareOptionsHeading}
                </ContentContainerHeading>
                <ShareOptions />
            </ContentContainer>
            {advertiseOpenSource && (
                <ContentContainer
                    boxProps={{
                        borderRadius: '16px',
                        position: 'relative',
                    }}
                >
                    <ContentContainerHeading>
                        Open-Source
                    </ContentContainerHeading>
                    <Flex align="center" justify="center" mt={4}>
                        <GithubIcon
                            opacity={0.9}
                            boxSize="72px"
                            mr={4}
                        />
                        <Text
                            fontSize="lg"
                        >
                            {'The source code for Coindrop is publicly available on '}
                            <Link isExternal href={githubUrl}>
                                Github
                            </Link>
                        </Text>
                    </Flex>
                </ContentContainer>
            )}
            <ContentContainer>
                <ContentContainerHeading>
                    Get started ?
                </ContentContainerHeading>
                <Text textAlign="center" fontSize="lg">
                    {getStartedText}
                </Text>
                <Box mt={2}>
                    <CreatePiggybankInput
                        createButtonColorScheme="orange"
                        onCancel={null}
                        instanceId="bottom"
                        buttonText="Create"
                        placeholder={createCoindropInputPlaceholder}
                    />
                </Box>
            </ContentContainer>
        </Container>
        <HeaderFooterContainer>
            <Footer />
        </HeaderFooterContainer>
        </>
    );
}
Example #23
Source File: logs.tsx    From ledokku with MIT License 4 votes vote down vote up
Logs = () => {
  const { id: appId } = useParams<{ id: string }>();

  const { data, loading /* error */ } = useAppByIdQuery({
    variables: {
      appId,
    },
  });

  const {
    data: appLogsData,
    loading: appLogsLoading,
    error: appLogsError,
  } = useAppLogsQuery({
    variables: {
      appId,
    },
    // we fetch status every 2 min 30 sec
    pollInterval: 15000,
  });

  const memoizedLogsHtml = useMemo(() => {
    if (!appLogsData?.appLogs.logs) {
      return null;
    }
    const data = appLogsData.appLogs.logs.map((log) => {
      const ansiIUp = new AnsiUp();
      const html = ansiIUp.ansi_to_html(log);
      return html;
    });
    return data;
  }, [appLogsData]);

  if (!data) {
    return null;
  }

  // // TODO display error

  if (loading) {
    // TODO nice loading
    return <p>Loading...</p>;
  }

  const { app } = data;

  if (!app) {
    // TODO nice 404
    return <p>App not found.</p>;
  }

  return (
    <div>
      <HeaderContainer>
        <Header />
        <AppHeaderInfo app={app} />
        <AppHeaderTabNav app={app} />
      </HeaderContainer>

      <Container maxW="5xl" mt={10}>
        <Heading as="h2" size="md" py={5}>
          Logs for {app.name} app:
        </Heading>

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

        {appLogsError ? (
          <Alert
            status="error"
            variant="top-accent"
            borderBottomRadius="base"
            boxShadow="md"
          >
            <AlertDescription>{appLogsError.message}</AlertDescription>
          </Alert>
        ) : null}

        {!appLogsLoading && !appLogsError && !appLogsData ? (
          <Alert
            status="info"
            variant="top-accent"
            borderBottomRadius="base"
            boxShadow="md"
          >
            <AlertDescription>
              There are no logs for {app.name}.
              <br />
              App is not deployed or still deploying.
            </AlertDescription>
          </Alert>
        ) : null}

        {memoizedLogsHtml ? (
          <Terminal mb="8">
            {memoizedLogsHtml.map((html, index) => (
              <p key={index} dangerouslySetInnerHTML={{ __html: html }}></p>
            ))}
          </Terminal>
        ) : null}
      </Container>
    </div>
  );
}
Example #24
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 #25
Source File: create-app-github.tsx    From ledokku with MIT License 4 votes vote down vote up
CreateAppGithub = () => {
  const history = useHistory();
  const toast = useToast();
  const { user } = useAuth();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  const repoOptions: RepoOption[] = [];

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

  let branchOptions: BranchOption[] = [];

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

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

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

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

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

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

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

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

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

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

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

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

            <ModalFooter>
              <Button
                color="grey"
                variant="outline"
                className="mr-3"
                onClick={() => setIsProceedModalOpen(false)}
              >
                Cancel
              </Button>
              <Button
                color="grey"
                onClick={() => {
                  handleOpen();
                  setIsProceedModalOpen(false);
                }}
              >
                Proceed
              </Button>
            </ModalFooter>
          </ModalContent>
        </Modal>
      </Container>
    </>
  );
}
Example #26
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 #27
Source File: dashboard.tsx    From ledokku with MIT License 4 votes vote down vote up
Dashboard = () => {
  // const history = useHistory();
  const { data /* loading, error */ } = useDashboardQuery({
    fetchPolicy: 'cache-and-network',
  });

  // TODO show loading
  // TODO handle error

  // TODO if no apps or dbs show onboarding screen

  return (
    <div>
      <HeaderContainer>
        <Header />
        <HomeHeaderTabNav />
      </HeaderContainer>

      <Container maxW="5xl" py={6}>
        <Box display="flex" justifyContent="flex-end" pb={6}>
          <Link to="/create-database">
            <Button colorScheme="gray" variant="outline" fontSize="sm" mr={3}>
              Create database
            </Button>
          </Link>
          <Link to="/create-app">
            <Button colorScheme="gray" fontSize="sm">
              Create app
            </Button>
          </Link>
        </Box>

        <Grid
          as="main"
          templateColumns="repeat(12, 1fr)"
          gap={{ base: 6, md: 20 }}
          pt={4}
        >
          <GridItem colSpan={{ base: 12, md: 7 }}>
            <Heading as="h2" size="md" py={5}>
              Apps
            </Heading>
            {data?.apps.length === 0 ? (
              <Text fontSize="sm" color="gray.400">
                No apps deployed.
              </Text>
            ) : null}
            {data?.apps.map((app) => (
              <Box
                key={app.id}
                py={3}
                borderBottom={'1px'}
                borderColor="gray.200"
              >
                <Box mb={1} color="gray.900" fontWeight="medium">
                  <Link to={`/app/${app.id}`}>{app.name}</Link>
                </Box>
                <Box
                  fontSize="sm"
                  color="gray.400"
                  display="flex"
                  justifyContent="space-between"
                >
                  <Text display="flex" alignItems="center">
                    <Box mr={1} as="span">
                      {app.appMetaGithub ? (
                        <GithubIcon size={16} />
                      ) : (
                        <Image
                          boxSize="16px"
                          objectFit="cover"
                          src="/dokku.png"
                          alt="dokkuLogo"
                        />
                      )}
                    </Box>

                    {app.appMetaGithub
                      ? `${app.appMetaGithub.repoOwner}/${app.appMetaGithub.repoName}`
                      : ''}
                  </Text>
                  <Text>
                    Created on {format(new Date(app.createdAt), 'MM/DD/YYYY')}
                  </Text>
                </Box>
              </Box>
            ))}

            <Heading as="h2" size="md" py={5} mt={8}>
              Databases
            </Heading>
            {data?.databases.length === 0 ? (
              <Text fontSize="sm" color="gray.400">
                No databases created.
              </Text>
            ) : null}
            {data?.databases.map((database) => {
              const DbIcon = dbTypeToIcon(database.type);

              return (
                <Box
                  key={database.id}
                  py={3}
                  borderBottom={'1px'}
                  borderColor="gray.200"
                >
                  <Box mb={1} color="gray.900" fontWeight="medium">
                    <Link to={`/database/${database.id}`}>{database.name}</Link>
                  </Box>
                  <Box
                    fontSize="sm"
                    color="gray.400"
                    display="flex"
                    justifyContent="space-between"
                  >
                    <Text display="flex" alignItems="center">
                      <Box mr={1} as="span">
                        <DbIcon size={16} />
                      </Box>
                      {dbTypeToReadableName(database.type)}
                    </Text>
                    <Text>
                      Created on{' '}
                      {format(new Date(database.createdAt), 'MM/DD/YYYY')}
                    </Text>
                  </Box>
                </Box>
              );
            })}
          </GridItem>

          <GridItem colSpan={{ base: 12, md: 5 }}>
            <Heading as="h2" size="md" py={5}>
              Latest activity
            </Heading>
            <Text fontSize="sm" color="gray.400">
              Coming soon
            </Text>
          </GridItem>
        </Grid>
      </Container>
    </div>
  );
}