@chakra-ui/react#Thead TypeScript Examples

The following examples show how to use @chakra-ui/react#Thead. 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: 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 #5
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 #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: 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 #9
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 #10
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 #11
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 #12
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 #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: 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 #17
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 #18
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>
    );
}