Migrate In-app wallet users page to shadcn/tailwind (#4348)
## Problem solved
Short description of the bug fixed or feature added
<!-- start pr-codex -->
---
## PR-Codex overview
The focus of this PR is to refactor and optimize components in the dashboard app.
### Detailed summary
- Updated rendering of supported platforms in `ConnectLanding`
- Refactored `SupportedPlatformLink` component
- Improved UI and functionality in `Analytics` and `UsageCard`
- Enhanced user experience in `index.tsx` for embedded wallets
> The following files were skipped due to too many changes: `apps/dashboard/src/components/embedded-wallets/Users/index.tsx`, `apps/dashboard/src/pages/dashboard/connect/in-app-wallets.tsx`
> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`
<!-- end pr-codex -->
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { TrackedLinkTW } from "@/components/ui/tracked-link";
|
||||
import { useAccountUsage } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import { Box, Flex } from "@chakra-ui/react";
|
||||
import { UsageCard } from "components/settings/Account/UsageCard";
|
||||
import { ArrowRightIcon } from "lucide-react";
|
||||
import { useMemo } from "react";
|
||||
import { Card, Heading, Text, TrackedLink } from "tw-components";
|
||||
import { toNumber, toPercent } from "utils/number";
|
||||
|
||||
interface AnalyticsProps {
|
||||
@@ -14,7 +15,7 @@ export const Analytics: React.FC<AnalyticsProps> = ({ trackingCategory }) => {
|
||||
|
||||
const walletsMetrics = useMemo(() => {
|
||||
if (!usageQuery?.data) {
|
||||
return {};
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const usageData = usageQuery.data;
|
||||
@@ -36,41 +37,39 @@ export const Analytics: React.FC<AnalyticsProps> = ({ trackingCategory }) => {
|
||||
};
|
||||
}, [usageQuery]);
|
||||
|
||||
if (usageQuery.isLoading || !usageQuery.data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card p={{ base: 6, lg: 12 }}>
|
||||
<Flex
|
||||
flexDir={{ base: "column", lg: "row" }}
|
||||
justifyContent="space-evenly"
|
||||
gap={6}
|
||||
>
|
||||
<Flex flexDir="column" gap={2} justifyContent="center">
|
||||
<Text size="label.sm">Analytics</Text>
|
||||
<Heading size="title.sm" maxW="md" lineHeight={1.3}>
|
||||
View more insights about how users are interacting with your
|
||||
application
|
||||
</Heading>
|
||||
<div className="border border-border rounded-lg p-4 md:px-10 md:py-12 flex flex-col lg:flex-row lg:justify-between gap-6 bg-muted/50">
|
||||
{/* Left */}
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground mb-3">Analytics</p>
|
||||
<h2 className="text-2xl tracking-tight font-semibold max-w-[500px] mb-5">
|
||||
View more insights about how users are interacting with your
|
||||
application
|
||||
</h2>
|
||||
|
||||
<TrackedLink
|
||||
color="blue.500"
|
||||
<Button asChild variant="outline" size="sm">
|
||||
<TrackedLinkTW
|
||||
href="/dashboard/connect/analytics"
|
||||
category={trackingCategory}
|
||||
label="view-analytics"
|
||||
className="mt-auto min-w-[150px] gap-2"
|
||||
>
|
||||
View Analytics
|
||||
</TrackedLink>
|
||||
</Flex>
|
||||
<Box minW={280}>
|
||||
<ArrowRightIcon className="size-4" />
|
||||
</TrackedLinkTW>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Right */}
|
||||
<div className="min-w-[280px]">
|
||||
{walletsMetrics && (
|
||||
<UsageCard
|
||||
{...walletsMetrics}
|
||||
name="Monthly Active Users"
|
||||
tooltip="Email wallet (with managed recovery code) usage is calculated by monthly active wallets (i.e. active as defined by at least 1 user log-in via email or social within the billing period month)."
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { PaginationButtons } from "@/components/pagination-buttons";
|
||||
import { CopyAddressButton } from "@/components/ui/CopyAddressButton";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import type { EmbeddedWalletUser } from "@3rdweb-sdk/react/hooks/useEmbeddedWallets";
|
||||
import { Flex, Switch } from "@chakra-ui/react";
|
||||
import { createColumnHelper } from "@tanstack/react-table";
|
||||
import { TWTable } from "components/shared/TWTable";
|
||||
import { format } from "date-fns/format";
|
||||
import Papa from "papaparse";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { Button, Text } from "tw-components";
|
||||
import { AddressCopyButton } from "tw-components/AddressCopyButton";
|
||||
import { withinDays } from "utils/date-utils";
|
||||
import { Analytics } from "./Analytics";
|
||||
|
||||
const ACTIVE_THRESHOLD_DAYS = 30;
|
||||
|
||||
@@ -26,13 +25,22 @@ const columns = [
|
||||
columnHelper.accessor("ews_authed_user", {
|
||||
header: "Email",
|
||||
enableColumnFilter: true,
|
||||
cell: (cell) => <Text>{cell.getValue()?.[0]?.email}</Text>,
|
||||
cell: (cell) => (
|
||||
<span className="text-sm">{cell.getValue()?.[0]?.email}</span>
|
||||
),
|
||||
}),
|
||||
columnHelper.accessor("embedded_wallet", {
|
||||
header: "Address",
|
||||
cell: (cell) => {
|
||||
const address = cell.getValue()?.[0]?.address;
|
||||
return address ? <AddressCopyButton address={address} /> : null;
|
||||
return address ? (
|
||||
<CopyAddressButton
|
||||
address={address}
|
||||
copyIconPosition="left"
|
||||
variant="ghost"
|
||||
className="-translate-x-2"
|
||||
/>
|
||||
) : null;
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("created_at", {
|
||||
@@ -43,7 +51,11 @@ const columns = [
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
return <Text>{format(new Date(value), "MMM dd, yyyy")}</Text>;
|
||||
return (
|
||||
<span className="text-sm">
|
||||
{format(new Date(value), "MMM dd, yyyy")}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("last_accessed_at", {
|
||||
@@ -54,7 +66,11 @@ const columns = [
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
return <Text>{format(new Date(value), "MMM dd, yyyy")}</Text>;
|
||||
return (
|
||||
<span className="text-sm">
|
||||
{format(new Date(value), "MMM dd, yyyy")}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
}),
|
||||
];
|
||||
@@ -63,7 +79,6 @@ export const Users: React.FC<UsersProps> = ({
|
||||
wallets,
|
||||
isLoading,
|
||||
isFetched,
|
||||
trackingCategory,
|
||||
}) => {
|
||||
const [onlyActive, setOnlyActive] = useState(true);
|
||||
|
||||
@@ -119,11 +134,12 @@ export const Users: React.FC<UsersProps> = ({
|
||||
}, [activePage, theWalletsWeWant]);
|
||||
|
||||
return (
|
||||
<Flex flexDir="column" gap={10}>
|
||||
<Flex flexDir="column" gap={6}>
|
||||
<Flex dir="row" justify="space-between" align="center">
|
||||
<div>
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* Top section */}
|
||||
<div className="flex justify-between items-center">
|
||||
<Button
|
||||
isDisabled={theWalletsWeWant.length === 0}
|
||||
disabled={theWalletsWeWant.length === 0}
|
||||
variant="outline"
|
||||
onClick={downloadCSV}
|
||||
size="sm"
|
||||
@@ -131,15 +147,17 @@ export const Users: React.FC<UsersProps> = ({
|
||||
Download as .csv
|
||||
</Button>
|
||||
|
||||
<Flex gap={2} alignItems="center" justifyContent="flex-end">
|
||||
<Text>Active last {ACTIVE_THRESHOLD_DAYS} days</Text>
|
||||
<div className="flex gap-2 items-center justify-end">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Active last {ACTIVE_THRESHOLD_DAYS} days
|
||||
</p>
|
||||
<Switch
|
||||
isChecked={onlyActive}
|
||||
onChange={() => setOnlyActive(!onlyActive)}
|
||||
checked={onlyActive}
|
||||
onCheckedChange={(v) => setOnlyActive(v)}
|
||||
disabled={wallets.length === 0}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TWTable
|
||||
title="active in-app wallets"
|
||||
@@ -156,9 +174,7 @@ export const Users: React.FC<UsersProps> = ({
|
||||
totalPages={totalPages}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
<Analytics trackingCategory={trackingCategory} />
|
||||
</Flex>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import {
|
||||
Box,
|
||||
Icon,
|
||||
Progress,
|
||||
Tooltip,
|
||||
VStack,
|
||||
useColorModeValue,
|
||||
} from "@chakra-ui/react";
|
||||
import { FiHelpCircle } from "react-icons/fi";
|
||||
import { Card, Heading, Text } from "tw-components";
|
||||
import { ToolTipLabel } from "@/components/ui/tooltip";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { CircleHelpIcon } from "lucide-react";
|
||||
import { toUSD } from "utils/number";
|
||||
|
||||
interface UsageCardProps {
|
||||
@@ -19,16 +12,6 @@ interface UsageCardProps {
|
||||
tooltip?: string;
|
||||
}
|
||||
|
||||
const getProgressColor = (percent: number) => {
|
||||
if (percent > 90) {
|
||||
return "red";
|
||||
}
|
||||
if (percent > 50) {
|
||||
return "yellow";
|
||||
}
|
||||
return "blue";
|
||||
};
|
||||
|
||||
export const UsageCard: React.FC<UsageCardProps> = ({
|
||||
name,
|
||||
title,
|
||||
@@ -37,75 +20,59 @@ export const UsageCard: React.FC<UsageCardProps> = ({
|
||||
progress,
|
||||
tooltip,
|
||||
}) => {
|
||||
const bg = useColorModeValue("backgroundCardHighlight", "transparent");
|
||||
|
||||
return (
|
||||
<Card minH={36} p={6} bg={bg}>
|
||||
<VStack gap={6} alignItems="flex-start">
|
||||
<Heading as="h3" size="label.lg">
|
||||
{name}
|
||||
<div className="p-4 bg-background border border-border rounded-lg min-h-[140px] flex flex-col">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="text-base font-medium">{name}</h3>
|
||||
{tooltip && (
|
||||
<ToolTipLabel label={tooltip}>
|
||||
<CircleHelpIcon className="text-muted-foreground size-4" />
|
||||
</ToolTipLabel>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{tooltip && (
|
||||
<Tooltip
|
||||
p={0}
|
||||
bg="transparent"
|
||||
boxShadow={"none"}
|
||||
label={
|
||||
<Card py={2} px={4} bgColor="backgroundHighlight">
|
||||
<Text size="body.md">{tooltip}</Text>
|
||||
</Card>
|
||||
}
|
||||
>
|
||||
<Box as="span" display="relative">
|
||||
<Icon
|
||||
opacity={0.5}
|
||||
as={FiHelpCircle}
|
||||
position="absolute"
|
||||
ml={1}
|
||||
/>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Heading>
|
||||
<div className="h-6" />
|
||||
|
||||
<VStack gap={2} alignItems="flex-start" w="full">
|
||||
{title && (
|
||||
<Text size="body.md" mb={2}>
|
||||
{title}
|
||||
</Text>
|
||||
)}
|
||||
<div className="flex flex-col gap-2 mt-auto">
|
||||
{title && <p className="mb-2 text-foreground font-semibold">{title}</p>}
|
||||
|
||||
{total !== undefined && (
|
||||
<Text
|
||||
size="body.md"
|
||||
mb={2}
|
||||
color={typeof total === "number" ? "bgBlack" : undefined}
|
||||
>
|
||||
{typeof total === "number" ? toUSD(total) : total}
|
||||
</Text>
|
||||
)}
|
||||
{total !== undefined && (
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{typeof total === "number" ? toUSD(total) : total}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{progress !== undefined && (
|
||||
<Progress
|
||||
w="full"
|
||||
size="xs"
|
||||
value={progress}
|
||||
rounded="full"
|
||||
colorScheme={getProgressColor(progress)}
|
||||
/>
|
||||
)}
|
||||
{progress !== undefined && <Progress value={progress} />}
|
||||
|
||||
{overage && (
|
||||
<Text size="body.md" mt={2}>
|
||||
Additional overage fees to your next invoice will be{" "}
|
||||
<Text as="span" size="label.sm" color="bgBlack">
|
||||
{toUSD(overage)}
|
||||
</Text>
|
||||
.
|
||||
</Text>
|
||||
)}
|
||||
</VStack>
|
||||
</VStack>
|
||||
</Card>
|
||||
{overage && (
|
||||
<p className="text-muted-foreground mt-2 text-sm">
|
||||
Additional overage fees to your next invoice will be{" "}
|
||||
<span className="text-foreground">{toUSD(overage)}</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function Progress(props: {
|
||||
value: number;
|
||||
}) {
|
||||
return (
|
||||
<div className="bg-muted rounded-full">
|
||||
<div
|
||||
className={cn(
|
||||
"h-2 rounded-full",
|
||||
props.value > 90
|
||||
? "bg-red-600"
|
||||
: props.value > 50
|
||||
? "bg-yellow-600"
|
||||
: "bg-blue-600",
|
||||
)}
|
||||
style={{
|
||||
width: `${props.value}%`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ export function TWTable<TRowData>(tableProps: TWTableProps<TRowData>) {
|
||||
|
||||
{tableProps.isLoading && (
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="flex py-4 gap-4 items-center">
|
||||
<div className="flex py-4 gap-2 items-center">
|
||||
<Spinner className="size-4" />
|
||||
<p className="text-muted-foreground text-sm">
|
||||
Loading {pluralize(tableProps.title, 0, false)}
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import { Flex, type FlexProps, Icon } from "@chakra-ui/react";
|
||||
import { TrackedLinkTW } from "@/components/ui/tracked-link";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { SiReact } from "@react-icons/all-files/si/SiReact";
|
||||
import { SiTypescript } from "@react-icons/all-files/si/SiTypescript";
|
||||
import { SiUnity } from "@react-icons/all-files/si/SiUnity";
|
||||
import type { IconType } from "react-icons/lib";
|
||||
import { Text, TrackedLink } from "tw-components";
|
||||
|
||||
export function SupportedPlatformLink(props: {
|
||||
platform: "React" | "React Native" | "Unity" | "TypeScript";
|
||||
href: string;
|
||||
noBorder?: boolean;
|
||||
size: "sm" | "md";
|
||||
trackingCategory: string;
|
||||
bg?: FlexProps["bg"];
|
||||
className?: string;
|
||||
}) {
|
||||
let icon: IconType = SiReact;
|
||||
if (props.platform === "Unity") {
|
||||
@@ -21,53 +19,18 @@ export function SupportedPlatformLink(props: {
|
||||
}
|
||||
|
||||
return (
|
||||
<TrackedLink
|
||||
<TrackedLinkTW
|
||||
category={props.trackingCategory}
|
||||
label={`platform-${props.platform.replace(" ", "-").toLowerCase()}`}
|
||||
href={props.href}
|
||||
isExternal
|
||||
_hover={{
|
||||
textDecor: "none",
|
||||
color: "blue.500",
|
||||
}}
|
||||
role="group"
|
||||
target="_blank"
|
||||
className={cn(
|
||||
"flex items-center gap-2 text-muted-foreground hover:text-foreground text-sm",
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
<Flex
|
||||
bg={props.bg}
|
||||
gap={props.size === "sm" ? 2 : 3}
|
||||
alignItems="center"
|
||||
border={props.noBorder ? undefined : "1px solid"}
|
||||
borderColor="borderColor"
|
||||
px={props.noBorder ? 0 : 2}
|
||||
py={1}
|
||||
borderRadius="md"
|
||||
_groupHover={{
|
||||
borderColor: "blue.500",
|
||||
}}
|
||||
transition="border-color 200ms ease"
|
||||
>
|
||||
<Icon
|
||||
as={icon}
|
||||
w={props.size === "sm" ? 4 : 5}
|
||||
h={props.size === "sm" ? 4 : 5}
|
||||
color="faded"
|
||||
_groupHover={{
|
||||
color: "blue.500",
|
||||
}}
|
||||
transition="color 200ms ease"
|
||||
/>
|
||||
<Text
|
||||
fontWeight={500}
|
||||
color="heading"
|
||||
_groupHover={{
|
||||
color: "blue.500",
|
||||
}}
|
||||
fontSize={props.size === "sm" ? 12 : 14}
|
||||
transition="color 200ms ease"
|
||||
>
|
||||
{props.platform}
|
||||
</Text>
|
||||
</Flex>
|
||||
</TrackedLink>
|
||||
{icon({ className: "size-4" })}
|
||||
{props.platform}
|
||||
</TrackedLinkTW>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,6 +94,21 @@ const GUIDES = [
|
||||
];
|
||||
|
||||
const ConnectLanding: ThirdwebNextPage = () => {
|
||||
const platforms = [
|
||||
{
|
||||
platform: "React",
|
||||
href: "https://portal.thirdweb.com/typescript/v5/react/components/ConnectButton",
|
||||
},
|
||||
{
|
||||
platform: "React Native",
|
||||
href: "https://portal.thirdweb.com/react-native/latest/components/ConnectWallet",
|
||||
},
|
||||
{
|
||||
platform: "Unity",
|
||||
href: "https://portal.thirdweb.com/unity/wallets/prefab",
|
||||
},
|
||||
] as const;
|
||||
|
||||
return (
|
||||
<LandingLayout
|
||||
bgColor="#0F0F0F"
|
||||
@@ -228,27 +243,15 @@ const ConnectLanding: ThirdwebNextPage = () => {
|
||||
>
|
||||
Supports
|
||||
</Text>
|
||||
<SupportedPlatformLink
|
||||
trackingCategory={TRACKING_CATEGORY}
|
||||
size="sm"
|
||||
platform="React"
|
||||
href="https://portal.thirdweb.com/typescript/v5/react/components/ConnectButton"
|
||||
bg="#0E0E0E"
|
||||
/>
|
||||
<SupportedPlatformLink
|
||||
trackingCategory={TRACKING_CATEGORY}
|
||||
size="sm"
|
||||
platform="React Native"
|
||||
href="https://portal.thirdweb.com/react-native/latest/components/ConnectWallet"
|
||||
bg="#0E0E0E"
|
||||
/>
|
||||
<SupportedPlatformLink
|
||||
trackingCategory={TRACKING_CATEGORY}
|
||||
size="sm"
|
||||
platform="Unity"
|
||||
href="https://portal.thirdweb.com/unity/wallets/prefab"
|
||||
bg="#0E0E0E"
|
||||
/>
|
||||
{platforms.map(({ platform, href }) => (
|
||||
<SupportedPlatformLink
|
||||
key={platform}
|
||||
trackingCategory={TRACKING_CATEGORY}
|
||||
platform={platform}
|
||||
href={href}
|
||||
className="text-foreground hover:text-link-foreground bg-background px-3 py-2 rounded-lg text-xs border border-border hover:border-link-foreground"
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
|
||||
<MiniPlayground trackingCategory={TRACKING_CATEGORY} />
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { Spinner } from "@/components/ui/Spinner/Spinner";
|
||||
import { TrackedLinkTW } from "@/components/ui/tracked-link";
|
||||
import { type ApiKey, useApiKeys } from "@3rdweb-sdk/react/hooks/useApi";
|
||||
import { useEmbeddedWallets } from "@3rdweb-sdk/react/hooks/useEmbeddedWallets";
|
||||
import { useLoggedInUser } from "@3rdweb-sdk/react/hooks/useLoggedInUser";
|
||||
import { Flex, Grid, HStack, Icon, Spacer } from "@chakra-ui/react";
|
||||
import { AppLayout } from "components/app-layouts/app";
|
||||
import { EmbeddedWallets } from "components/embedded-wallets";
|
||||
import { ApiKeysMenu } from "components/settings/ApiKeys/Menu";
|
||||
import { NoApiKeys } from "components/settings/ApiKeys/NoApiKeys";
|
||||
import { ConnectSidebar } from "core-ui/sidebar/connect";
|
||||
import { ArrowRightIcon } from "lucide-react";
|
||||
import { useRouter } from "next/router";
|
||||
import { PageId } from "page-id";
|
||||
import { useMemo, useState } from "react";
|
||||
import { AiOutlineArrowRight } from "react-icons/ai";
|
||||
import { Card, Heading, Text, TrackedLink } from "tw-components";
|
||||
import type { ThirdwebNextPage } from "utils/types";
|
||||
import { Analytics } from "../../../components/embedded-wallets/Users/Analytics";
|
||||
import { SupportedPlatformLink } from "../../../components/wallets/SupportedPlatformLink";
|
||||
|
||||
const TRACKING_CATEGORY = "embedded-wallet";
|
||||
@@ -64,44 +64,43 @@ const DashboardConnectEmbeddedWallets: ThirdwebNextPage = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex flexDir="column" gap={8}>
|
||||
<Flex flexDir="column" gap={2}>
|
||||
<Flex
|
||||
justifyContent="space-between"
|
||||
direction={{ base: "column", lg: "row" }}
|
||||
gap={4}
|
||||
>
|
||||
<Heading size="title.lg" as="h1">
|
||||
<div>
|
||||
<div className="flex lg:justify-between gap-4 flex-col lg:flex-row">
|
||||
<div>
|
||||
<h1 className="font-semibold text-3xl tracking-tigher">
|
||||
In-App Wallets
|
||||
</Heading>
|
||||
{hasApiKeys && (
|
||||
<HStack gap={3}>
|
||||
{selectedKey && (
|
||||
<ApiKeysMenu
|
||||
apiKeys={apiKeys}
|
||||
selectedKey={selectedKey}
|
||||
onSelect={setSelectedKey}
|
||||
/>
|
||||
)}
|
||||
</HStack>
|
||||
)}
|
||||
</Flex>
|
||||
</h1>
|
||||
|
||||
<Text maxW="xl">
|
||||
A wallet infrastructure that enables apps to create, manage, and
|
||||
control their users wallets. Email login, social login, and
|
||||
bring-your-own auth supported.{" "}
|
||||
<TrackedLink
|
||||
isExternal
|
||||
href="https://portal.thirdweb.com/connect/in-app-wallet/overview"
|
||||
label="learn-more"
|
||||
category={TRACKING_CATEGORY}
|
||||
color="primary.500"
|
||||
>
|
||||
Learn more
|
||||
</TrackedLink>
|
||||
</Text>
|
||||
</Flex>
|
||||
<div className="h-3" />
|
||||
|
||||
<p className="max-w-[500px] text-muted-foreground ">
|
||||
A wallet infrastructure that enables apps to create, manage, and
|
||||
control their users wallets. Email login, social login, and
|
||||
bring-your-own auth supported.{" "}
|
||||
<TrackedLinkTW
|
||||
target="_blank"
|
||||
href="https://portal.thirdweb.com/connect/in-app-wallet/overview"
|
||||
label="learn-more"
|
||||
category={TRACKING_CATEGORY}
|
||||
className="text-link-foreground hover:text-foreground"
|
||||
>
|
||||
Learn more
|
||||
</TrackedLinkTW>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{hasApiKeys && selectedKey && (
|
||||
<ApiKeysMenu
|
||||
apiKeys={apiKeys}
|
||||
selectedKey={selectedKey}
|
||||
onSelect={setSelectedKey}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-8" />
|
||||
|
||||
{keysQuery.isLoading ? (
|
||||
<div className="flex h-[500px] items-center justify-center">
|
||||
@@ -124,78 +123,62 @@ const DashboardConnectEmbeddedWallets: ThirdwebNextPage = () => {
|
||||
</>
|
||||
)}
|
||||
|
||||
<Spacer height={20} />
|
||||
<div className="h-16" />
|
||||
<Analytics trackingCategory={TRACKING_CATEGORY} />
|
||||
<div className="h-5" />
|
||||
|
||||
<FooterSection />
|
||||
</Flex>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||||
<ViewDocs />
|
||||
<Templates />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function FooterSection() {
|
||||
return (
|
||||
<Grid templateColumns={["1fr", "1fr 1fr"]} gap={5}>
|
||||
<ViewDocs />
|
||||
<Templates />
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
function ViewDocs() {
|
||||
return (
|
||||
<Card p={5} className="bg-muted/50">
|
||||
<Flex gap={2} alignItems="center">
|
||||
<Heading fontSize={16} as="h3">
|
||||
View Docs
|
||||
</Heading>
|
||||
<Icon as={AiOutlineArrowRight} width={5} height={5} />
|
||||
</Flex>
|
||||
<div className="p-4 lg:p-6 bg-muted/50 border border-border rounded-lg">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="font-semibold">View Docs</h3>
|
||||
<ArrowRightIcon className="size-4" />
|
||||
</div>
|
||||
|
||||
<Spacer height={6} />
|
||||
<div className="h-4" />
|
||||
|
||||
<Grid templateColumns={"1fr 1fr"} gap={3} maxW="400px">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<SupportedPlatformLink
|
||||
trackingCategory={TRACKING_CATEGORY}
|
||||
size="md"
|
||||
noBorder
|
||||
platform="React"
|
||||
href="https://portal.thirdweb.com/connect/in-app-wallet/overview"
|
||||
/>
|
||||
|
||||
<SupportedPlatformLink
|
||||
trackingCategory={TRACKING_CATEGORY}
|
||||
noBorder
|
||||
size="md"
|
||||
platform="Unity"
|
||||
href="https://portal.thirdweb.com/unity/wallets/providers/embedded-wallet"
|
||||
/>
|
||||
<SupportedPlatformLink
|
||||
trackingCategory={TRACKING_CATEGORY}
|
||||
noBorder
|
||||
size="md"
|
||||
platform="React Native"
|
||||
href="https://portal.thirdweb.com/react-native/latest/connect/in-app-wallet"
|
||||
href="https://portal.thirdweb.com/react/v5/in-app-wallet/get-started"
|
||||
/>
|
||||
<SupportedPlatformLink
|
||||
trackingCategory={TRACKING_CATEGORY}
|
||||
noBorder
|
||||
size="md"
|
||||
platform="TypeScript"
|
||||
href="https://portal.thirdweb.com/connect/in-app-wallet/overview"
|
||||
/>
|
||||
</Grid>
|
||||
</div>
|
||||
|
||||
<Spacer height={8} />
|
||||
<div className="h-6" />
|
||||
|
||||
<Flex gap={2} alignItems="center">
|
||||
<Heading fontSize={16} as="h3">
|
||||
Relevant Guides
|
||||
</Heading>
|
||||
<Icon as={AiOutlineArrowRight} width={5} height={5} />
|
||||
</Flex>
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="font-semibold">Relevant Guides</h3>
|
||||
<ArrowRightIcon className="size-4" />
|
||||
</div>
|
||||
|
||||
<Spacer height={5} />
|
||||
<div className="h-4" />
|
||||
|
||||
<Flex gap={3} flexDirection="column">
|
||||
<div className="flex flex-col gap-3">
|
||||
<GuideLink
|
||||
href="https://blog.thirdweb.com/what-are-embedded-wallets/"
|
||||
label="what-is-an-embedded-wallet"
|
||||
@@ -230,8 +213,8 @@ function ViewDocs() {
|
||||
>
|
||||
Create a custom auth server
|
||||
</GuideLink>
|
||||
</Flex>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -241,7 +224,7 @@ function GuideLink(props: {
|
||||
href: string;
|
||||
}) {
|
||||
return (
|
||||
<TrackedLink
|
||||
<TrackedLinkTW
|
||||
category={TRACKING_CATEGORY}
|
||||
label={"guide"}
|
||||
trackingProps={{
|
||||
@@ -249,26 +232,24 @@ function GuideLink(props: {
|
||||
}}
|
||||
href={props.href}
|
||||
className="text-sm !text-muted-foreground hover:!text-foreground"
|
||||
isExternal
|
||||
target="_blank"
|
||||
>
|
||||
{props.children}
|
||||
</TrackedLink>
|
||||
</TrackedLinkTW>
|
||||
);
|
||||
}
|
||||
|
||||
function Templates() {
|
||||
return (
|
||||
<Card p={5} className="bg-muted/50">
|
||||
<Flex gap={2} alignItems="center">
|
||||
<Heading fontSize={16} as="h3">
|
||||
Relevant Templates
|
||||
</Heading>
|
||||
<Icon as={AiOutlineArrowRight} width={5} height={5} />
|
||||
</Flex>
|
||||
<div className="p-4 lg:p-6 bg-muted/50 border border-border rounded-lg">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="font-semibold">Relevant Templates</h3>
|
||||
<ArrowRightIcon className="size-4" />
|
||||
</div>
|
||||
|
||||
<Spacer height={6} />
|
||||
<div className="h-6" />
|
||||
|
||||
<Flex gap={3} flexDirection="column">
|
||||
<div className="flex flex-col gap-3">
|
||||
<GuideLink
|
||||
href="https://github.com/thirdweb-example/embedded-smart-wallet"
|
||||
label="embedded-smart-wallet"
|
||||
@@ -296,8 +277,8 @@ function Templates() {
|
||||
>
|
||||
In-App Wallet With Custom UI [ReactNative]
|
||||
</GuideLink>
|
||||
</Flex>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user