@chakra-ui/react#Table TypeScript Examples

The following examples show how to use @chakra-ui/react#Table. 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: DevicesTable.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
DevicesTable = ({ devices }: { devices: Array<DeviceItem> }): JSX.Element => {
    return (
        <Table variant="striped" colorScheme="blue" size='sm'>
            <TableCaption>Devices registered for notifications over Google Play Services</TableCaption>
            <Thead>
                <Tr>
                    <Th>Name</Th>
                    <Th>ID</Th>
                    <Th isNumeric>Last Active</Th>
                </Tr>
            </Thead>
            <Tbody>
                {devices.map(item => (
                    <Tr key={item.name}>
                        <Td wordBreak='break-all'>{item.name}</Td>
                        <Td wordBreak='break-all'>{`${item.id.substring(0, 100)}...`}</Td>
                        <Td isNumeric>{new Date(item.lastActive).toLocaleString()}</Td>
                    </Tr>
                ))}
            </Tbody>
        </Table>
    );
}
Example #2
Source File: LogsTable.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
LogsTable = ({ logs }: { logs: Array<LogItem> }): JSX.Element => {
    return (
        <Table variant="striped" colorScheme="blue" size='sm'>
            <TableCaption>Logs will stream in as they come in</TableCaption>
            <Thead>
                <Tr>
                    <Th>Log</Th>
                    <Th isNumeric>Timestamp</Th>
                </Tr>
            </Thead>
            <Tbody>
                {logs.map(item => (
                    <Tr key={item.id}>
                        <Td wordBreak="break-word">{item.message}</Td>
                        <Td isNumeric>{item.timestamp.toLocaleString()}</Td>
                    </Tr>
                ))}
            </Tbody>
        </Table>
    );
}
Example #3
Source File: NotificationsTable.tsx    From bluebubbles-server with Apache License 2.0 6 votes vote down vote up
NotificationsTable = ({ notifications }: { notifications: Array<NotificationItem> }): JSX.Element => {
    return (
        <Table variant="striped" colorScheme="blue">
            <TableCaption>
                Alerts are normal to have. As long as the server recovers,
                you have nothing to worry about. Alerts are mostly helpful when you
                are experiencing an issue and want to see if any errors have occured.
            </TableCaption>
            <Thead>
                <Tr>
                    <Th>Type</Th>
                    <Th>Notification</Th>
                    <Th isNumeric>Time / Read</Th>
                </Tr>
            </Thead>
            <Tbody>
                {notifications.map(item => (
                    <Tr key={item.id} color={(item?.read ?? false) ? 'gray.400' : 'current'}>
                        <Td>
                            <Icon
                                ml={2}
                                fontSize="24"
                                as={AlertTypeIcon[item.type] ?? AiOutlineWarning}
                            />
                        </Td>
                        <Td>{item.message}</Td>
                        <Td isNumeric>
                            <Flex flexDirection="row" justifyContent='flex-end' alignItems='center'>
                                <Text mr={1}>{item.timestamp.toLocaleString()}</Text>
                                {(item?.read ?? false) ? <BsCheckAll fontSize={24} /> : null}
                            </Flex>
                            
                        </Td>
                    </Tr>
                ))}
            </Tbody>
        </Table>
    );
}
Example #4
Source File: OraclesTable.tsx    From rari-dApp with GNU Affero General Public License v3.0 6 votes vote down vote up
OraclesTable = ({
  data,
  oraclesMap,
}: {
  data: any;
  oraclesMap: {
    [oracleAddr: string]: string[];
  };
}) => {
  return (
    <Table variant="unstyled">
      <Thead>
        <Tr>
          <Th color="white">Oracle:</Th>
          <Th color="white">Assets</Th>
        </Tr>
      </Thead>
      <Tbody>
        {!!data.defaultOracle && (
          <OracleRow
            oracle={data.defaultOracle}
            underlyings={[]}
            isDefault={true}
          />
        )}
        {Object.keys(oraclesMap).map((oracle) => {
          const underlyings = oraclesMap[oracle];
          return <OracleRow oracle={oracle} underlyings={underlyings} />;
        })}
      </Tbody>
    </Table>
  );
}
Example #5
Source File: Lobby.tsx    From dope-monorepo with GNU General Public License v3.0 6 votes vote down vote up
Lobby = () => {
  return (
    <Layout>
      <Stack>
        <Flex
          align="center"
          border="2px"
          borderRadius="md"
          height={160}
          justify="center"
        >
          RYO
        </Flex>
        <Container>
          <ContainerHeader>
            Lobby
          </ContainerHeader>
          <Table size="sm" color="white">
            <Thead>
              <Tr>
                <Th>Starts</Th>
                <Th isNumeric>Players</Th>
              </Tr>
            </Thead>
            <Tbody>
              {[0, 1, 2].map((match) => (
                <MatchRow key={match} />
              ))}
            </Tbody>
          </Table>
        </Container>
      </Stack>
    </Layout>
  )
}
Example #6
Source File: ControlAttendeesPage.tsx    From takeout-app with MIT License 5 votes vote down vote up
ControlTrackCardsPage: React.FC = () => {
  const [query, setQuery] = React.useState<string | null>(null);
  const [errorAlert, setErrorAlert] = React.useState<JSX.Element | null>(null);
  const { register, handleSubmit } = useForm<{
    query: string;
  }>({ defaultValues: { query: "" } });
  const { data: list, isValidating } = ControlApi.useAttendeeList(query);

  const onSubmit = handleSubmit(async (data) => {
    setQuery(data.query);
  });

  // TODO: link to registration page and support email
  return (
    <>
      {errorAlert}
      <Container mt="20px" maxW={["auto", "auto", "auto", "1400px"]}>
        <Box>
          <form onSubmit={onSubmit}>
            <Input {...register("query")} placeholder="Name, reference code (or submit empty to list all attendees)" />
            <Button mt={4} size="lg" type="submit" isLoading={isValidating}>
              Search
            </Button>
          </form>
        </Box>

        {list ? (
          <Box>
            <Table>
              <Thead>
                <Tr>
                  <Th>Name</Th>
                  <Th>Reference</Th>
                  <Th>Flag</Th>
                </Tr>
              </Thead>
              <Tbody>
                {list.items.map((item) => (
                  <Tr key={item.ticket.id}>
                    <Td>
                      <Link as={RouterLink} to={`/control/attendees/${item.ticket.id}`}>
                        {item.attendee.is_ready ? (
                          <span>{item.attendee.name}</span>
                        ) : (
                          <i>
                            {item.ticket.first_name} {item.ticket.last_name}
                          </i>
                        )}
                      </Link>
                    </Td>
                    <Td>
                      <Link href={item.ticket.admin_url} isExternal>
                        {item.ticket.reference}
                      </Link>
                    </Td>
                    <Td>
                      {[
                        item.attendee.is_speaker ? "Speaker" : null,
                        item.attendee.is_committer ? "Committer" : null,
                        item.attendee.is_staff ? "Staff" : null,
                      ]
                        .filter((v): v is string => !!v)
                        .join(", ")}
                    </Td>
                  </Tr>
                ))}
              </Tbody>
            </Table>
          </Box>
        ) : null}
      </Container>
    </>
  );
}
Example #7
Source File: mdxComponents.tsx    From lucide with ISC License 5 votes vote down vote up
components = {
  h1: (props) => (
    <HeadingAnchored as="h1"  size="xl" mb={4} {...props}/>
  ),
  h2: ({children, ...rest}) => (
    <HeadingAnchored as="h2" size="lg" py={4} { ...rest}>
      {children}
      <Divider mt={4}/>
    </HeadingAnchored>
  ),
  h3: (props) => (
    <HeadingAnchored as="h3" size="md" pt={4} mb={4} {...props}/>
  ),
  h4: (props) => (
    <HeadingAnchored as="h4" size="sm" pt={4} mb={4} {...props}/>
  ),
  h5: (props) => (
    <HeadingAnchored as="h5" size="xs" pt={2} mb={1} {...props}/>
  ),
  h6: (props) => (
    <HeadingAnchored as="h6" size="xs" pt={2} mb={1} opacity={.75} {...props}/>
  ),
  ul: (props) => <UnorderedList my={2}>{props.children}</UnorderedList>,
  ol: (props) => <OrderedList my={2}>{props.children}</OrderedList>,
  li: (props) => <ListItem my={1}>{props.children}</ListItem>,
  p: (props) => <Text my={4}>{props.children}</Text>,
  img: ({ children, ...rest }) => <Image {...rest} borderRadius={4} my={2}>{children}</Image>,
  code: ({ className, children: code }) => {
    const language = className.replace('language-', '');

    return (
      <CodeBlock
        //@ts-ignore
        my={6}
        code={code}
        language={language}
      />
    )
  },
  table: (props) => <Table {...props} rounded={4} mb={4}/>,
  thead: Thead,
  tbody: Tbody,
  tr: Tr,
  th: Th,
  td: Td,
  blockquote: (props) => (
    <Alert
      mt="4"
      role="none"
      status="warning"
      variant="left-accent"
      as="blockquote"
      rounded={4}
      my="1.5rem"
      {...props}
    />
  ),
  inlineCode: InlineCode,
  hr: (props) => <Divider my={4}/>,
  a: ({children, href, ...rest}) => {
    let link = href
    const isExternal = link.startsWith('http')

    if(link.startsWith('packages/')) {
      link = href.replace('packages/', '')
    }

    link = link.replace('.md', '')

    return (
      <NextLink
        href={isExternal ? href : `/docs/${link}`}
        {...rest}
        passHref
      >
        <Link isExternal={isExternal} color='#F56565'>{children}</Link>
      </NextLink>
    )
  }
}
Example #8
Source File: MatchDetails.tsx    From dope-monorepo with GNU General Public License v3.0 5 votes vote down vote up
MatchDetails = () => {
  const { account } = useStarknet();
  const registry = useUserRegistryContract();

  const isRegistered = useMemo(
    () => registry?.call('get_user_info', { user_id: account! }),
    [account, registry],
  );

  const { invoke, data, loading, error } = useStarknetInvoke({
    contract: registry,
    method: 'register_user',
  });

  return (
    <Layout>
      <Container>
        <ContainerHeader>
          Lobby
        </ContainerHeader>
        <Table size="sm" color="white">
          <Tbody>
            {[0, 1, 2].map((match, index) => (
              <PlayerRow key={match} rowIndex={index} />
            ))}
          </Tbody>
        </Table>
      </Container>
      {isRegistered ? (
        <NavLink href="/roll-your-own/1/location/brooklyn" passHref>
          <Button variant="primary">Play</Button>
        </NavLink>
      ) : (
        <Button
          variant="primary"
          onClick={() =>
            invoke({
              args: { user_id: account, data: '84622096520155505419920978765481155' },
            })
          }
        >
          Join
        </Button>
      )}
    </Layout>
  )
}
Example #9
Source File: Drugs.tsx    From dope-monorepo with GNU General Public License v3.0 5 votes vote down vote up
Drugs = () => {
  const { data } = useDrugsQuery();

  const drugs = useMemo(() => {
    if (!data?.items.edges) return [];

    return data.items.edges.reduce((result, edge) => {
      if (!edge || !edge.node) return result;

      const { node } = edge;

      return [
        ...result,
        {
          id: node.id,
          name: node?.name,
          cost: 10,
          quantity: 1,
          rle: node?.rles ? node?.rles?.male : node?.base?.rles?.male,
        },
      ];
    }, [] as Drug[]);
  }, [data]);

  return (
    <Box px={3}>
      <Table size="sm" color="white">
        <Thead>
          <Tr>
            <Th></Th>
            <Th>Product</Th>
            <Th isNumeric>Cost</Th>
            <Th isNumeric>Quantity</Th>
          </Tr>
        </Thead>
        <Tbody>
          {drugs.map(drug => (
            <DrugRow key={drug.name} drug={drug} />
          ))}
        </Tbody>
      </Table>
    </Box>
  );
}
Example #10
Source File: GearCard.tsx    From dope-monorepo with GNU General Public License v3.0 5 votes vote down vote up
GearCard = ({
  item,
  balance,
  showEquipFooter = false,
  showUnEquipFooter = false,
  hustlerId,
}: {
  item: GearItem;
  balance?: number;
  showEquipFooter?: boolean;
  showUnEquipFooter?: boolean;
  hustlerId?: BigNumberish;
}) => {
  return (
    <ProfileCard>
      <ProfileCardHeader>
        <div>{item.name}</div>
        {balance && balance > 1 && (
          <div
            css={css`
              padding-right: 16px;
              color: var(--new-year-red);
            `}
            title="You have this many in inventory"
          >
            <ItemCount count={balance} />
          </div>
        )}
      </ProfileCardHeader>
      <PanelBody>
        <Stack>
          <Image
            borderRadius="md"
            src={getImageSrc(item)}
            alt={item.name}
            css={css`
              ${getImageSrc(item).includes('/icon') ? 'opacity:0.1' : ''}
            `}
          />
          <Table variant="small">
            <Tr>
              <Td>Type:</Td>
              <Td>{item.type}</Td>
            </Tr>
            <Tr>
              <Td>Origin:</Td>
              <Td>{getOrigin(item.suffix)}</Td>
            </Tr>
            <Tr>
              <Td>Title: </Td>
              <Td>{item.fullname}</Td>
            </Tr>
          </Table>
        </Stack>
      </PanelBody>
      {showEquipFooter && <GearEquipFooter id={item.id} />}
      {showUnEquipFooter && hustlerId && 
        <GearUnEquipFooter 
          id={item.id} 
          type={item.type}
          hustlerId={hustlerId} 
        />}
    </ProfileCard>
  );
}
Example #11
Source File: StatsEarnSection.tsx    From rari-dApp with GNU Affero General Public License v3.0 5 votes vote down vote up
Earn = () => {
  const { totals, aggregatePoolsInfo } = useAggregatePoolInfos();

  const { t } = useTranslation();

  const hasDeposits = useMemo(() => totals.balance > 0, [totals.balance]);

  return (
    <motion.div
      key="earn"
      style={{ width: "100%" }}
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    >
      <Table variant="simple">
        <Thead color="white">
          <Tr>
            <Th color="white">{t("Pool")}</Th>
            <Th color="white" textAlign="right">
              {t("APY")}
            </Th>
            <Th color="white" textAlign="right">
              {t("Deposits")}
            </Th>
            <Th color="white" textAlign="right">
              {t("Interest")}
            </Th>
            <Th color="white" textAlign="right">
              {t("Growth")}
            </Th>
          </Tr>
        </Thead>
        <Tbody>
          {aggregatePoolsInfo?.map((aggPoolInfo) => {
            if (aggPoolInfo?.poolBalance && !aggPoolInfo.poolBalance.isZero()) {
              return (
                <Tr key={aggPoolInfo.poolInfo.title}>
                  <Td>{aggPoolInfo.poolInfo.title}</Td>
                  <Td textAlign="right">
                    {aggPoolInfo.poolAPY ?? <Spinner />}%
                  </Td>
                  <Td textAlign="right">
                    {aggPoolInfo.formattedPoolBalance ?? <Spinner />}
                  </Td>
                  <Td textAlign="right">
                    {aggPoolInfo.formattedPoolInterestEarned ?? <Spinner />}
                  </Td>
                  <Td textAlign="right">
                    {aggPoolInfo.formattedPoolGrowth ?? <Spinner />}%
                  </Td>
                </Tr>
              );
            } else return null;
          })}
          {/* Todo (sharad) - implement totals for apy and growth */}
          <Tr fontWeight={hasDeposits ? "bold" : "normal"}>
            <Td>
              <Text>{t("Total")}</Text>
            </Td>
            <Td textAlign="right"></Td>
            <Td textAlign="right">
              <Text>{smallUsdFormatter(totals?.balance)}</Text>
            </Td>
            <Td textAlign="right">
              <Text>{totals?.interestEarned}</Text>
            </Td>
            <Td textAlign="right"></Td>
          </Tr>
        </Tbody>
      </Table>
    </motion.div>
  );
}
Example #12
Source File: WebhooksTable.tsx    From bluebubbles-server with Apache License 2.0 5 votes vote down vote up
WebhooksTable = ({ webhooks }: { webhooks: Array<WebhookItem> }): JSX.Element => {
    const dispatch = useAppDispatch();
    const dialogRef = useRef(null);
    const [selectedId, setSelectedId] = useState(undefined as number | undefined);
    return (
        <Box>
            <Table variant="striped" colorScheme="blue">
                <TableCaption>These are callbacks to receive events from the BlueBubbles Server</TableCaption>
                <Thead>
                    <Tr>
                        <Th>URL</Th>
                        <Th>Event Subscriptions</Th>
                        <Th isNumeric>Actions</Th>
                    </Tr>
                </Thead>
                <Tbody>
                    {webhooks.map(item => (
                        <Tr key={item.id}>
                            <Td>{item.url}</Td>
                            <Td>{JSON.parse(item.events).map((e: string) => webhookEventValueToLabel(e)).join(', ')}</Td>
                            <Td isNumeric>
                                <Grid templateColumns="repeat(2, 1fr)">
                                    <Tooltip label='Edit' placement='bottom'>
                                        <GridItem _hover={{ cursor: 'pointer' }} onClick={() => setSelectedId(item.id)}>
                                            <Icon as={AiOutlineEdit} />
                                        </GridItem>
                                    </Tooltip>
                                    
                                    <Tooltip label='Delete' placement='bottom'>
                                        <GridItem _hover={{ cursor: 'pointer' }} onClick={() => dispatch(remove(item.id))}>
                                            <Icon as={FiTrash} />
                                        </GridItem>
                                    </Tooltip>
                                </Grid>
                            </Td>
                        </Tr>
                    ))}
                </Tbody>
            </Table>

            <AddWebhookDialog
                existingId={selectedId}
                modalRef={dialogRef}
                isOpen={!!selectedId}
                onClose={() => {
                    setSelectedId(undefined);
                }}
            />
        </Box>
    );
}
Example #13
Source File: StatsTranchesSection.tsx    From rari-dApp with GNU Affero General Public License v3.0 4 votes vote down vote up
Earn = () => {
  const { t } = useTranslation();

  const mySaffronData: SaffronTranchePool[] = useMySaffronData();
  const daiSPrincipal = usePrincipal(TranchePool.DAI, TrancheRating.S);
  const daiAPrincipal = usePrincipal(TranchePool.DAI, TrancheRating.A);
  const estimatedSFI = useEstimatedSFI();
  const totalPrincipalFormatted = usePrincipalBalance();
  const totalPrincipal: number = totalPrincipalFormatted
    ? parseFloat(totalPrincipalFormatted?.replace(",", "").replace("$", ""))
    : 0;
  const hasDeposits = useMemo(() => totalPrincipal > 0, [totalPrincipal]);

  return (
    <motion.div
      key="pool2"
      style={{ width: "100%" }}
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    >
      <Table variant="simple">
        <Thead color="white">
          <Tr>
            <Th color="white">{t("Pool")}</Th>
            <Th color="white" textAlign="right">
              {t("APY")}
            </Th>
            <Th color="white" textAlign="right">
              {t("Deposits")}
            </Th>
            <Th color="white" textAlign="right">
              {t("Est. SFI Earnings")}
            </Th>
            <Th color="white" textAlign="right">
              {t("Growth")}
            </Th>
          </Tr>
        </Thead>
        <Tbody>
          <>
            {/* DAI S Pool */}
            {hasDeposits && (
              <>
                <Tr>
                  <Td textAlign="right">
                    <Row
                      mainAxisAlignment="flex-start"
                      crossAxisAlignment="center"
                    >
                      <Box>
                        <Text textAlign="right"> {t("DAI-S")} </Text>
                      </Box>
                    </Row>
                  </Td>
                  <Td textAlign="right">
                    <Text>
                      {
                        mySaffronData?.[0]?.tranches?.[TrancheRating.S]?.[
                          "total-apy"
                        ]
                      }
                      %
                    </Text>
                  </Td>
                  <Td textAlign="right">
                    <Text>
                      {daiSPrincipal} {t("DAI")}
                    </Text>
                  </Td>
                  <Td textAlign="right">
                    <Text>{estimatedSFI?.formattedSPoolSFIEarned}</Text>
                  </Td>
                  <Td textAlign="right">
                    <Text textAlign="right">{t("N/A")}</Text>
                  </Td>
                </Tr>
                <Tr>
                  <Td>
                    <Row
                      mainAxisAlignment="flex-start"
                      crossAxisAlignment="center"
                    >
                      <Box>
                        <Text textAlign="right"> {t("DAI-A")} </Text>
                      </Box>
                    </Row>
                  </Td>
                  <Td>
                    <Text textAlign="right">
                      {
                        mySaffronData?.[0]?.tranches?.[TrancheRating.A]?.[
                          "total-apy"
                        ]
                      }
                      %
                    </Text>
                  </Td>
                  <Td>
                    <Text textAlign="right">
                      {daiAPrincipal} {t("DAI")}
                    </Text>
                  </Td>
                  <Td>
                    {" "}
                    <Text textAlign="right">
                      {estimatedSFI?.formattedAPoolSFIEarned}
                    </Text>{" "}
                  </Td>
                  <Td>
                    <Text textAlign="right">{t("N/A")}</Text>
                  </Td>
                </Tr>
              </>
            )}
            {/* Totals */}
            <Tr fontWeight={hasDeposits ? "bold" : "normal"}>
              <Td>
                <Text>{t("Total")}</Text>
              </Td>
              <Td>
                <Text textAlign="right"></Text>
              </Td>
              <Td>
                <Text textAlign="right">
                  {smallUsdFormatter(totalPrincipal) ?? 0}
                </Text>
              </Td>
              <Td>
                <Text textAlign="right">
                  {estimatedSFI?.formattedTotalSFIEarned ?? "0 SFI"}
                </Text>
              </Td>
              <Td>
                <Text textAlign="right" />
              </Td>
            </Tr>
          </>
        </Tbody>
      </Table>
    </motion.div>
  );
}
Example #14
Source File: StatsTotalSection.tsx    From rari-dApp with GNU Affero General Public License v3.0 4 votes vote down vote up
StatsTotalSection = ({
  setNetDeposits,
  setNetDebt,
}: {
  setNetDeposits: (input: number) => void;
  setNetDebt: (input: number) => void;
}) => {
  const { t } = useTranslation();

  // Earn
  const { totals, aggregatePoolsInfo }: AggregatePoolsInfoReturn =
    useAggregatePoolInfos();
  const hasDepositsInEarn = aggregatePoolsInfo?.some(
    (p) => !p?.poolBalance?.isZero()
  );

  // Fuse
  const { filteredPools: filteredFusePools } = useFusePools("my-pools");
  const poolIds: number[] = filteredFusePools?.map(({ id }) => id) ?? [];
  const fusePoolsData: FusePoolData[] | null = useFusePoolsData(poolIds);

  // Pool2
  const apr = usePool2APR();
  const earned = usePool2UnclaimedRGT();
  const balance = usePool2Balance();
  const hasDepositsInPool2 = !!balance?.SLP;

  // Tranches
  const daiSPrincipal: string | undefined = usePrincipal(
    TranchePool.DAI,
    TrancheRating.S
  );
  const daiAPrincipal: string | undefined = usePrincipal(
    TranchePool.DAI,
    TrancheRating.A
  );
  const totalPrincipal: string | undefined = usePrincipalBalance();
  const parsedTotalPrincipal: number = totalPrincipal
    ? parseFloat(totalPrincipal?.replace(",", "").replace("$", ""))
    : 0;
  const estimatedSFI: UseEstimatedSFIReturn | undefined = useEstimatedSFI();
  const hasDepositsInTranches = useMemo(
    () => parsedTotalPrincipal > 0 ?? false,
    [parsedTotalPrincipal]
  );

  // Total Deposits
  const totalDepositsUSD = useMemo(() => {
    const { totalSupplyBalanceUSD: fuseTotal }: FusePoolData =
      fusePoolsData?.reduce((a, b) => {
        return {
          totalSupplyBalanceUSD:
            a.totalSupplyBalanceUSD + b.totalSupplyBalanceUSD,
        } as FusePoolData;
      }) ?? ({ totalSupplyBalanceUSD: 0 } as FusePoolData);

    const vaultTotal = totals?.balance ?? 0;

    const pool2Total = balance?.balanceUSD ?? 0;

    const tranchesTotal = parsedTotalPrincipal ?? 0;

    const total = fuseTotal + vaultTotal + pool2Total + tranchesTotal;

    return total;
  }, [totals, fusePoolsData, balance, parsedTotalPrincipal]);

  // Total debt - todo: refactor into the `useFusePoolsData` hook
  const totalDebtUSD = useMemo(() => {
    const { totalBorrowBalanceUSD }: FusePoolData =
      fusePoolsData?.reduce((a, b) => {
        return {
          totalBorrowBalanceUSD:
            a.totalBorrowBalanceUSD + b.totalBorrowBalanceUSD,
        } as FusePoolData;
      }) ?? ({ totalBorrowBalanceUSD: 0 } as FusePoolData);
    return totalBorrowBalanceUSD;
  }, [fusePoolsData]);

  useEffect(() => {
    if (totalDepositsUSD && !Number.isNaN(totalDepositsUSD))
      setNetDeposits(totalDepositsUSD);
    if (totalDebtUSD && !Number.isNaN(totalDebtUSD)) setNetDebt(totalDebtUSD);
  }, [totalDepositsUSD, totalDebtUSD, setNetDeposits, setNetDebt]);

  const earnedHeaderText = hasDepositsInTranches
    ? "RGT + SFI Earned"
    : "RGT Earned";

  return (
    <motion.div
      key="totals"
      style={{ width: "100%" }}
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    >
      <Table variant="simple">
        <Thead color="white">
          <Tr>
            <Th color="white">{t("Product")}</Th>
            <Th color="white" textAlign="right">
              {t("Pool")}
            </Th>
            <Th color="white" textAlign="right">
              {t("Deposits")}
            </Th>
            <Th color="white" textAlign="right">
              {t(earnedHeaderText)}
            </Th>
            <Th color="white" textAlign="right">
              Interest Earned
            </Th>
          </Tr>
        </Thead>

        <Tbody>
          {/* Fuse section */}
          {fusePoolsData && (
            <FuseRow
              fusePoolsData={fusePoolsData}
              filteredPoolsData={filteredFusePools}
            />
          )}
          {/* earn section */}
          {hasDepositsInEarn && <EarnRow poolsInfo={aggregatePoolsInfo} />}
          {/* Pool2 Section */}
          {hasDepositsInPool2 && (
            <Pool2Row apr={apr} earned={earned} balance={balance} />
          )}
          {/* Tranches */}
          {hasDepositsInTranches && (
            <TranchesRow
              daiSPrincipal={daiSPrincipal}
              daiAPrincipal={daiAPrincipal}
              estimatedSFI={estimatedSFI}
            />
          )}
          {/* Todo (sharad) - implement totals for apy and growth */}
          <motion.tr
            initial={{ opacity: 0, y: -40 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: 40 }}
          >
            <Td fontWeight="bold">{t("Total")}</Td>
            <Td textAlign="right"></Td>
            <Td textAlign="right">
              <Text fontWeight="bold">
                {smallUsdFormatter(totalDepositsUSD)}
              </Text>
            </Td>
            <Td textAlign="right">
              <Text fontWeight="bold">
                {earned?.toFixed(2)} RGT
                {hasDepositsInTranches &&
                  ` + ${estimatedSFI?.formattedTotalSFIEarned}`}
              </Text>
            </Td>
            <Td textAlign="right">
              <Text fontWeight="bold">{totals?.interestEarned}</Text>
            </Td>
          </motion.tr>
        </Tbody>
      </Table>
    </motion.div>
  );
}
Example #15
Source File: DopeTable.tsx    From dope-monorepo with GNU General Public License v3.0 4 votes vote down vote up
DopeTable = ({ className = '', data, selected, onSelect }: DopeTableProps) => {
  const [sort, setSort] = useState('id');

  const amountOfUnclaimedPaper = (): number => {
    const paperPerToken = 125000;
    let numberUnclaimed = 0;
    for (const item of data) {
      numberUnclaimed = item.claimed ? numberUnclaimed : numberUnclaimed + 1;
    }
    return numberUnclaimed * paperPerToken;
  };
  const formattedUnclaimedPaper = (): string => {
    const formatter = Intl.NumberFormat('en', { notation: 'compact' });
    return formatter.format(amountOfUnclaimedPaper());
  };

  const items = useMemo(
    () =>
      data
        .map(({ id, opened, claimed, rank }, idx) => ({
          id,
          rank,
          opened: opened ? (
            ''
          ) : (
            <Check
              css={css`
                display: inline;
              `}
            />
          ),
          claimed: claimed ? (
            ''
          ) : (
            <Check
              css={css`
                display: inline;
              `}
            />
          ),
          idx,
        }))
        .sort((a, b) => {
          switch (sort) {
            case 'id':
              return a.id < b.id ? -1 : 1;
            case 'rank':
              return a.rank < b.rank ? -1 : 1;
            default:
              return a.id > b.id ? -1 : 1;
          }
        }),
    [data, sort],
  );

  return (
    <PanelContainer
      className={className}
      css={css`
        tfoot th {
          // Screen > Tablet display items side by side
          span.separator {
            display: block;
            height: 0;
            margin: 0;
            padding: 0;
            overflow: hidden;
            ${media.tablet`
              display: inline;
              font-size: var(--text-00);
              height: auto;
              padding: 8px;
              color: #a8a9ae;
            `}
          }
        }
      `}
    >
      <div
        css={css`
          display: flex;
          min-height: 100%;
          flex-direction: column;
          // justify-content: space-around;
          // align-items: stretch;
        `}
      >
        <Table variant="dope">
          <colgroup>
            <col width="25%" />
            <col width="25%" />
            <col width="25%" />
            <col width="25%" />
          </colgroup>
          <Thead>
            <Tr>
              <Th onClick={() => setSort('id')}>Dope ID</Th>
              <Th onClick={() => setSort('rank')}>Rank</Th>
              <Th>Has Paper</Th>
              <Th>Has Gear</Th>
            </Tr>
          </Thead>
          <Tbody>
            {items.map(({ id, rank, opened, claimed, idx }) => (
              <Tr
                className={selected === idx ? 'selected' : ''}
                key={id}
                onClick={() => onSelect(idx)}
              >
                <Td>{id}</Td>
                <Td>{rank}</Td>
                <Td>{claimed}</Td>
                <Td>{opened}</Td>
              </Tr>
            ))}
          </Tbody>
          <Tfoot>
            <Th colSpan={4}>
              {items.length} DOPE {items.length > 1 ? 'Tokens' : 'Token'}
              <span
                className="separator"
                css={css`
                  padding: 8px;
                  color: rgb(168, 169, 174);
                `}
              >
                /
              </span>
              {formattedUnclaimedPaper()} Unclaimed $PAPER
            </Th>
          </Tfoot>
        </Table>
      </div>
    </PanelContainer>
  );
}
Example #16
Source File: StatsPool2Section.tsx    From rari-dApp with GNU Affero General Public License v3.0 4 votes vote down vote up
Earn = () => {
  const { t } = useTranslation();

  const apr = usePool2APR();
  const earned = usePool2UnclaimedRGT();
  const balance = usePool2Balance();

  const balanceSLP = balance?.hasDeposited ? balance.SLP!.toFixed(4) : "0.0000";
  const balanceETH = balance?.hasDeposited ? balance.eth!.toFixed(2) : "0.0000";
  const balanceRGT = balance?.hasDeposited ? balance.rgt!.toFixed(2) : "0.0000";
  const balanceUSD = balance?.hasDeposited
    ? smallUsdFormatter(balance.balanceUSD)
    : "$0";

  const hasDeposits = useMemo(() => earned! > 0, [earned]);

  return (
    <motion.div
      key="pool2"
      style={{ width: "100%" }}
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    >
      <Table variant="simple">
        <Thead color="white">
          <Tr>
            <Th color="white">{t("Pool")}</Th>
            <Th color="white" textAlign="right">
              {t("APY")}
            </Th>
            <Th color="white" textAlign="right">
              {t("Deposits")}
            </Th>
            <Th color="white" textAlign="right">
              {t("RGT Earned")}
            </Th>
            <Th color="white" textAlign="right">
              {t("Growth")}
            </Th>
          </Tr>
        </Thead>
        <Tbody>
          <>
            {hasDeposits && (
              <Tr>
                <Td>
                  <Row
                    mainAxisAlignment="flex-start"
                    crossAxisAlignment="center"
                  >
                    <Box>
                      <Avatar
                        bg="#FFF"
                        boxSize="30px"
                        name={"RGT"}
                        src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xD291E7a03283640FDc51b121aC401383A46cC623/logo.png"
                      />
                      <Avatar
                        bg="#FFF"
                        boxSize="30px"
                        name={"ETH"}
                        src="https://icons.iconarchive.com/icons/cjdowner/cryptocurrency-flat/64/Ethereum-ETH-icon.png"
                      />
                    </Box>
                    <Box ml={3}>RGT-ETH</Box>
                  </Row>
                </Td>
                <Td textAlign="right">{apr}%</Td>
                <Td textAlign="right">
                  <Row
                    mainAxisAlignment="flex-start"
                    crossAxisAlignment="center"
                  >
                    <Box>{balanceSLP} RGT-ETH</Box>
                    <SimpleTooltip
                      label={`${balanceRGT} RGT - ${balanceETH} ETH  `}
                      placement="top-start"
                    >
                      <Box
                        ml={4}
                        my="auto"
                        _hover={{ color: "gray", cursor: "auto" }}
                      >
                        <QuestionOutlineIcon color="currentColor" />
                      </Box>
                    </SimpleTooltip>
                  </Row>
                </Td>
                <Td textAlign="right">{earned?.toFixed(2)} RGT</Td>
                <Td textAlign="right">0%</Td>
              </Tr>
            )}

            {/* Todo (sharad) - implement totals for apy and growth */}
            <Tr>
              <Td>
                <Text fontWeight={hasDeposits ? "bold" : "normal"}>Total</Text>
              </Td>

              <Td textAlign="right">
                <Text fontWeight={hasDeposits ? "bold" : "normal"}>
                  {parseFloat(balanceSLP) > 0 ? `${apr}%` : null}
                </Text>
              </Td>

              <Td textAlign="right">
                <Text fontWeight={hasDeposits ? "bold" : "normal"}>
                  {balanceUSD}
                </Text>
              </Td>

              <Td textAlign="right">
                <Text fontWeight={hasDeposits ? "bold" : "normal"}>
                  {earned?.toFixed(2)} RGT
                </Text>
              </Td>

              <Td textAlign="right"> </Td>
            </Tr>
          </>
        </Tbody>
      </Table>
    </motion.div>
  );
}
Example #17
Source File: Hustlers.tsx    From dope-monorepo with GNU General Public License v3.0 4 votes vote down vote up
Hustlers = ({ searchValue }: { searchValue: string }) => {
  const { account } = useWeb3React();

  const { data, hasNextPage, isFetching, fetchNextPage } = useInfiniteProfileHustlersQuery(
    {
      where: {
        hasWalletWith: [
          {
            id: account,
          },
        ],
        nameContains: searchValue,
      },
      first: 50,
    },
    {
      getNextPageParam: lastPage => {
        if (lastPage.hustlers.pageInfo.hasNextPage) {
          return {
            after: lastPage.hustlers.pageInfo.endCursor,
          };
        }
        return false;
      },
    },
  );

  const hustlerData: HustlerData = useMemo(() => {
    const defaultValue = { hustlers: [], totalCount: 0 };

    if (!data?.pages) return defaultValue;

    return data.pages.reduce((result, page) => {
      if (!page.hustlers.edges) return result;

      const { totalCount } = page.hustlers;

      return {
        totalCount,
        hustlers: [
          ...result.hustlers,
          ...page.hustlers.edges.reduce((result, edge) => {
            if (!edge?.node) return result;

            return [...result, edge.node];
          }, [] as ProfileHustler[]),
        ],
      };
    }, defaultValue as HustlerData);
  }, [data]);

  return (
    <>
      <SectionHeader>
        <HStack alignContent="center" justifyContent="space-between" width="100%" marginRight="8px">
          <span>Hustlers</span>
          <ItemCount count={hustlerData.totalCount} />
        </HStack>
      </SectionHeader>
      <SectionContent
        isFetching={isFetching && !hustlerData.hustlers.length}
        minH={isFetching ? 200 : 0}
      >
        {hustlerData.hustlers.length ? (
          <CardContainer>
            {hustlerData.hustlers.map(({ id, name, svg, title, type }) => {
              const formattedType = formatType(type);

              return (
                <ProfileCard key={id}>
                  <ProfileCardHeader>
                    {formattedType} #{id}
                  </ProfileCardHeader>
                  <PanelBody>
                    {svg && (
                      <AspectRatio ratio={1}>
                        <Link href={`/hustlers/${id}`} passHref>
                          <Image
                            alt={name || 'Hustler'}
                            borderRadius="md"
                            src={svg}
                            cursor="pointer"
                          />
                        </Link>
                      </AspectRatio>
                    )}
                    <Table variant="small">
                      <Tr>
                        <Td>Name:</Td>
                        <Td>{name?.trim().length !== 0 ? name : `Hustler #${id}`}</Td>
                      </Tr>
                      {title && (
                        <Tr>
                          <Td>Title:</Td>
                          <Td>{title}</Td>
                        </Tr>
                      )}
                      {/* For spacing if no OG title */}
                      {!title && (
                        <Tr>
                          <Td colSpan={2}>&nbsp;</Td>
                        </Tr>
                      )}
                    </Table>
                  </PanelBody>
                  <HustlerFooter id={id} />
                </ProfileCard>
              );
            })}
            {isFetching && hustlerData.hustlers.length && <LoadingBlock maxRows={1} />}
            {hasNextPage && <Button onClick={() => fetchNextPage()}>Load more</Button>}
          </CardContainer>
        ) : (
          <span>No Hustlers found</span>
        )}
      </SectionContent>
    </>
  );
}
Example #18
Source File: StatsFuseSection.tsx    From rari-dApp with GNU Affero General Public License v3.0 4 votes vote down vote up
Fuse = () => {
  // Todo - write useFusePoolsData
  const { filteredPools } = useFusePools("my-pools");

  const { t } = useTranslation();

  const poolIds: number[] = filteredPools?.map(({ id }) => id) ?? [];

  const fusePoolsData: FusePoolData[] | null = useFusePoolsData(poolIds);

  const assetsArray: USDPricedFuseAsset[][] | null =
    fusePoolsData?.map((pool) => pool?.assets) ?? null;
  const maxBorrows = useBorrowLimits(assetsArray);

  const { tokensDataMap }: { tokensDataMap: TokensDataHash } =
    useAssetsMapWithTokenData(assetsArray);

  const totalBorrowBalanceUSD =
    fusePoolsData?.reduce((a, b) => {
      return a + b.totalBorrowBalanceUSD;
    }, 0) ?? 0;

  const totalSupplyBalanceUSD =
    fusePoolsData?.reduce((a, b) => {
      return a + b.totalSupplyBalanceUSD;
    }, 0) ?? 0;

  const hasDeposits = useMemo(
    () => totalSupplyBalanceUSD > 0,
    [totalSupplyBalanceUSD]
  );

  return (
    <motion.div
      key="fuse"
      style={{ width: "100%" }}
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
    >
      <Table variant="simple">
        <Thead color="white">
          <Tr>
            <Th textAlign="center" color="white">
              {t("Pool")}
            </Th>
            <Th textAlign="right" color="white">
              {t("Borrow Limit")}
            </Th>
            <Th textAlign="right" color="white">
              {t("Deposits")}
            </Th>
            <Th textAlign="right" color="white">
              {t("Borrows")}
            </Th>
            <Th textAlign="right" color="white">
              {`${t("Lend APY")} / ${t("Borrow APY")}`}
            </Th>
          </Tr>
        </Thead>
        <Tbody>
          {filteredPools?.map((filteredPool, index) => {
            const fusePoolData = fusePoolsData?.[index];
            const maxBorrow = maxBorrows?.[index];

            const ratio =
              fusePoolData?.totalBorrowBalanceUSD && maxBorrow
                ? (fusePoolData.totalBorrowBalanceUSD / maxBorrow) * 100
                : 0;

            const isAtRiskOfLiquidation = ratio && ratio > 95;

            return (
              <Tr key={filteredPool.id}>
                <Td textAlign="center" fontSize="large">
                  {filteredPool.id}
                </Td>
                {/* Borrow limit */}
                <Td
                  textAlign="right"
                  textStyle="bold"
                  color={isAtRiskOfLiquidation ? "red" : "#FFF"}
                  fontSize="large"
                  fontWeight="bold"
                >
                  {!!ratio ? `${ratio.toFixed(1)}%` : "0%"}
                </Td>
                {/* Deposits By Asset */}
                {/* Lend Balance */}
                <Td textAlign="right">
                  {fusePoolData?.assets.map(
                    (asset: USDPricedFuseAsset) =>
                      asset.supplyBalanceUSD > 0 && (
                        <Box mt={2}>
                          <AssetContainer
                            asset={asset}
                            tokenData={tokensDataMap[asset.underlyingToken]}
                          />
                        </Box>
                      )
                  )}
                </Td>
                {/* Borrow Balance */}
                <Td textAlign="right">
                  {fusePoolData?.assets.map(
                    (asset: USDPricedFuseAsset) =>
                      asset.borrowBalanceUSD > 0 && (
                        <Box mt={2}>
                          <AssetContainer
                            asset={asset}
                            type={AssetContainerType.BORROW}
                            tokenData={tokensDataMap[asset.underlyingToken]}
                          />
                        </Box>
                      )
                  )}
                </Td>
                {/* Lend Borrow rates */}
                <Td textAlign="right">
                  {fusePoolData?.assets.map(
                    (asset: USDPricedFuseAsset) =>
                      (asset.supplyBalanceUSD > 0 ||
                        asset.borrowBalanceUSD > 0) && (
                        <Box mt={4}>
                          <AssetContainer
                            asset={asset}
                            type={AssetContainerType.RATES}
                            tokenData={tokensDataMap[asset.underlyingToken]}
                          />
                        </Box>
                      )
                  )}
                </Td>
              </Tr>
            );
          })}
          {/* Totals */}
          <Tr fontWeight={hasDeposits ? "bold" : "normal"}>
            <Td>
              <Text>Total</Text>
            </Td>

            <Td textAlign="right"></Td>

            <Td textAlign="right">
              <Text>{smallUsdFormatter(totalSupplyBalanceUSD)}</Text>
            </Td>

            <Td textAlign="right">
              <Text>-{smallUsdFormatter(totalBorrowBalanceUSD)}</Text>
            </Td>

            <Td textAlign="right"></Td>
          </Tr>
        </Tbody>
      </Table>
    </motion.div>
  );
}
Example #19
Source File: FusePoolEditPage.tsx    From rari-dApp with GNU Affero General Public License v3.0 4 votes vote down vote up
FusePoolEditPage = memo(() => {
  const { isAuthed } = useRari();

  const isMobile = useIsSemiSmallScreen();

  const {
    isOpen: isAddAssetModalOpen,
    onOpen: openAddAssetModal,
    onClose: closeAddAssetModal,
  } = useDisclosure();

  const {
    isOpen: isAddRewardsDistributorModalOpen,
    onOpen: openAddRewardsDistributorModal,
    onClose: closeAddRewardsDistributorModal,
  } = useDisclosure();

  const {
    isOpen: isEditRewardsDistributorModalOpen,
    onOpen: openEditRewardsDistributorModal,
    onClose: closeEditRewardsDistributorModal,
  } = useDisclosure();

  const authedOpenModal = useAuthedCallback(openAddAssetModal);

  const { t } = useTranslation();

  const { poolId } = useParams();

  const data = useFusePoolData(poolId);
  const isAdmin = useIsComptrollerAdmin(data?.comptroller);

  // RewardsDistributor stuff
  const poolIncentives = usePoolIncentives(data?.comptroller);
  const rewardsDistributors = useRewardsDistributorsForPool(data?.comptroller);
  const [rewardsDistributor, setRewardsDistributor] = useState<
    RewardsDistributor | undefined
  >();

  console.log({ rewardsDistributors, poolIncentives });

  const handleRewardsRowClick = useCallback(
    (rD: RewardsDistributor) => {
      setRewardsDistributor(rD);
      openEditRewardsDistributorModal();
    },
    [setRewardsDistributor, openEditRewardsDistributorModal]
  );

  return (
    <>
      {data ? (
        <AddAssetModal
          comptrollerAddress={data.comptroller}
          poolOracleAddress={data.oracle}
          oracleModel={data.oracleModel}
          existingAssets={data.assets}
          poolName={data.name}
          poolID={poolId!}
          isOpen={isAddAssetModalOpen}
          onClose={closeAddAssetModal}
        />
      ) : null}

      {data ? (
        <AddRewardsDistributorModal
          comptrollerAddress={data.comptroller}
          poolName={data.name}
          poolID={poolId!}
          isOpen={isAddRewardsDistributorModalOpen}
          onClose={closeAddRewardsDistributorModal}
        />
      ) : null}

      {data && !!rewardsDistributor ? (
        <EditRewardsDistributorModal
          rewardsDistributor={rewardsDistributor}
          pool={data}
          isOpen={isEditRewardsDistributorModalOpen}
          onClose={closeEditRewardsDistributorModal}
        />
      ) : null}

      <Column
        mainAxisAlignment="flex-start"
        crossAxisAlignment="center"
        color="#FFFFFF"
        mx="auto"
        width={isMobile ? "100%" : "1150px"}
        px={isMobile ? 4 : 0}
      >
        <Header isAuthed={isAuthed} isFuse />

        <FuseStatsBar data={data} />

        <FuseTabBar />

        {!!data && (
          <AdminAlert
            isAdmin={isAdmin}
            isAdminText="You are the admin of this Fuse Pool!"
            isNotAdminText="You are not the admin of this Fuse Pool!"
          />
        )}

        <RowOrColumn
          width="100%"
          mainAxisAlignment="flex-start"
          crossAxisAlignment="flex-start"
          isRow={!isMobile}
        >
          <DashboardBox
            width={isMobile ? "100%" : "50%"}
            height={isMobile ? "auto" : "560px"}
            mt={4}
          >
            {data ? (
              <PoolConfiguration
                assets={data.assets}
                comptrollerAddress={data.comptroller}
                oracleAddress={data.oracle}
              />
            ) : (
              <Center expand>
                <Spinner my={8} />
              </Center>
            )}
          </DashboardBox>

          <Box pl={isMobile ? 0 : 4} width={isMobile ? "100%" : "50%"}>
            <DashboardBox
              width="100%"
              mt={4}
              height={isMobile ? "auto" : "560px"}
            >
              {data ? (
                data.assets.length > 0 ? (
                  <AssetConfiguration
                    openAddAssetModal={authedOpenModal}
                    assets={data.assets}
                    poolOracleAddress={data.oracle}
                    oracleModel={data.oracleModel}
                    comptrollerAddress={data.comptroller}
                    poolID={poolId!}
                    poolName={data.name}
                  />
                ) : (
                  <Column
                    expand
                    mainAxisAlignment="center"
                    crossAxisAlignment="center"
                    py={4}
                  >
                    <Text mb={4}>{t("There are no assets in this pool.")}</Text>

                    <AddAssetButton
                      comptrollerAddress={data.comptroller}
                      openAddAssetModal={authedOpenModal}
                    />
                  </Column>
                )
              ) : (
                <Center expand>
                  <Spinner my={8} />
                </Center>
              )}
            </DashboardBox>
          </Box>
        </RowOrColumn>

        {/* Rewards Distributors */}
        <DashboardBox w="100%" h="100%" my={4}>
          <Row
            mainAxisAlignment="space-between"
            crossAxisAlignment="center"
            p={3}
          >
            <Heading size="md">Rewards Distributors </Heading>
            <AddRewardsDistributorButton
              openAddRewardsDistributorModal={openAddRewardsDistributorModal}
              comptrollerAddress={data?.comptroller}
            />
          </Row>

          {!!data && !rewardsDistributors.length && (
            <Column
              w="100%"
              h="100%"
              mainAxisAlignment="center"
              crossAxisAlignment="center"
              p={4}
            >
              <Text mb={4}>
                {t("There are no RewardsDistributors for this pool.")}
              </Text>
              <AddRewardsDistributorButton
                openAddRewardsDistributorModal={openAddRewardsDistributorModal}
                comptrollerAddress={data?.comptroller}
              />
            </Column>
          )}

          {!data && (
            <Column
              w="100%"
              h="100%"
              mainAxisAlignment="center"
              crossAxisAlignment="center"
              p={4}
            >
              <Spinner />
            </Column>
          )}

          {!!data && !!rewardsDistributors.length && (
            <Table>
              <Thead>
                <Tr>
                  <Th color="white" size="sm">
                    {t("Reward Token:")}
                  </Th>
                  <Th color="white">{t("Active CTokens:")}</Th>
                  <Th color="white">{t("Balance:")}</Th>
                  <Th color="white">{t("Admin?")}</Th>
                </Tr>
              </Thead>

              <Tbody minHeight="50px">
                {!data && !rewardsDistributors.length ? (
                  <Spinner />
                ) : (
                  rewardsDistributors.map((rD, i) => {
                    return (
                      <RewardsDistributorRow
                        key={rD.address}
                        rewardsDistributor={rD}
                        handleRowClick={handleRewardsRowClick}
                        hideModalDivider={i === rewardsDistributors.length - 1}
                        activeCTokens={
                          poolIncentives.rewardsDistributorCtokens[rD.address]
                        }
                      />
                    );
                  })
                )}
              </Tbody>
            </Table>
          )}

          <ModalDivider />
        </DashboardBox>
      </Column>
    </>
  );
})
Example #20
Source File: quick-buy.tsx    From dope-monorepo with GNU General Public License v3.0 4 votes vote down vote up
QuickBuyHustler = () => {
  const { data: unclaimedDope, status: searchStatus } = useSearchDopeQuery({
    query: '',
    first: 50,
    orderBy: {
      field: SearchOrderField.SalePrice,
      direction: OrderDirection.Asc,
    },
    where: {
      type: SearchType.Dope,
      opened: false,
      saleActive: true,
    },
  });
  const isLoading = searchStatus === 'loading';
  const [bgColor, setBgColor] = useState(getRandomBgColor());
  const [currentDopeIndex, setCurrentDopeIndex] = useState(0);
  const [showHustler, setShowHustler] = useState(true);
  const [paperPrice, setPaperPrice] = useState<BigNumber>();
  
  const oneclick = useOneClickInitiator();

  useEffect(() => {
    setBgColor(getRandomBgColor());
  }, []);

  const unclaimedDopeArr = useMemo(() => {
    if (unclaimedDope?.search?.edges) {
      // Have to filter array for things that have "node" property
      // which are the DOPE objects we want
      const dopes = unclaimedDope.search.edges?.reduce((prev, dope) => {
        if (dope && dope.node && dope.node.__typename === 'Dope') {
          return [...prev, dope.node];
        }
        return prev;
      }, [] as any);

      setCurrentDopeIndex(getRandomNumber(0, dopes.length - 1));
      return dopes;
    }
    return [];
  }, [setCurrentDopeIndex, unclaimedDope]);

  const currentDope = useMemo(
    () => unclaimedDopeArr && unclaimedDopeArr[currentDopeIndex],
    [unclaimedDopeArr, currentDopeIndex],
  );

  const decrementIndex = () => {
    let index = currentDopeIndex;
    index--;
    if (0 > index) index = 0;
    setCurrentDopeIndex(index);
  };

  const incrementIndex = () => {
    let index = currentDopeIndex;
    const maxIndex = unclaimedDopeArr.length - 1;
    index++;
    if (index > maxIndex) index = maxIndex;
    setCurrentDopeIndex(index);
  };

  useEffect(() => {
    if (oneclick) {
      oneclick.callStatic.estimate(ethers.utils.parseUnits('22500', 18)).then(setPaperPrice);
    }
  }, [oneclick]);

  const currentPrice = useMemo(() => {
    const activeListings = currentDope?.listings?.filter((l: any) => l?.active);
    const listingPrice = activeListings?.[0]?.inputs?.[0]?.amount;
    return `${(+ethers.utils.formatEther(
      listingPrice && paperPrice ? BigNumber.from(listingPrice).add(paperPrice) : 0,
    )).toFixed(4)}`;
  }, [currentDope, paperPrice]);

  const CarouselButtons = () => (
    <Box display="flex" justifyContent="space-between" gap="8px" width="100%">
      <Button flex="1" onClick={decrementIndex} disabled={currentDopeIndex <= 0}>
        <Image src="/images/icon/arrow-back.svg" alt="Previous" width="16px" marginRight="8px;" />
        Prev
      </Button>
      <Button flex="2" onClick={() => setShowHustler(!showHustler)}>
        {showHustler ? 'Show Gear' : 'Show Hustler'}
      </Button>
      <Button
        flex="1"
        onClick={incrementIndex}
        disabled={currentDopeIndex >= unclaimedDopeArr.length - 1}
      >
        Next
        <Image src="/images/icon/arrow-forward.svg" alt="Next" width="16px" marginLeft="8px;" />
      </Button>
    </Box>
  );

  const QuickBuyFooter = () => (
    <Box display="flex" flexDirection="column" justifyContent="flex-start" gap="8px">
      <Link href={`/hustlers/${currentDope.id}/initiate?quickBuy`} passHref>
        <Button 
          // autoFocus
          variant="primary">Customize</Button>
      </Link>
    </Box>
  );

  return (
    <AppWindow 
      title="Welcome To The Streets" 
      fullScreen 
      background={bgColor}
    >
      <StackedResponsiveContainer css={css`
        padding: 16px !important;
        ${media.tablet`
          padding: 64px !important;
        `}
      `}>
        {(isLoading || !currentDope) && <LoadingBlock maxRows={5} />}
        {!isLoading && currentDope && (
          <>
            <Box
              flex="3 !important"
              display="flex"
              flexDirection="column"
              justifyContent="center"
              alignItems="center"
              gap="8px"
            >
              {/* <div className="smallest">
                <a
                  href="/swap-meet"
                  css={css`
                    border-bottom: 1px solid black;
                  `}
                >
                  See more DOPE on our Swap Meet
                </a>
              </div> */}
              <Box width="100%" height="100%" position="relative" minHeight="350px">
                <div
                  css={css`
                    position: absolute;
                    left: 0;
                    bottom: 0;
                    top: 0;
                    right: 0;
                    opacity: ${showHustler ? '1' : '0'};
                  `}
                >
                  <RenderFromDopeIdOnly id={currentDope.id} />
                </div>

                {!showHustler && (
                  <div
                    css={css`
                      display: flex;
                      align-items: center;
                      justify-content: center;
                      height:100%;
                    `}
                  >
                    <DopeCard
                      key={currentDope.id}
                      dope={currentDope}
                      isExpanded={true}
                      buttonBar={null}
                      showCollapse
                      hidePreviewButton
                    />
                  </div>
                )}
              </Box>
              <CarouselButtons />
            </Box>
            <Box display="flex" flexDirection="column" justifyContent="center" gap="16px">
              <Box flex="1"></Box>
              <Box padding="8px" flex="2">
                <h2>Get Hooked On DOPE</h2>
                <hr className="onColor" />
                <p>
                <a href="https://dope-wars.notion.site/dope-wars/Dope-Wiki-e237166bd7e6457babc964d1724befb2#d491a70fab074062b7b3248d6d09c06a" target="wiki" className="underline">Hustlers</a> are the in-game characters of <a href="/about" className="underline" target="about">Dope Wars</a>.
                </p>
                <p>
                  Hustlers can own up to 10 different
                  pieces of interchangeable NFT Gear, which will be useful in a series of games currently under development.
                </p>
                <p>
                  Dope Gear comes directly from unclaimed floor-priced Dope NFT tokens, which sold out in September 2021.
                </p>
                <p>
                  Get a fully equipped Dope Wars setup now.
                </p>
                <Box>
                  <Table
                    css={css`
                      td {
                        padding: 16px 0;
                        border-top: 2px solid rgba(0, 0, 0, 0.15);
                        border-bottom: 2px solid rgba(0, 0, 0, 0.15);
                        vertical-align: top;
                      }
                      dl {
                        width: 100%;
                        dt {
                          width: 100%;
                          display: flex;
                          justify-content: space-between;
                          gap: 4px;
                          margin-bottom: 0.5em;
                          img {
                            opacity: 0.5;
                          }
                        }
                      }
                    `}
                  >
                    <Tr>
                      <Td className="noWrap">You receive</Td>
                      <Td>
                        <dl>
                          <dt>
                            DOPE #{currentDope.id}
                            <Image
                              src="/images/icon/ethereum.svg"
                              width="16px"
                              alt="This asset lives on Ethereum Mainnet"
                            />
                          </dt>
                          <dt>
                            10,000 $PAPER
                            <Image
                              src="/images/icon/ethereum.svg"
                              width="16px"
                              alt="This asset lives on Ethereum Mainnet"
                            />
                          </dt>
                          <dt>
                            1 Hustler
                            <Image
                              src="/images/icon/optimism.svg"
                              width="16px"
                              alt="This asset lives on Optimism"
                            />
                          </dt>
                          <dt>
                            9 Gear
                            <Image
                              src="/images/icon/optimism.svg"
                              width="16px"
                              alt="This asset lives on Optimism"
                            />
                          </dt>
                        </dl>
                      </Td>
                    </Tr>
                    <Tr className="noWrap">
                      <Td borderBottom="0 !important">Estimated Total</Td>
                      <Td borderBottom="0 !important">{currentPrice} Ξ</Td>
                    </Tr>
                  </Table>
                </Box>
              </Box>
              <Box flex="1"></Box>
              <QuickBuyFooter />
            </Box>
          </>
        )}
      </StackedResponsiveContainer>
    </AppWindow>
  );
}
Example #21
Source File: AppProxyPorts.tsx    From ledokku with MIT License 4 votes vote down vote up
AppProxyPorts = ({ appId }: AppProxyPortsProps) => {
  const toast = useToast();
  const [isAddModalOpen, setIsAddModalOpen] = useState(false);
  const [isDeleteModalOpen, setIsDeleteModalOpen] = useState<
    false | AppProxyPort
  >(false);
  const {
    data: appProxyPortsData,
    loading: appProxyPortsLoading,
    // TODO display error
    // error: appProxyPortsError,
    refetch: appProxyPortsRefetch,
  } = useAppProxyPortsQuery({
    variables: { appId },
    notifyOnNetworkStatusChange: true,
  });
  const [
    removeAppProxyPortMutation,
    { loading: removeAppPortLoading },
  ] = useRemoveAppProxyPortMutation();

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

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

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

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

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

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

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

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

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

      <AddAppProxyPorts
        appId={appId}
        appProxyPortsRefetch={appProxyPortsRefetch}
        open={isAddModalOpen}
        onClose={() => setIsAddModalOpen(false)}
      />
    </>
  );
}
Example #22
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 #23
Source File: index.tsx    From ledokku with MIT License 4 votes vote down vote up
Database = () => {
  const { id: databaseId } = useParams<{ id: string }>();
  const toast = useToast();
  const [isUnlinkModalOpen, setIsUnlinkModalOpen] = useState(false);
  const [isLinkModalOpen, setIsLinkModalOpen] = useState(false);
  const [arrayOfUnlinkLogs, setArrayOfUnlinkLogs] = useState<RealTimeLog[]>([]);
  const [arrayOfLinkLogs, setArrayOfLinkLogs] = useState<RealTimeLog[]>([]);
  const [appAboutToUnlink, setAppAboutToUnlink] = 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 [selectedApp, setSelectedApp] = useState({
    value: { name: '', id: '' },
    label: 'Please select an app',
  });
  const [
    linkDatabaseMutation,
    {
      data: databaseLinkData,
      loading: databaseLinkLoading,
      error: databaseLinkError,
    },
  ] = useLinkDatabaseMutation();

  const { data: appsData } = useAppsQuery();

  const { data, loading, refetch /* error */ } = useDatabaseByIdQuery({
    variables: {
      databaseId,
    },
    ssr: false,
    skip: !databaseId,
  });

  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');
        }
      }
    },
  });

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

  // // TODO display error

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

  const { database } = data;
  const { apps } = appsData;

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

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

  const linkedApps = database.apps;
  const linkedIds = linkedApps?.map((db) => db.id);
  const notLinkedApps = apps.filter((db) => {
    return linkedIds?.indexOf(db.id) === -1;
  });

  const appOptions = notLinkedApps.map((app) => {
    return {
      value: { name: app.name, id: app.id },
      label: app.name,
    };
  });

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

  return (
    <div>
      <HeaderContainer>
        <Header />
        <DatabaseHeaderInfo database={database} />
        <DatabaseHeaderTabNav database={database} />
      </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}>
              Database 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">
                      Database name
                    </Td>
                    <Td py="3" px="4">
                      {database.name}
                    </Td>
                  </Tr>
                  <Tr>
                    <Td className="font-semibold" py="7" px="4">
                      id
                    </Td>
                    <Td w="1/3" py="3" px="4">
                      {database.id}
                    </Td>
                  </Tr>
                  <Tr>
                    <Td className="font-semibold" py="3" px="4">
                      Type
                    </Td>
                    <Td py="3" px="4">
                      {database.type}
                    </Td>
                  </Tr>
                </Tbody>
              </Table>
            </div>
          </div>

          <div className="w-full">
            <h1 className="font-bold text-lg font-bold py-5">Apps</h1>
            {apps.length === 0 ? (
              <>
                <div className="mt-3 mb-4">
                  <h2 className="text-gray-400">
                    Currently you haven't created apps, to do so proceed with
                    the app creation flow
                  </h2>
                </div>
                <Link to="/create-app">
                  <Button width="large" color={'grey'}>
                    Create app
                  </Button>
                </Link>
              </>
            ) : (
              <>
                {notLinkedApps.length !== 0 ? (
                  <div>
                    <Listbox
                      as="div"
                      value={selectedApp}
                      //@ts-ignore
                      onChange={setSelectedApp}
                    >
                      {({ 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">
                              {selectedApp.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 shadow-ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm sm:leading-5"
                              >
                                {appOptions.map(
                                  (app) =>
                                    app.value.id !== selectedApp.value.id && (
                                      <Listbox.Option
                                        key={appOptions.indexOf(app)}
                                        value={app as any}
                                      >
                                        {({ active }) => (
                                          <div
                                            className={cx(
                                              'cursor-default select-none relative py-2 px-4',
                                              {
                                                'bg-gray-200': active,
                                                'bg-white text-black': !active,
                                              }
                                            )}
                                          >
                                            {app.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={!selectedApp.value.id}
                      onClick={() => setIsLinkModalOpen(true)}
                    >
                      Link app
                    </Button>
                    {isLinkModalOpen && (
                      <Modal>
                        <ModalTitle>Link app</ModalTitle>
                        <ModalDescription>
                          {isTerminalVisible ? (
                            <>
                              <p className="mb-2 ">
                                Linking <b>{selectedApp.value.name}</b> with{' '}
                                <b>{database.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>{selectedApp.value.name}</b> to{' '}
                              <b>{database.name}</b>?
                            </p>
                          )}
                        </ModalDescription>
                        <ModalButton
                          ctaFn={() => {
                            setProcessStatus('running');
                            handleConnect(databaseId, selectedApp.value.id);
                          }}
                          ctaText={'Link'}
                          otherButtonText={'Cancel'}
                          isCtaLoading={isTerminalVisible ? false : linkLoading}
                          isCtaDisabled={isTerminalVisible}
                          isOtherButtonDisabled={processStatus === 'running'}
                          closeModal={() => {
                            setIsLinkModalOpen(false);
                            refetch({ databaseId });
                            setLinkLoading(false);
                            setIsTerminalVisible(false);
                            setProcessStatus('notStarted');
                          }}
                        />
                      </Modal>
                    )}
                  </div>
                ) : (
                  <>
                    <p className="mt-3 mb-3 mr-8 text-cool-gray-400">
                      All your apps are already linked to this database! If you
                      want to create more apps proceed with create app flow.
                    </p>
                    <div className="ml-80">
                      <Link to="/create-app">
                        <Button
                          color={'grey'}
                          variant="outline"
                          className="text-sm mr-3"
                        >
                          Create app
                        </Button>
                      </Link>
                    </div>
                  </>
                )}

                {!loading && database && database.apps && (
                  <>
                    <h2 className="mb-1 mt-3 font-semibold">
                      {database.apps.length > 0 && 'Linked apps'}
                    </h2>
                    {database.apps.map((app) => (
                      <div
                        key={database.apps?.indexOf(app)}
                        className="flex flex-row justify-start"
                      >
                        <Link to={`/app/${app.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">
                            {app.name}
                          </div>
                        </Link>
                        <Button
                          width="normal"
                          className="mt-4 ml-2 h-10"
                          color="red"
                          onClick={() => {
                            setIsUnlinkModalOpen(true);
                            setAppAboutToUnlink(app.name);
                          }}
                        >
                          Unlink
                        </Button>

                        {isUnlinkModalOpen && (
                          <Modal>
                            <ModalTitle>Unlink app</ModalTitle>
                            <ModalDescription>
                              {isTerminalVisible ? (
                                <>
                                  <p className="mb-2 ">
                                    Unlinking <b>{database.name}</b> from{' '}
                                    <b>{appAboutToUnlink}</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>{database.name}</b> from{' '}
                                  <b>{appAboutToUnlink}</b>?
                                </p>
                              )}
                            </ModalDescription>
                            <ModalButton
                              ctaFn={() => {
                                setProcessStatus('running');
                                handleUnlink(database.id, app.id);
                              }}
                              ctaText={'Unlink'}
                              otherButtonText={'Cancel'}
                              isCtaLoading={
                                isTerminalVisible ? false : unlinkLoading
                              }
                              isOtherButtonDisabled={
                                processStatus === 'running'
                              }
                              isCtaDisabled={isTerminalVisible}
                              closeModal={() => {
                                setIsUnlinkModalOpen(false);
                                refetch({ databaseId });
                                setUnlinkLoading(false);
                                setIsTerminalVisible(false);
                                setAppAboutToUnlink('');
                                setProcessStatus('notStarted');
                              }}
                            />
                          </Modal>
                        )}
                      </div>
                    ))}
                  </>
                )}
              </>
            )}
          </div>
        </div>
      </Container>
    </div>
  );
}
Example #24
Source File: ContactsTable.tsx    From bluebubbles-server with Apache License 2.0 4 votes vote down vote up
ContactsTable = ({
    contacts,
    onCreate,
    onDelete,
    onUpdate,
    onAddressAdd,
    onAddressDelete
}: {
    contacts: Array<ContactItem>,
    onCreate?: (contact: ContactItem) => void,
    onDelete?: (contactId: number | string) => void,
    onUpdate?: (contact: Partial<ContactItem>) => void,
    onAddressAdd?: (contactId: number | string, address: string) => void;
    onAddressDelete?: (contactAddressId: number) => void;
}): JSX.Element => {
    const dialogRef = useRef(null);
    const [dialogOpen, setDialogOpen] = useBoolean();
    const [selectedContact, setSelectedContact] = useState(null as any | null);

    return (
        <Box>
            <Table variant="striped" colorScheme="blue" size='sm'>
                <Thead>
                    <Tr>
                        <Th>Edit</Th>
                        <Th>Display Name</Th>
                        <Th isNumeric>Addresses</Th>
                    </Tr>
                </Thead>
                <Tbody>
                    {contacts.map(item => {
                        const name = (item.displayName && item.displayName.length > 0)
                            ? item.displayName
                            : [item?.firstName, item?.lastName].filter((e) => e && e.length > 0).join(' ');
                        const addresses = [
                            ...(item.phoneNumbers ?? []).map(e => e.address),
                            ...(item.emails ?? []).map(e => e.address)
                        ];
                        return (
                            <Tr key={`${item.sourceType}-${item.id}-${name}-${addresses.join('_')}`}>
                                <Td _hover={{ cursor: (item?.sourceType === 'api') ? 'auto' : 'pointer' }} onClick={() => {
                                    if (item?.sourceType === 'api') return;
                                    setSelectedContact(item);
                                    setDialogOpen.on();
                                }}>
                                    {(item?.sourceType === 'api') ? (
                                        <Tooltip label="Not Editable" hasArrow aria-label='not editable tooltip'>
                                            <span>
                                                <Icon as={MdOutlineEditOff} />
                                            </span>
                                        </Tooltip>
                                    ): (
                                        <Tooltip label="Click to Edit" hasArrow aria-label='editable tooltip'>
                                            <span>
                                                <Icon as={AiOutlineEdit} />
                                            </span>
                                        </Tooltip>
                                    )}
                                </Td>
                                <Td>{name}</Td>
                                <Td isNumeric>{addresses.map((addr) => (
                                    <Badge ml={2} key={`${name}-${addr}-${addresses.length}`}>{addr}</Badge>
                                ))}</Td>
                            </Tr>
                        );
                    })}
                </Tbody>
            </Table>

            <ContactDialog
                modalRef={dialogRef}
                isOpen={dialogOpen}
                existingContact={selectedContact}
                onDelete={onDelete}
                onCreate={onCreate}
                onUpdate={onUpdate}
                onAddressAdd={onAddressAdd}
                onAddressDelete={onAddressDelete}
                onClose={() => {
                    setSelectedContact(null);
                    setDialogOpen.off();
                }}
            />
        </Box>
    );
}