@chakra-ui/react#Tr TypeScript Examples
The following examples show how to use
@chakra-ui/react#Tr.
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 |
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 |
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 |
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: MatchDetails.tsx From dope-monorepo with GNU General Public License v3.0 | 6 votes |
PlayerRow = ({ rowIndex }: {
rowIndex: number
}) => {
return (
<Tr>
<Td>
{rowIndex}
</Td>
<Td>
(hustler name)
</Td>
</Tr>
)
}
Example #5
Source File: Lobby.tsx From dope-monorepo with GNU General Public License v3.0 | 6 votes |
MatchRow = () => {
const [isExpanded, setIsExpanded] = useBoolean();
const CELL_PROPS = {
borderBottom: isExpanded ? 'none' : 'inherit',
};
const background = isExpanded ? '#434345' : 'inherit';
return (
<>
<Tr background={background} cursor="pointer" onClick={() => setIsExpanded.toggle()}>
<Td {...CELL_PROPS}>
30 min
</Td>
<Td isNumeric {...CELL_PROPS}>
16
</Td>
</Tr>
{isExpanded && (
<Tr background={background}>
<Td colSpan={2}>
<NavLink href={`/roll-your-own/1`}>
<Button color="black" w="full">View</Button>
</NavLink>
</Td>
</Tr>
)}
</>
)
}
Example #6
Source File: OraclesTable.tsx From rari-dApp with GNU Affero General Public License v3.0 | 6 votes |
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 #7
Source File: Lobby.tsx From dope-monorepo with GNU General Public License v3.0 | 6 votes |
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 #8
Source File: ControlAttendeesPage.tsx From takeout-app with MIT License | 5 votes |
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 #9
Source File: mdxComponents.tsx From lucide with ISC License | 5 votes |
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 #10
Source File: Drugs.tsx From dope-monorepo with GNU General Public License v3.0 | 5 votes |
DrugRow = ({ drug }: { drug: Drug }) => {
const router = useRouter();
const { roundId, locationId } = router.query;
const [isExpanded, setIsExpanded] = useBoolean();
const CELL_PROPS = {
borderBottom: isExpanded ? 'none' : 'inherit',
};
const background = isExpanded ? '#434345' : 'inherit';
return (
<>
<Tr background={background} cursor="pointer" onClick={() => setIsExpanded.toggle()}>
<Td {...CELL_PROPS}>
{drug.rle && (
<div
css={css`
width: 32px;
height: 32px;
overflow: hidden;
}`}
dangerouslySetInnerHTML={{ __html: buildIconSVG([drug.rle]) }}
/>
)}
</Td>
<Td {...CELL_PROPS}>{drug.name}</Td>
<Td isNumeric {...CELL_PROPS}>
{drug.cost}
</Td>
<Td isNumeric {...CELL_PROPS}>
{drug.quantity}
</Td>
</Tr>
{isExpanded && (
<Tr background={background}>
<Td colSpan={2}>
<NavLink href={`/roll-your-own/${roundId}/location/${locationId}/buy/${drug.id}`}>
<Button color="black" w="full">Buy</Button>
</NavLink>
</Td>
<Td colSpan={2}>
<NavLink href={`/roll-your-own/${roundId}/location/${locationId}/sell/${drug.id}`}>
<Button color="black" w="full">SELL</Button>
</NavLink>
</Td>
</Tr>
)}
</>
);
}
Example #11
Source File: Drugs.tsx From dope-monorepo with GNU General Public License v3.0 | 5 votes |
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 #12
Source File: GearCard.tsx From dope-monorepo with GNU General Public License v3.0 | 5 votes |
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 #13
Source File: StatsEarnSection.tsx From rari-dApp with GNU Affero General Public License v3.0 | 5 votes |
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 #14
Source File: OraclesTable.tsx From rari-dApp with GNU Affero General Public License v3.0 | 5 votes |
OracleRow = ({
oracle,
underlyings,
isDefault = false,
}: {
oracle: string;
underlyings: string[];
isDefault?: boolean;
}) => {
const oracleIdentity = useIdentifyOracle(oracle);
const displayedOracle = !!oracleIdentity
? oracleIdentity
: shortAddress(oracle);
return (
<>
<Tr>
<Td>
<Link
href={`https://etherscan.io/address/${oracle}`}
isExternal
_hover={{ pointer: "cursor", color: "#21C35E" }}
>
<Text fontWeight="bold">{displayedOracle}</Text>
</Link>
</Td>
<Td>
{isDefault ? (
<span style={{ fontWeight: "bold" }}>DEFAULT</span>
) : (
<AvatarGroup size="xs" max={30} mr={2}>
{underlyings.map((underlying) => {
return <CTokenIcon key={underlying} address={underlying} />;
})}
</AvatarGroup>
)}
</Td>
</Tr>
</>
);
}
Example #15
Source File: FusePoolEditPage.tsx From rari-dApp with GNU Affero General Public License v3.0 | 5 votes |
RewardsDistributorRow = ({
rewardsDistributor,
handleRowClick,
hideModalDivider,
activeCTokens,
}: {
rewardsDistributor: RewardsDistributor;
handleRowClick: (rD: RewardsDistributor) => void;
hideModalDivider: boolean;
activeCTokens: string[];
}) => {
const { address, fuse } = useRari();
const isAdmin = address === rewardsDistributor.admin;
const tokenData = useTokenData(rewardsDistributor.rewardToken);
// Balances
const { data: rDBalance } = useTokenBalance(
rewardsDistributor.rewardToken,
rewardsDistributor.address
);
const underlyingsMap = useCTokensUnderlying(activeCTokens);
const underlyings = Object.values(underlyingsMap);
return (
<>
<Tr
_hover={{ background: "grey", cursor: "pointer" }}
h="30px"
p={5}
flexDir="row"
onClick={() => handleRowClick(rewardsDistributor)}
>
<Td>
<HStack>
{tokenData?.logoURL ? (
<Image
src={tokenData.logoURL}
boxSize="30px"
borderRadius="50%"
/>
) : null}
<Heading fontSize="22px" color={tokenData?.color ?? "#FFF"} ml={2}>
{tokenData
? tokenData.symbol ?? "Invalid Address!"
: "Loading..."}
</Heading>
</HStack>
</Td>
<Td>
{!!underlyings.length ? (
<CTokenAvatarGroup tokenAddresses={underlyings} popOnHover={true} />
) : (
<Badge colorScheme="red">Inactive</Badge>
)}
</Td>
<Td>
{(
parseFloat(rDBalance?.toString() ?? "0") /
10 ** (tokenData?.decimals ?? 18)
).toFixed(3)}{" "}
{tokenData?.symbol}
</Td>
<Td>
<Badge colorScheme={isAdmin ? "green" : "red"}>
{isAdmin ? "Is Admin" : "Not Admin"}
</Badge>
</Td>
</Tr>
{/* {!hideModalDivider && <ModalDivider />} */}
</>
);
}
Example #16
Source File: WebhooksTable.tsx From bluebubbles-server with Apache License 2.0 | 5 votes |
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 #17
Source File: StatsTranchesSection.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
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 #18
Source File: StatsTotalSection.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
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 #19
Source File: DopeTable.tsx From dope-monorepo with GNU General Public License v3.0 | 4 votes |
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 #20
Source File: StatsPool2Section.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
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 #21
Source File: Hustlers.tsx From dope-monorepo with GNU General Public License v3.0 | 4 votes |
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}> </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 #22
Source File: StatsFuseSection.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
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 #23
Source File: FusePoolEditPage.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
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 #24
Source File: quick-buy.tsx From dope-monorepo with GNU General Public License v3.0 | 4 votes |
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 #25
Source File: AppProxyPorts.tsx From ledokku with MIT License | 4 votes |
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 #26
Source File: index.tsx From ledokku with MIT License | 4 votes |
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 #27
Source File: index.tsx From ledokku with MIT License | 4 votes |
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 #28
Source File: ContactsTable.tsx From bluebubbles-server with Apache License 2.0 | 4 votes |
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>
);
}