@chakra-ui/react#SimpleGrid JavaScript Examples

The following examples show how to use @chakra-ui/react#SimpleGrid. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.js    From codeursenseine.com with MIT License 6 votes vote down vote up
Speakers = () => {
  const data = useStaticQuery(graphql`
    query {
      allFile(
        filter: { sourceInstanceName: { eq: "speakers" } }
        sort: { fields: childMdx___frontmatter___name }
      ) {
        nodes {
          childMdx {
            frontmatter {
              name
              image {
                publicURL
              }
              company
              twitterLink
              githubLink
            }
            body
          }
        }
      }
    }
  `);

  const speakers = data.allFile.nodes.filter((speaker) => speaker.childMdx);

  return (
    <Stack my={5}>
      <SimpleGrid columns={{ base: 1, lg: 1, xl: 2 }} spacing={5}>
        {speakers.map((speaker, index) => (
          <SpeakerCard key={`speaker-${index}`} speaker={speaker} />
        ))}
      </SimpleGrid>
    </Stack>
  );
}
Example #2
Source File: Home.js    From react-sample-projects with MIT License 6 votes vote down vote up
Home = () => {
  const { items, itemsLoaded } = useSelector(state => state.product);
  const dispatch = useDispatch();

  useEffect(() => {
    if (!itemsLoaded) {
      dispatch(fetchProducts());
    }
    return () => {};
  }, [dispatch, itemsLoaded]);

  if (!items || items.length === 0) return <Box m="3">loading...</Box>;

  return (
    <SimpleGrid columns={{ sm: 1, md: 2, lg: 3, xl: 4 }} spacing="40px" p={3}>
      {items.map(i => (
        <Item key={i.id} item={i}></Item>
      ))}
    </SimpleGrid>
  );
}
Example #3
Source File: MeetupSponsors.js    From codeursenseine.com with MIT License 5 votes vote down vote up
MeetupSponsors = () => {
  const data = useStaticQuery(graphql`
    query {
      allFile(
        filter: {
          sourceInstanceName: { eq: "sponsors" }
          childMdx: { frontmatter: { isMeetupSponsor: { eq: true } } }
        }
        sort: { order: ASC, fields: childMdx___frontmatter___name }
      ) {
        nodes {
          childMdx {
            frontmatter {
              name
              link
              logo {
                publicURL
              }
            }
            body
            excerpt(pruneLength: 1000)
          }
        }
      }
    }
  `);

  return (
    <SimpleGrid columns={{ base: 1, sm: 2, lg: 3 }} gap={8}>
      {data.allFile.nodes.map((sponsor, index) => (
        <SponsorCard
          key={index}
          name={sponsor.childMdx.frontmatter.name}
          link={sponsor.childMdx.frontmatter.link}
          logoSrc={sponsor.childMdx.frontmatter.logo.publicURL}
          excerpt={sponsor.childMdx.excerpt}
        >
          {sponsor.childMdx.body}
        </SponsorCard>
      ))}
    </SimpleGrid>
  );
}
Example #4
Source File: SponsorsList.js    From codeursenseine.com with MIT License 5 votes vote down vote up
SponsorsList = ({ ...props }) => {
  const data = useStaticQuery(graphql`
    query {
      site {
        siteMetadata {
          currentYear
        }
      }
      allFile(
        filter: {
          sourceInstanceName: { eq: "sponsors" }
          childMdx: { frontmatter: { sponsor: { ne: "disabled" } } }
        }
        sort: { fields: childMdx___frontmatter___name }
      ) {
        nodes {
          childMdx {
            frontmatter {
              name
              link
              sponsor
              logo {
                publicURL
              }
            }
          }
        }
      }
    }
  `);

  const sponsors = data.allFile.nodes.filter(
    (node) => !!node.childMdx?.frontmatter?.sponsor
  );

  const year = data?.site?.siteMetadata?.currentYear;

  return (
    <Stack spacing={8} {...props}>
      <Heading as="h2" size="md">
        Sponsors {year} : {sponsors.length} sponsor
        {sponsors.length > 1 ? "s" : ""}.
      </Heading>
      <SimpleGrid columns={{ base: 3, sm: 4, lg: 5 }} gap={4}>
        {sponsors.map(({ childMdx: { frontmatter } }) => (
          <Card
            key={slugify(frontmatter.name)}
            p={0}
            isLink
            as="a"
            href={frontmatter.link}
          >
            <AspectRatio ratio={320 / 190}>
              <Image
                src={frontmatter.logo?.publicURL}
                alt={frontmatter.name}
                objectFit="fit"
              />
            </AspectRatio>
          </Card>
        ))}
      </SimpleGrid>
    </Stack>
  );
}
Example #5
Source File: ProjectListFull.js    From benjamincarlson.io with MIT License 5 votes vote down vote up
ProjectListFull = () => {
    const [searchValue, setSearchValue] = useState('');
    const { data, error } = useSWR('/api/projects', fetcher)
    if (error) return <div style={{ width: '100%' }}>Failed to load projects! Please check your internet connection. If the error persists, contact me.</div>
    if (!data) return (
        <div style={{ width: '100%' }}>
            <InputGroup mb={4} mr={4} w="100%">
                <Input
                    aria-label="Search by name, description, and language"
                    placeholder="Search by name, description, and language"
                />
                <InputRightElement children={<SearchIcon color="gray.500" />} />
            </InputGroup>
            <SimpleGrid minChildWidth="300px" spacing="40px">
                {[...Array(10)].map((_, i) => (
                    <Skeleton key={i} h="250px" />
                ))}
            </SimpleGrid>
        </div>
    )

    const filteredProjects = Object(data.repos)
        .filter((project) =>
            project?.name?.toLowerCase().includes(searchValue.toLowerCase())
            || project?.description?.toLowerCase().includes(searchValue.toLowerCase())
            || project?.language?.toLowerCase().includes(searchValue.toLowerCase())
        )
        .sort(
            (a, b) =>
                Number(b.stars) - Number(a.stars)
        )

    return (
        <>
            <InputGroup mb={4} mr={4} w="100%">
                <Input
                    aria-label="Search by name, description, and language"
                    onChange={(e) => setSearchValue(e.target.value)}
                    placeholder="Search by name, description, and language"
                />
                <InputRightElement children={<SearchIcon color="gray.500" />} />
            </InputGroup>
            <SimpleGrid minChildWidth="300px" spacing="40px">
                {!filteredProjects.length && <Text>No projects found for "<strong>{searchValue}</strong>"!</Text>}
                {filteredProjects
                    .map((p, index) => (
                        <ProjectCard
                            key={index}
                            title={p.name}
                            description={p.description}
                            repoHref={p.url}
                            languageColor={getLanguageColor(p.language)}
                            language={p.language}
                            starCount={p.stars}
                            stargazersUrl={p.stargazers_url}
                            homepage={p.homepage}
                        />
                    ))}
            </SimpleGrid>
        </>
    )
}
Example #6
Source File: statistics.js    From benjamincarlson.io with MIT License 5 votes vote down vote up
Statistics = () => {
    const { colorMode } = useColorMode()

    const colorSecondary = {
        light: 'gray.600',
        dark: 'gray.400'
    }
    return (
        <>
            <NextSeo
                title={title}
                description={description}
                canonical={url}
                openGraph={{
                    url,
                    title,
                    description
                }}
            />
            <Container>
                <Flex
                    flexDirection="column"
                    maxWidth="1000px"
                    alignSelf={[null, "center"]}
                >
                    <motion.div
                        initial={{ y: -20, opacity: 0 }}
                        animate={{ y: 0, opacity: 1 }}
                        transition={{ duration: .7, delay: .4 }}
                    >
                        <Heading letterSpacing="tight" my={4} as="h1" size="2xl">
                            Statistics
                        </Heading>
                        <Text color={colorSecondary[colorMode]} mb={6}>A simple dashboard containing various statistics. The data is fetched in realtime via Next.js api routes from various api's. Build your own by following along with this video.</Text>
                        <SimpleGrid minChildWidth="300px" spacing="20px">
                            <YouTubeData />
                            <GitHubData />
                            <StravaData />
                        </SimpleGrid>
                    </motion.div>
                </Flex>
            </Container>
        </>
    )
}
Example #7
Source File: ProductDetails.js    From react-sample-projects with MIT License 5 votes vote down vote up
ProductDetails = () => {
  const { product } = useSelector(state => state.product);
  const dispatch = useDispatch();
  const { state } = useLocation() || {};

  const params = useParams();

  const addItemToCartHandler = e => {
    e.stopPropagation();
    e.preventDefault();
    dispatch(addItemToCart(product));
  };

  useEffect(() => {
    if (!params.id) return;
    if (state?.item) {
      dispatch(setProductDetails(state?.item));
    } else {
      dispatch(fetchProductDetails(params.id));
    }
    return () => {};
  }, [params.id, dispatch, state?.item]);

  if (!product) return <Box m="3">loading...</Box>;

  return (
    <Box
      m="3"
      boxShadow="base"
      borderWidth="1px"
      borderRadius="lg"
      overflow="hidden"
    >
      <SimpleGrid columns={{ sm: 1, md: 2, lg: 2 }} spacing="40px">
        <Center>
          <Box p="6">
            <Image src={product.image} alt={product.title} maxH="400" />
          </Box>
        </Center>
        <Box p="6">
          <Box d="flex" alignItems="baseline">
            <Box
              color="gray.500"
              fontWeight="semibold"
              letterSpacing="wide"
              fontSize="xs"
              textTransform="uppercase"
            >
              {product.category}
            </Box>
          </Box>

          <Box textAlign="left">${product.price}</Box>
          <Box fontWeight="semibold" mt="1" as="h2" textAlign="left">
            Product Info:
          </Box>
          <Box textAlign="left" fontSize="md">
            {product.description}
          </Box>
          <Button
            onClick={addItemToCartHandler}
            m={3}
            variant={'solid'}
            colorScheme={'teal'}
            size={'sm'}
          >
            Add to Cart
          </Button>
        </Box>
      </SimpleGrid>
    </Box>
  );
}
Example #8
Source File: SavedBlobs.js    From blobs.app with MIT License 5 votes vote down vote up
SavedBlobs = ({ savedBlobs = [], deleteBlob, loadBlobs }) => {
  const cardHoverBg = useColorModeValue('gray.100', 'gray.700');
  useEffect(() => {
    if (!savedBlobs) loadBlobs();
  }, [savedBlobs]);
  return (
    <Box>
      {savedBlobs?.length === 0 && (
        <Box textAlign="center">
          <Text my="20" fontSize="2xl">
            No saved blobs found!
          </Text>
        </Box>
      )}
      <SimpleGrid
        columns={{ sm: 2, md: 4 }}
        spacing="40px"
        mb="20"
        justifyContent="center"
      >
        {savedBlobs?.map((blob) => (
          <LinkBox
            // h="200"
            rounded="2xl"
            p="5"
            borderWidth="1px"
            _hover={{ boxShadow: '2xl', background: cardHoverBg }}
            role="group"
          >
            <Text
              fontSize="sm"
              display="flex"
              alignItems="center"
              justifyContent="space-between"
            >
              <LinkOverlay
                style={{ textTransform: 'uppercase', fontWeight: 600 }}
                href={blob.url}
              >
                {blob.name}
              </LinkOverlay>
              <Button
                variant="unstyled"
                visibility="hidden"
                h="auto"
                _groupHover={{ visibility: 'visible' }}
                onClick={() => {
                  deleteBlob(blob.id);
                }}
              >
                <TrashIcon color="tomato" />
              </Button>
            </Text>
            <Divider mt="4" />

            <Blob {...blob} />
          </LinkBox>
        ))}
      </SimpleGrid>
    </Box>
  );
}
Example #9
Source File: DashboardPanels.js    From web-client with Apache License 2.0 5 votes vote down vote up
DashboardPanels = () => {
    const user = Auth.getLoggedInUser();
    user.preferences = initialiseUserPreferences(user);
    const [dashboardConfig, setDashboardConfig] = useState(user?.preferences?.['web-client.widgets'] || InitialiseWidgetConfig());
    const [visibleWidgets, setVisibleWidgets] = useState(filterWidgets(user));

    const onWidgetChange = ev => {
        setDashboardConfig(prev => ({ ...prev, [ev.target.name]: { ...prev[ev.target.name], visible: ev.target.checked } }));
    }

    const onSave = () => {
        const user = Auth.getLoggedInUser();
        user.preferences = initialiseUserPreferences(user);

        user.preferences['web-client.widgets'] = dashboardConfig;

        secureApiFetch(`/users/${user.id}`, {
            method: 'PATCH',
            body: JSON.stringify({ preferences: user.preferences })
        })
            .then(() => {
                localStorage.setItem('user', JSON.stringify(user));

                setVisibleWidgets(filterWidgets(user));

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

    if (dashboardConfig === null) return <Loading />

    return <section>
        <Title type="Home" title="Dashboard" icon={<IconChartBar />} />
        <PageTitle value="Dashboard" />
        <Tabs>
            <TabList>
                <Tab>View</Tab>
                <Tab>Configure</Tab>
            </TabList>
            <TabPanels>
                <TabPanel>
                    <SimpleGrid gap="3" columns={{ base: "1", md: "2", xl: "3" }}>
                        {visibleWidgets.length === 0 ? <WelcomeWidget /> : visibleWidgets}
                    </SimpleGrid>
                </TabPanel>
                <TabPanel>
                    <h4>Select which widgets to present in your dashboard</h4>
                    <br />
                    <Stack>
                        {Object.keys(Widgets).map(widgetKey => {
                            const widget = Widgets[widgetKey];
                            if (!widget.hasOwnProperty("permissions") || PermissionsService.isAllowed(widget.permissions, user.permissions)) {
                                return <Checkbox key={widgetKey} name={widgetKey} isChecked={dashboardConfig.hasOwnProperty(widgetKey) && dashboardConfig[widgetKey].visible} onChange={onWidgetChange}>{Widgets[widgetKey].title}. <em>{Widgets[widgetKey].description}</em></Checkbox>
                            } else {
                                return <></>
                            }
                        })}
                    </Stack>

                    <Button onClick={onSave}>Save</Button>
                </TabPanel>
            </TabPanels>
        </Tabs>
    </section>
}
Example #10
Source File: index.js    From codeursenseine.com with MIT License 4 votes vote down vote up
SponsorsPage = ({ pageContext }) => {
  const { sponsors } = pageContext;

  const data = useStaticQuery(graphql`
    {
      placeholderImage: file(
        relativePath: { eq: "ces/dossier-sponsoring.jpg" }
      ) {
        childImageSharp {
          gatsbyImageData(width: 250, quality: 80, layout: CONSTRAINED)
        }
      }
    }
  `);

  const sponsorLevels = ["platinium", "gold", "silver", "bronze"];

  return (
    <Layout theme="ces">
      <OGImage path="/images/ces/social.jpg" />
      <Seo title="Sponsors" />
      <Heading as="h1" mb={8}>
        Devenir Sponsor
      </Heading>

      <Grid templateColumns={["1fr", "1fr", "1fr 2fr"]} gap={8} mb={8}>
        <Box maxWidth="250px">
          <A
            d="block"
            boxShadow="brand"
            overflow="hidden"
            borderRadius="md"
            href="https://drive.google.com/file/d/1zclVxBxeUZFUxX2kxVXCoAW8CnFr3p40/view?usp=sharing"
            title="Dossier de sponsoring"
            target="_blank"
          >
            <GatsbyImage
              image={data.placeholderImage.childImageSharp.gatsbyImageData}
              alt="Première page du dossier de sponsoring"
            />
          </A>
        </Box>
        <Box>
          <Stack spacing={8}>
            <Text>
              Codeurs en Seine est à la recherche de sponsors pour proposer un
              événement d'une qualité toujours meilleure.
            </Text>
            <Text>
              Les partenaires des éditions précédentes ont confirmé la
              visibilité offerte par ce sponsoring, surtout dans le cadre d'une
              politique de recrutement.
            </Text>
            <Text>
              Si vous souhaitez soutenir l'événement, téléchargez{" "}
              <A
                href="https://drive.google.com/file/d/1zclVxBxeUZFUxX2kxVXCoAW8CnFr3p40/view?usp=sharing"
                target="_blank"
              >
                le dossier de sponsoring
              </A>
              ,{" "}
              <A
                href="https://docs.google.com/document/d/14dtwH8QfzXuvPddlbo2fYgRy78RPtwwU1vPsH9tdgr4/edit?usp=sharing"
                target="_blank"
              >
                la convention de sponsoring
              </A>{" "}
              et contactez-nous à l'adresse{" "}
              <A href="mailto:[email protected]">
                [email protected]
              </A>
              .
            </Text>
            <ButtonGroup>
              <Button
                as="a"
                href="https://drive.google.com/file/d/1zclVxBxeUZFUxX2kxVXCoAW8CnFr3p40/view?usp=sharing"
                target="_blank"
                colorScheme="brand"
              >
                Dossier de sponsoring
              </Button>
              <Box display="flex" flexDir="column">
                <Button
                  as="a"
                  href="https://docs.google.com/document/d/14dtwH8QfzXuvPddlbo2fYgRy78RPtwwU1vPsH9tdgr4/edit?usp=sharing"
                  target="_blank"
                  colorScheme="brand"
                  variant="outline"
                >
                  Convention de sponsoring
                </Button>
                <Button
                  as="a"
                  href="https://docs.google.com/document/d/1oI6vAZBttTuSgxHH__LCVS1XV8j7wdiozc1TEzuVEhk/edit?usp=sharing"
                  target="_blank"
                  colorScheme="brand"
                  variant="outline"
                  marginTop={4}
                >
                  ?? Sponsorship agreement
                </Button>
              </Box>
            </ButtonGroup>
          </Stack>
        </Box>
      </Grid>
      <Divider mb={6} />
      <Stack spacing={6}>
        {sponsorLevels.map((level) => {
          const thisLevelSponsors = sponsors.filter(
            (sponsor) => sponsor.frontmatter.sponsor === level
          );

          return (
            thisLevelSponsors.length > 0 && (
              <Stack spacing={6} key={level}>
                <Heading size="lg" color="brand.700" fontWeight="normal">
                  Sponsors {level}
                </Heading>
                <SimpleGrid columns={{ base: 1, sm: 2, lg: 3 }} gap={8}>
                  {thisLevelSponsors.map((sponsor, index) => (
                    <SponsorCard
                      key={index}
                      name={sponsor.frontmatter.name}
                      link={sponsor.frontmatter.link}
                      logoSrc={sponsor.frontmatter?.logo?.publicURL}
                      excerpt={sponsor.excerpt}
                      isDonator={sponsor.frontmatter.isDonator}
                    >
                      {sponsor.body}
                    </SponsorCard>
                  ))}
                </SimpleGrid>
                <Divider />
              </Stack>
            )
          );
        })}
      </Stack>
    </Layout>
  );
}
Example #11
Source File: ApplySection.js    From interspace.chat with GNU General Public License v3.0 4 votes vote down vote up
ApplySection = () => {
  const ref = useRef(null);
  const onScreen = useOnScreen(ref);
  const [openSpeakerApplication, setOpenSpeakerApplication] = useState(false);
  const [openContributorApplication, setOpenContributorApplication] = useState(
    false
  );
  const [openPerformerApplication, setOpenPerformerApplication] = useState(
    false
  );
  const [openSponsorApplication, setOpenSponsorApplication] = useState(false);

  return (
    <Box as="section" id="apply" position="relative">
      <Box
        ref={ref}
        className="__content"
        width="100%"
        transform={`translate3d(${onScreen ? 0 : "-70px"}, 0, 0)`}
        opacity={onScreen ? 1 : 0}
        transition="transform 0.3s 0.4s ease-in-out, opacity 0.6s 0.5s ease-in"
        willChange={true}
      >
        <Box className="__content__body" d={{base: 'unset', md: "flex"}} w="100%" flexFlow={{base: 'column wrap', md: "row nowrap"}} alignItems="center" justifyContent="space-between">
        <Container
            maxW={{base: '100%', md: "2xl"}}
          >
          <Text
            as="h2"
          >
            Apply here
          </Text>
          {/* <span className="fest-dates">9 - 23rd JUNE</span> */}
          </Container>
          <Container maxW={{base: '100%', md: "2xl"}} p={0} mt={{base: 5, md: 0}}>
          <Box
            maxW={{base: '100%', md: "2xl"}}
            p={{ base: 8, md: 12 }}
            sx={{
              bg: "rgba(255,255,255,0.1)",
              backdropFilter: "blur(7px)",
              borderRadius: "5px 30px 10px 0",
              boxShadow: "0 0 30px #00000070",
            }}
          >
            <SimpleGrid columns={{ base: 1 }} spacing={0}>
              <Stack spacing={{base: 2, md: 4}} >
                <Text
                  textTransform={"uppercase"}
                  fontWeight={500}
                  fontSize={{ base: "2.2vmin", md: "0.7vmax" }}
                  className="gradient"
                  p={0}
                  alignSelf={"flex-start"}
                  rounded={"md"}
                >
                  <span role="img" aria-label="Yay, come join us!">
                    ?
                  </span>{" "}
                  Join the party!{" "}
                  <span role="img" aria-label="Yay, come join us!">
                    ?
                  </span>
                </Text>
                <Text as="h3" mt={1}>
                  <span>MetaFest2 needs YOU</span>
                  <span
                    className="gradient"
                    role="img"
                    aria-label="Pointing at the forms below"
                  >
                    ?
                  </span>
                </Text>
                <Text as="p" >
                  What is an event without amazing folks like you! People
                  who want to help organise &amp; greet, tell us about their
                  projects, teach, sing, code...we'd love to see you. Apply
                  below.
                </Text>
                <Stack spacing={4} divider={<StackDivider />}>
                    <Feature
                      icon={SpeakerIcon}
                    iconBg={"yellow.900"}
                    text={"Speakers"}
                    call={() =>
                      setOpenSpeakerApplication(!openSpeakerApplication)
                    }
                  />
                    <Feature
                      icon={ContributorIcon}
                    iconBg={"green.900"}
                    text={"Contributors"}
                    call={() =>
                      setOpenContributorApplication(!openContributorApplication)
                    }
                  />
                    <Feature
                      icon={PerformerIcon}
                    iconBg={"purple.900"}
                    text={"Performers"}
                    call={() =>
                      setOpenPerformerApplication(!openPerformerApplication)
                    }
                  />
                    <Feature
                      icon={SponsorIcon}
                    iconBg={"purple.900"}
                    text={"Sponsors"}
                    call={() =>
                      setOpenSponsorApplication(!openSponsorApplication)
                    }
                  />
                </Stack>
              </Stack>
            </SimpleGrid>
          </Box>
          </Container>

        </Box>
      </Box>
      {openSpeakerApplication && (
        <>
          <Button
            position="absolute"
            bottom={{ base: 10, md: 20 }}
            aria-label="Open the speaker application form"
            right={6}
            colorScheme="pink"
            bg="#FF61E6"
            boxShadow="0 0 10px rgba(0, 0, 0, 0.6)"
            size="sm"
            transition="all 0.3s 0.8s ease"
            transform={`translateY(${openSpeakerApplication ? 0 : "-70px"})`}
            onClick={() => setOpenSpeakerApplication(!openSpeakerApplication)}
            zIndex={2003}
          >
            Close form
          </Button>
          <Box
            ref={ref}
            position="absolute"
            top="12.5vh"
            left={0}
            height="75vh"
            minH="75vh"
            width="100vw"
            sx={{
              bg: "rgba(25,0,50,0.4)",
              backdropFilter: "blur(7px)",
            }}
            boxShadow="0 0 30px rgba(0,0,0,0.8)"
            opacity={onScreen ? 1 : 0}
            transition="opacity 1.2s 0.8s ease-in-out"
            zIndex={2001}
            overflowY="scroll"
          >
            <AirtableSpeakerInstance />
          </Box>
        </>
      )}
      {openContributorApplication && (
        <>
          <Button
            aria-label="Open the contributor application form"
            position="absolute"
            bottom={{base: 10, md: 20}}
            right={6}
            colorScheme="pink"
            bg="#FF61E6"
            boxShadow="0 0 10px rgba(0, 0, 0, 0.6)"
            size="sm"
            transition="all 0.3s 0.8s ease"
            transform={`translateY(${
              openContributorApplication ? 0 : "-70px"
            })`}
            onClick={() =>
              setOpenContributorApplication(!openContributorApplication)
            }
            zIndex={2002}
          >
            Close form
          </Button>
          <Box
            ref={ref}
            position="absolute"
            top="12.5vh"
            left={0}
            height="75vh"
            minH="75vh"
            midth="100vw"
            sx={{
              bg: "rgba(25,0,50,0.4)",
              backdropFilter: "blur(7px)",
            }}
            boxShadow="0 0 30px rgba(0,0,0,0.8)"
            opacity={onScreen ? 1 : 0}
            transition="opacity 1.2s 0.8s ease-in-out"
            zIndex={2001}
            overflowY="scroll"
          >
            <AirtableContributorInstance />
          </Box>
        </>
      )}
      {openPerformerApplication && (
        <>
          <Button
            aria-label="Open the performer application form"
            position="absolute"
            bottom={{base: 10, md: 20}}
            right={6}
            colorScheme="pink"
            bg="#FF61E6"
            boxShadow="0 0 10px rgba(0, 0, 0, 0.6)"
            size="sm"
            transition="all 0.3s 0.8s ease"
            transform={`translateY(${openPerformerApplication ? 0 : "-70px"})`}
            onClick={() =>
              setOpenPerformerApplication(!openPerformerApplication)
            }
            zIndex={2002}
          >
            Close form
          </Button>
          <Box
            ref={ref}
            position="absolute"
            top="12.5vh"
            left={0}
            height="75vh"
            minH="75vh"
            width="100vw"
            sx={{
              bg: "rgba(25,0,50,0.4)",
              backdropFilter: "blur(7px)",
            }}
            boxShadow="0 0 30px rgba(0,0,0,0.8)"
            opacity={onScreen ? 1 : 0}
            transition="opacity 1.2s 0.8s ease-in-out"
            zIndex={2001}
            overflowY="scroll"
          >
            <AirtablePerformerInstance />
          </Box>
        </>
      )}
      {openSponsorApplication && (
        <>
          <Button
            aria-label="Open the sponsor application form"
            position="absolute"
            bottom={{base: 10, md: 20}}
            right={6}
            colorScheme="pink"
            bg="#FF61E6"
            boxShadow="0 0 10px rgba(0, 0, 0, 0.6)"
            size="sm"
            transition="all 0.3s 0.8s ease"
            transform={`translateY(${openSponsorApplication ? 0 : "-70px"})`}
            onClick={() => setOpenSponsorApplication(!openSponsorApplication)}
            zIndex={2002}
          >
            Close form
          </Button>
          <Box
            ref={ref}
            position="absolute"
            top="12.5vh"
            left={0}
            height="75vh"
            minH="75vh"
            width="100vw"
            sx={{
              bg: "rgba(25,0,50,0.4)",
              backdropFilter: "blur(7px)",
            }}
            boxShadow="0 0 30px rgba(0,0,0,0.8)"
            opacity={onScreen ? 1 : 0}
            transition="opacity 1.2s 0.8s ease-in-out"
            zIndex={2001}
            overflowY="scroll"
          >
            <AirtableSponsorInstance />
          </Box>
        </>
      )}
    </Box>
  );
}
Example #12
Source File: index.js    From benjamincarlson.io with MIT License 4 votes vote down vote up
export default function Index() {
  const { colorMode } = useColorMode()

  const colorSecondary = {
    light: 'gray.600',
    dark: 'gray.400'
  }

  const iconColor = {
    light: 'gray.600',
    dark: 'gray.300'
  }

  const linkColor = {
    light: 'blue.400',
    dark: 'blue.600'
  }

  return (
    <>
      <NextSeo
        title={title}
        description={description}
        canonical={url}
        openGraph={{
          url,
          title,
          description
        }}
      />
      <Container>
        <Flex
          flexDirection="column"
          maxWidth="1000px"
          alignSelf={[null, "center"]}
        >
          {/* hero is defined inside of components/Container.js which allows it to have a different bg color without applying p to a bunch of tags */}
          <motion.div
            initial={{ y: -20, opacity: 0 }}
            animate={{ y: 0, opacity: 1 }}
            transition={{ duration: .7, delay: 2 }}
          >
            <Box as="section" mt={10} mb={20}>
              <Heading letterSpacing="tight" mt={8} size="lg" fontWeight={700} as="h2" mb={4}>About Me</Heading>
              <Text color={colorSecondary[colorMode]}>Hi everyone ?, I'm Benjamin Carlson. I go to <Link color="blue.500" href="https://www2.ccsu.edu/" isExternal>Central Connecticut State University</Link> where I study computer science and mathematics. My personal website is where I showcase my projects, writing, statistics, experience, and more. It also serves as a sandbox to play around with new technologies, as seen by the <Link href="https://github.com/bjcarlson42/benjamincarlson.io#overview" color={linkColor[colorMode]} isExternal>evolution</Link> of this website! Feel free to reach out via email or any social media.</Text>
            </Box>

            <Box as="section" mt={10} mb={20}>
              <Heading letterSpacing="tight" mt={8} size="lg" fontWeight={700} as="h2" mb={4}>Featured Projects ?‍?</Heading>
              <SimpleGrid minChildWidth="300px" spacing="40px">
                <ProjectCard
                  title="coffeeclass.io"
                  description="coffeeclass.io is a tutorial website I started to teach programming and computer science skills in a fun and easy to learn manner."
                  repoHref="https://github.com/carlson-technologies/coffeeclass.io"
                  demoHref="https://www.coffeeclass.io?utm_source=website&utm_campaign=benjamincarlson.io"
                  languageColor="#2b7489"
                  language="TypeScript"
                />
                <ProjectCard
                  title="benjamincarlson.io"
                  description="This website is a personal website I built to showcase my projects and experience."
                  repoHref="https://github.com/bjcarlson42/benjamincarlson.io"
                  demoHref="https://benjamincarlson.io"
                  languageColor="#f1e05a"
                  language="JavaScript"
                />
                <ProjectCard
                  title="Word Of The Day App"
                  description="A word of the day app built using Google's Flutter - a cross platform mobile app framework. View current and past words and save your favorites!"
                  repoHref="https://github.com/bjcarlson42/wotd"
                  youtubeId="https://youtu.be/17wMTF_bnnc"
                  languageColor="#00B4AB"
                  language="Dart"
                />
              </SimpleGrid>
            </Box>

            <Box as="section" mt={10} mb={20}>
              <Heading letterSpacing="tight" mt={8} mb={4} size="lg" fontWeight={700} as="h2">Publications ?</Heading>
              <Text color={colorSecondary[colorMode]}>I began writing about programming back in 2019 on my first blog that is no longer alive. Since then I have expanded to different media outlets and covered a variety of topics from programming, to productivity, to business.</Text>
              {/* <Flex align="center" mt={4}> */}
              <SimpleGrid minChildWidth="200px" spacing="20px" my={10}>
                <Flex flexDir="column">
                  <Icon as={YoutubeIcon} color="red.500" fontSize="2xl" mb={2} />
                  <Heading as="h3" size="md" fontWeight={400} mb={2} letterSpacing="tight">
                    <Link href='https://youtube.com/benjamincarlson' color={linkColor[colorMode]} isExternal>YouTube</Link>
                  </Heading>
                  <Text>I started uploading YouTube videos in 2020 when the pandemic started. I mostly upload programming tutorial videos but I also upload developer vlogs and informational videos. I have uploaded (almost) weekly since then and have grown my channel to an audience of over 4k subscribers and 450k views!</Text>
                </Flex>
                <Flex flexDir="column">
                  <Icon as={SiMedium} fontSize="2xl" mb={2} />
                  <Heading as="h3" size="md" fontWeight={400} mb={2} letterSpacing="tight">
                    <Link href='https://benjamincarlson.medium.com' color={linkColor[colorMode]} isExternal>Medium</Link>
                  </Heading>
                  <Text>Medium was the first publication I started. I wrote my <Link color="blue.500" href="https://levelup.gitconnected.com/using-javascript-to-scramble-a-rubiks-cube-306f52908f18" isExternal>first article</Link> in March 2020, and since then I have written about a dozen more articles. Nowadays I write less for Medium and more for coffeeclass.io.</Text>
                </Flex>
                <Flex flexDir="column">
                  <Icon as={FiCoffee} color="yellow.500" fontSize="2xl" mb={2} />
                  <Heading as="h3" size="md" fontWeight={400} mb={2} letterSpacing="tight">
                    <Link href='https://www.coffeeclass.io' color={linkColor[colorMode]} isExternal>coffeeclass.io</Link>
                  </Heading>
                  <Text>Because I enjoyed uploading YouTube videos about programming and writing about programming on Medium, I decided to start my own programming tutorial website, coffeeclass.io. If you are interested in writing about code, see our <Link color="blue.500" href="https://www.coffeeclass.io/contribute/getting-started" isExternal>getting started</Link> page.</Text>
                </Flex>
                <Flex flexDir="column">
                  <Icon as={BsGear} color="gray.500" fontSize="2xl" mb={2} />
                  <Heading as="h3" size="md" fontWeight={400} mb={2} letterSpacing="tight">
                    <Link href='https://www.engineering.coffeeclass.io' color={linkColor[colorMode]} isExternal>engineering.coffeeclass.io</Link>
                  </Heading>
                  <Text>The behind the scenes look at coffeeclass.io. On this site I write about the development of coffeeclass.io. Everything from the current tech stack, future plans, growing pains, and more.</Text>
                </Flex>
              </SimpleGrid>
              {/* </Flex> */}
              <Flex
                mb={4}
                bgColor={useColorModeValue("gray.100", "gray.900")}
                p={[5, 20, 50]}
                borderRadius={3}
                as="blockquote"
                borderLeft="10px solid"
                borderLeftColor={useColorModeValue("blue.400", "blue.700")}
              >
                <Icon as={GrBlockQuote} fontSize={40} color={colorSecondary[colorMode]} mr={4} />
                <Flex flexDir="column">
                  <Text fontSize="xl" fontStyle="italic" color={colorSecondary[colorMode]}>If You Can Think and Speak and Write, You Are Absolutely Deadly.</Text>
                  <Text fontSize="xl" fontWeight="bold" mt={2}>Jordan B. Peterson</Text>
                </Flex>
              </Flex>
            </Box>

            <Todo />
            <TechStack />

            <Box as="section">
              <Text mt={10}>Looks like you've made it to the end of this page... feel free to <Link href="https://youtube.com/benjamincarlson" isExternal color={linkColor[colorMode]}>check out my YouTube channel</Link> or
                visit <Link href="https://www.coffeeclass.io/?utm_source=website&utm_campaign=benjamincarlson.io" isExternal color={linkColor[colorMode]}>coffeeclass.io</Link> where
                you can find even more programming content.
              </Text>
            </Box>
          </motion.div>

        </Flex>
      </Container>
    </>
  )
}
Example #13
Source File: image-search.js    From idena-web with MIT License 4 votes vote down vote up
export function ImageSearchDialog({onPick, onClose, onError, ...props}) {
  const {t} = useTranslation()

  const searchInputRef = React.useRef()

  const [current, send] = useMachine(
    Machine({
      context: {
        images: [],
      },
      initial: 'idle',
      states: {
        idle: {},
        searching: {
          invoke: {
            // eslint-disable-next-line no-shadow
            src: (_, {query}) => searchImages(query),
            onDone: {
              target: 'done',
              actions: [
                assign({
                  images: (_, {data}) => data,
                }),
                log(),
              ],
            },
            onError: 'fail',
          },
        },
        done: {
          on: {
            PICK: {
              actions: [
                assign({
                  selectedImage: (_, {image}) => image,
                }),
                log(),
              ],
            },
          },
        },
        fail: {
          entry: [(_, {data: {message}}) => onError(message), log()],
        },
      },
      on: {
        SEARCH: 'searching',
      },
    })
  )

  const {images, selectedImage} = current.context
  const [query, setQuery] = useState()

  return (
    <Dialog
      size="lg"
      initialFocusRef={searchInputRef}
      onClose={onClose}
      {...props}
    >
      <DialogBody d="flex">
        <Stack minH="sm" maxH="sm" spacing={4} flex={1}>
          <Stack
            isInline
            as="form"
            onSubmit={e => {
              e.preventDefault()
              send('SEARCH', {query})
            }}
          >
            <InputGroup w="full">
              <InputLeftElement w={5} h={5} top={1.5} left={3}>
                <SearchIcon boxSize={3} color="gray.200" />
              </InputLeftElement>
              <Input
                ref={searchInputRef}
                type="search"
                id="query"
                placeholder={t('Search the picture on the web')}
                bg="gray.50"
                pl={10}
                value={query}
                onChange={e => setQuery(e.target.value)}
              />
            </InputGroup>
            <PrimaryButton type="submit">Search</PrimaryButton>
          </Stack>
          {eitherState(current, 'idle') && (
            <Flex direction="column" flex={1} align="center" justify="center">
              <Stack spacing={4} align="center" w="3xs">
                <Box p={3}>
                  <SearchIcon boxSize="56px" color="gray.100" />
                </Box>
                <Text color="muted" textAlign="center" w="full">
                  {t(
                    'Type your search in the box above to find images using search box'
                  )}
                </Text>
              </Stack>
            </Flex>
          )}
          {eitherState(current, 'done') && (
            <SimpleGrid
              columns={4}
              spacing={2}
              overflow="auto"
              px={6}
              style={{marginLeft: '-24px', marginRight: '-24px'}}
            >
              {images.map(({thumbnail}) => (
                <AspectRatio
                  ratio={1}
                  w={28}
                  minH={28}
                  bg={thumbnail === selectedImage ? 'blue.032' : 'white'}
                  borderColor={
                    thumbnail === selectedImage ? 'blue.500' : 'gray.50'
                  }
                  borderWidth={1}
                  borderRadius="md"
                  overflow="hidden"
                  position="relative"
                  transition="all 0.6s cubic-bezier(0.16, 1, 0.3, 1)"
                  onClick={() => {
                    send('PICK', {image: thumbnail})
                  }}
                  onDoubleClick={() => {
                    onPick(selectedImage)
                  }}
                >
                  <Image
                    src={thumbnail}
                    objectFit="contain"
                    objectPosition="center"
                    borderColor={
                      thumbnail === selectedImage ? 'blue.500' : 'transparent'
                    }
                    borderWidth={1}
                    borderRadius="md"
                  />
                </AspectRatio>
              ))}
            </SimpleGrid>
          )}
          {eitherState(current, 'searching') && (
            <Flex direction="column" flex={1} align="center" justify="center">
              <Spinner color="blue.500" />
            </Flex>
          )}
        </Stack>
      </DialogBody>
      <DialogFooter>
        <SecondaryButton onClick={onClose}>{t('Cancel')}</SecondaryButton>
        <PrimaryButton
          onClick={() => {
            onPick(selectedImage)
          }}
        >
          {t('Select')}
        </PrimaryButton>
      </DialogFooter>
    </Dialog>
  )
}
Example #14
Source File: BuilderProfileView.jsx    From scaffold-directory with MIT License 4 votes vote down vote up
export default function BuilderProfileView({ serverUrl, mainnetProvider, address, userProvider, userRole }) {
  const { builderAddress } = useParams();
  const { primaryFontColor, secondaryFontColor, borderColor, iconBgColor } = useCustomColorModes();
  const [builder, setBuilder] = useState();
  const [challengeEvents, setChallengeEvents] = useState([]);
  const [isLoadingBuilder, setIsLoadingBuilder] = useState(false);
  const [isBuilderOnBg, setIsBuilderOnBg] = useState(false);
  const [isLoadingTimestamps, setIsLoadingTimestamps] = useState(false);
  const toast = useToast({ position: "top", isClosable: true });
  const toastVariant = useColorModeValue("subtle", "solid");
  const challenges = builder?.challenges ? Object.entries(builder.challenges) : undefined;
  const acceptedChallenges = getAcceptedChallenges(builder?.challenges);
  const isMyProfile = builderAddress === address;

  const fetchBuilder = async () => {
    setIsLoadingBuilder(true);
    const fetchedBuilder = await axios.get(serverUrl + `/builders/${builderAddress}`);
    setBuilder(fetchedBuilder.data);

    try {
      await axios.get(bgBackendUrl + `/builders/${builderAddress}`);
    } catch (e) {
      // Builder Not found in BG
      setIsLoadingBuilder(false);
      return;
    }

    setIsBuilderOnBg(true);
    setIsLoadingBuilder(false);
  };

  useEffect(() => {
    fetchBuilder();
    // eslint-disable-next-line
  }, [builderAddress]);

  useEffect(() => {
    if (!builderAddress) {
      return;
    }

    async function fetchChallengeEvents() {
      setIsLoadingTimestamps(true);
      try {
        const fetchedChallengeEvents = await getChallengeEventsForUser(builderAddress);
        setChallengeEvents(fetchedChallengeEvents.sort(byTimestamp).reverse());
        setIsLoadingTimestamps(false);
      } catch (error) {
        toast({
          description: "Can't get challenges metadata. Please try again",
          status: "error",
          variant: toastVariant,
        });
      }
    }
    fetchChallengeEvents();
    // eslint-disable-next-line
  }, [builderAddress]);

  return (
    <Container maxW="container.xl">
      <SimpleGrid gap={14} columns={{ base: 1, xl: 4 }}>
        <GridItem colSpan={1}>
          <BuilderProfileCard
            builder={builder}
            mainnetProvider={mainnetProvider}
            isMyProfile={isMyProfile}
            userProvider={userProvider}
            fetchBuilder={fetchBuilder}
            userRole={userRole}
          />
        </GridItem>
        {isBuilderOnBg ? (
          <GridItem colSpan={{ base: 1, xl: 3 }}>
            <Box borderColor={borderColor} borderWidth={1} p={5}>
              <Flex direction="column" align="center" justify="center">
                <Image src="/assets/bg.png" mb={3} />
                <Text mb={3} fontSize="lg" fontWeight="bold">
                  This builder has upgraded to BuidlGuidl.
                </Text>
                <Button as={Link} href={`${BG_FRONTEND_URL}/builders/${builderAddress}`} isExternal colorScheme="blue">
                  View their profile on Buidlguidl
                </Button>
              </Flex>
            </Box>
          </GridItem>
        ) : (
          <GridItem colSpan={{ base: 1, xl: 3 }}>
            <HStack spacing={4} mb={8}>
              <Flex borderRadius="lg" borderColor={borderColor} borderWidth={1} p={4} w="full" justify="space-between">
                <Flex bg={iconBgColor} borderRadius="lg" w={12} h={12} justify="center" align="center">
                  <InfoOutlineIcon w={5} h={5} />
                </Flex>
                <div>
                  <Text fontSize="xl" fontWeight="medium" textAlign="right">
                    {acceptedChallenges.length}
                  </Text>
                  <Text fontSize="sm" color={secondaryFontColor} textAlign="right">
                    challenges completed
                  </Text>
                </div>
              </Flex>
              <Flex borderRadius="lg" borderColor={borderColor} borderWidth={1} p={4} w="full" justify="space-between">
                <Flex bg={iconBgColor} borderRadius="lg" w={12} h={12} justify="center" align="center">
                  <InfoOutlineIcon w={5} h={5} />
                </Flex>
                <div>
                  <Text fontSize="xl" fontWeight="medium" textAlign="right">
                    {builder?.function ? (
                      <Tag colorScheme={userFunctionDescription[builder?.function].colorScheme} variant="solid">
                        {userFunctionDescription[builder?.function].label}
                      </Tag>
                    ) : (
                      "-"
                    )}
                  </Text>
                  <Text fontSize="sm" color={secondaryFontColor} textAlign="right">
                    Role
                  </Text>
                </div>
              </Flex>
            </HStack>
            <Flex mb={4}>
              <Text fontSize="2xl" fontWeight="bold">
                Challenges
              </Text>
              <Spacer />
            </Flex>
            {isLoadingBuilder && <BuilderProfileChallengesTableSkeleton />}
            {!isLoadingBuilder &&
              (challenges ? (
                <Box overflowX="auto">
                  <Table>
                    {isMyProfile && (
                      <TableCaption>
                        <Button as={RouteLink} colorScheme="blue" to="/">
                          Start a challenge
                        </Button>
                      </TableCaption>
                    )}
                    <Thead>
                      <Tr>
                        <Th>Name</Th>
                        <Th>Contract</Th>
                        <Th>Live Demo</Th>
                        <Th>Updated</Th>
                        <Th>Status</Th>
                      </Tr>
                    </Thead>
                    <Tbody>
                      {challenges.map(([challengeId, lastSubmission]) => {
                        if (!challengeInfo[challengeId]) {
                          return null;
                        }
                        const lastEventForChallenge = challengeEvents.filter(
                          event => event.payload.challengeId === challengeId,
                        )[0];
                        return (
                          <Tr key={challengeId}>
                            <Td>
                              <Link as={RouteLink} to={`/challenge/${challengeId}`} fontWeight="700" color="teal.500">
                                {challengeInfo[challengeId].label}
                              </Link>
                            </Td>
                            <Td>
                              <Link
                                // Legacy branchUrl
                                href={lastSubmission.contractUrl || lastSubmission.branchUrl}
                                color="teal.500"
                                target="_blank"
                                rel="noopener noreferrer"
                              >
                                Code
                              </Link>
                            </Td>
                            <Td>
                              <Link
                                href={lastSubmission.deployedUrl}
                                color="teal.500"
                                target="_blank"
                                rel="noopener noreferrer"
                              >
                                Demo
                              </Link>
                            </Td>
                            <Td>
                              {isLoadingTimestamps ? (
                                <SkeletonText noOfLines={1} />
                              ) : (
                                <DateWithTooltip timestamp={lastEventForChallenge?.timestamp} />
                              )}
                            </Td>
                            <Td>
                              <ChallengeStatusTag
                                status={lastSubmission.status}
                                comment={lastSubmission.reviewComment}
                                autograding={lastSubmission.autograding}
                              />
                            </Td>
                          </Tr>
                        );
                      })}
                    </Tbody>
                  </Table>
                </Box>
              ) : (
                <Flex
                  justify="center"
                  align="center"
                  borderRadius="lg"
                  borderColor={borderColor}
                  borderWidth={1}
                  py={36}
                  w="full"
                >
                  {isMyProfile ? (
                    <Box maxW="xs" textAlign="center">
                      <Text fontWeight="medium" color={primaryFontColor} mb={2}>
                        Start a new challenge
                      </Text>
                      <Text color={secondaryFontColor} mb={4}>
                        Show off your skills. Learn everything you need to build on Ethereum!
                      </Text>
                      <Button as={RouteLink} colorScheme="blue" to="/">
                        Start a challenge
                      </Button>
                    </Box>
                  ) : (
                    <Box maxW="xs" textAlign="center">
                      <Text color={secondaryFontColor} mb={4}>
                        This builder hasn't completed any challenges.
                      </Text>
                    </Box>
                  )}
                </Flex>
              ))}
          </GridItem>
        )}
      </SimpleGrid>
    </Container>
  );
}
Example #15
Source File: DetailsTab.js    From web-client with Apache License 2.0 4 votes vote down vote up
ProjectDetailsTab = ({ project }) => {
    const isTemplate = project.is_template === 1;

    return <section className="grid grid-two">
        <div>
            <h4>
                <IconDocument /> Project details
            </h4>
            <dl>
                {!isTemplate && <>
                    <dt>Visibility</dt>
                    <dd><VisibilityLegend visibility={project.visibility} /></dd>


                    <dt>Status</dt>
                    <dd>{project.archived ? 'Archived' : 'Active'}</dd>
                </>}

                {project.category_id && <>
                    <dt>Category</dt>
                    <dd>{project.category_name}</dd>
                </>}

                {project.vulnerability_metrics && <>
                    <dt>Vulnerability metrics</dt>
                    <dd>{project.vulnerability_metrics}</dd>
                </>}

                <dt>Description</dt>
                <dd>{project.description ? <ReactMarkdown>{project.description}</ReactMarkdown> : <EmptyField />}</dd>

                {!isTemplate && <>
                    <dt>External ID</dt>
                    <dd>{project.external_id}</dd>
                </>}
            </dl>

            <h4>
                Vulnerabilities summary
            </h4>
            <dl>
                <dt>Management Summary</dt>
                <dd>{project.management_summary ? <ReactMarkdown>{project.management_summary}</ReactMarkdown> : <EmptyField />}</dd>

                <dt>Conclusion</dt>
                <dd>{project.management_conclusion ? <ReactMarkdown>{project.management_conclusion}</ReactMarkdown> : <EmptyField />}</dd>
            </dl>

            <h4 style={{ marginTop: 20 }}>
                <IconChartBar /> Stats
            </h4>

            <SimpleGrid gap="3" columns={{ base: "1", md: "2", xl: "3" }}>
                <VulnerabilitiesByRiskStatsWidget projectId={project.id} />
                <VulnerabilitiesByCategoryStatsWidget projectId={project.id} />
            </SimpleGrid>
        </div>

        <div>
            <h4>Relations</h4>
            <dl>
                {!isTemplate && <>
                    <dt>Client</dt>
                    <dd><ClientLink clientId={project.client_id}>{project.client_name}</ClientLink></dd>
                </>}

                <dt>Created by</dt>
                <dd><UserLink userId={project.creator_uid}>{project.creator_full_name}</UserLink></dd>
            </dl>

            <TimestampsSection entity={project} />

            {(project.engagement_start_date || project.engagement_end_date) &&
                <dl>
                    {project.engagement_start_date && <>
                        <dt>Engagement start date</dt>
                        <dd><RelativeDateFormatter date={project.engagement_start_date} /></dd>
                    </>}

                    {project.engagement_end_date && <>
                        <dt>Engagement end date</dt>
                        <dd><RelativeDateFormatter date={project.engagement_end_date} /></dd>
                    </>}
                </dl>
            }

            {project.archived === 1 &&
                <dl>
                    <dt>Archived</dt>
                    <dd><RelativeDateFormatter date={project.archive_ts} /></dd>
                </dl>
            }

        </div>
    </section>
}