diff --git a/apps/dashboard/src/app/(dashboard)/dashboard/page.tsx b/apps/dashboard/src/app/(dashboard)/dashboard/page.tsx new file mode 100644 index 00000000000..3b7f4c1a216 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/dashboard/page.tsx @@ -0,0 +1,64 @@ +import { Changelog, type ChangelogItem } from "components/dashboard/Changelog"; +import { HomeProductCard } from "components/dashboard/HomeProductCard"; +import { OnboardingSteps } from "components/onboarding/Steps"; +import { PRODUCTS } from "components/product-pages/common/nav/data"; + +const TRACKING_CATEGORY = "dashboard"; + +export default async function Page() { + const changelog = await getChangelog(); + + return ( +
+
+

+ Get started quickly +

+
+ +
+ {["connect", "contracts", "infrastructure"].map((section) => { + const products = PRODUCTS.filter( + (p) => p.section === section && !!p.dashboardLink, + ); + + return ( +
+

+ {section === "infrastructure" ? "Engine" : section} +

+
+ {products.map((product) => ( + + ))} +
+
+ ); + })} +
+
+
+
+

+ Latest changes +

+ +
+
+ ); +} + +async function getChangelog() { + const res = await fetch( + "https://thirdweb.ghost.io/ghost/api/content/posts/?key=49c62b5137df1c17ab6b9e46e3&fields=title,url,published_at&filter=tag:changelog&visibility:public&limit=5", + ); + const json = await res.json(); + return json.posts as ChangelogItem[]; +} + +// revalidate every 5 minutes +export const revalidate = 300; diff --git a/apps/dashboard/src/components/dashboard/Changelog.tsx b/apps/dashboard/src/components/dashboard/Changelog.tsx index 093c5b2af61..c618c59239f 100644 --- a/apps/dashboard/src/components/dashboard/Changelog.tsx +++ b/apps/dashboard/src/components/dashboard/Changelog.tsx @@ -1,7 +1,7 @@ import { Skeleton } from "@/components/ui/skeleton"; import { formatDistance } from "date-fns/formatDistance"; import { ArrowRightIcon } from "lucide-react"; -import { Link } from "tw-components"; +import Link from "next/link"; import { ClientOnly } from "../ClientOnly/ClientOnly"; export interface ChangelogItem { @@ -23,7 +23,7 @@ export const Changelog: React.FC = ({ changelog }) => {
= ({ changelog }) => { {item.title}
- }> + }> {formatDistance(new Date(item.published_at), Date.now(), { addSuffix: true, })} @@ -42,7 +42,7 @@ export const Changelog: React.FC = ({ changelog }) => { ))} View More diff --git a/apps/dashboard/src/components/dashboard/HomeProductCard.tsx b/apps/dashboard/src/components/dashboard/HomeProductCard.tsx index c82dc954fc5..5803619aa01 100644 --- a/apps/dashboard/src/components/dashboard/HomeProductCard.tsx +++ b/apps/dashboard/src/components/dashboard/HomeProductCard.tsx @@ -1,8 +1,8 @@ -import { Flex, LinkBox, LinkOverlay } from "@chakra-ui/react"; -import { ChakraNextImage } from "components/Image"; +"use client"; import type { SectionItemProps } from "components/product-pages/common/nav/types"; import { useTrack } from "hooks/analytics/useTrack"; -import { Card, Text } from "tw-components"; +import Image from "next/image"; +import Link from "next/link"; interface HomeProductCardProps { product: SectionItemProps; @@ -17,43 +17,35 @@ export const HomeProductCard: React.FC = ({ }) => { const trackEvent = useTrack(); return ( - { - trackEvent({ - category: TRACKING_CATEGORY, - action: "click", - label: "select-product", - product: product.name, - }); - }} - > - - - - {product.icon && ( - - )} - - - {isFromLandingPage - ? product.name - : product?.dashboardName || product.name} - - - - - {product.description} - - - - +
+ {product.icon && ( +
+ +
+ )} +
+ { + trackEvent({ + category: TRACKING_CATEGORY, + action: "click", + label: "select-product", + product: product.name, + }); + }} + > + {isFromLandingPage + ? product.name + : product?.dashboardName || product.name} + +

+ {product.description} +

+
+
); }; diff --git a/apps/dashboard/src/components/onboarding/Steps.tsx b/apps/dashboard/src/components/onboarding/Steps.tsx index 0f8878eca53..adb424a649a 100644 --- a/apps/dashboard/src/components/onboarding/Steps.tsx +++ b/apps/dashboard/src/components/onboarding/Steps.tsx @@ -1,5 +1,7 @@ "use client"; -import { cn } from "@/lib/utils"; + +import { Button } from "@/components/ui/button"; +import { useDashboardRouter } from "@/lib/DashboardRouter"; import { CustomConnectWallet } from "@3rdweb-sdk/react/components/connect-wallet"; import { AccountStatus, @@ -8,27 +10,28 @@ import { useApiKeys, } from "@3rdweb-sdk/react/hooks/useApi"; import { useLoggedInUser } from "@3rdweb-sdk/react/hooks/useLoggedInUser"; -import { Flex, useBreakpointValue } from "@chakra-ui/react"; -import { ChakraNextImage } from "components/Image"; import { OPSponsoredChains } from "constants/chains"; import { useTrack } from "hooks/analytics/useTrack"; import { useLocalStorage } from "hooks/useLocalStorage"; +import { ExternalLinkIcon } from "lucide-react"; import { useTheme } from "next-themes"; import type { StaticImageData } from "next/image"; -import { useRouter } from "next/router"; +import Image from "next/image"; +import Link from "next/link"; import { useEffect, useMemo } from "react"; import { useActiveWalletChain } from "thirdweb/react"; -import { Button, Card, Heading, Link, LinkButton, Text } from "tw-components"; -enum Step { - Keys = "keys", - Docs = "docs", - OptimismCredits = "optimismCredits", - Payment = "payment", -} +const Step = { + keys: "keys", + docs: "docs", + optimismCredits: "optimismCredits", + payment: "payment", +} as const; + +type StepId = keyof typeof Step; type StepData = { - key: Step; + key: StepId; title: string; description: string | JSX.Element; cta: string; @@ -47,11 +50,10 @@ interface OnboardingStepsProps { export const OnboardingSteps: React.FC = ({ onlyOptimism, }) => { - const isMobile = useBreakpointValue({ base: true, md: false }); const { isLoggedIn } = useLoggedInUser(); const meQuery = useAccount(); const apiKeysQuery = useApiKeys(); - const router = useRouter(); + const router = useDashboardRouter(); const trackEvent = useTrack(); const { theme } = useTheme(); const { data: credits } = useAccountCredits(); @@ -89,9 +91,9 @@ export const OnboardingSteps: React.FC = ({ } }, [chainId]); - const currentStep = useMemo(() => { + const currentStep: StepId | null = useMemo(() => { if (onlyOptimism && (!hasAppliedForOpGrant || !opCredit)) { - return Step.OptimismCredits; + return Step.optimismCredits; } if (!isLoggedIn) { @@ -99,16 +101,16 @@ export const OnboardingSteps: React.FC = ({ } if (isSponsoredChain && (!hasAppliedForOpGrant || !opCredit)) { - return Step.OptimismCredits; + return Step.optimismCredits; } if (!onboardingKeys && !hasApiKeys) { - return Step.Keys; + return Step.keys; } if (!hasValidPayment && !onboardingPaymentMethod) { - return Step.Payment; + return Step.payment; } if (!onboardingDocs) { - return Step.Docs; + return Step.docs; } return null; }, [ @@ -131,7 +133,7 @@ export const OnboardingSteps: React.FC = ({ onClick, }: { isSkip?: true; - step: Step; + step: StepId; href?: string; onClick?: () => void; }) => { @@ -151,15 +153,15 @@ export const OnboardingSteps: React.FC = ({ onClick(); } - if (step === Step.Keys) { + if (step === Step.keys) { setOnboardingKeys(true); } - if (step === Step.Docs) { + if (step === Step.docs) { setOnboardingDocs(true); } - if (step === Step.Payment) { + if (step === Step.payment) { setOnboardingPaymentMethod(true); } @@ -185,7 +187,7 @@ export const OnboardingSteps: React.FC = ({ const STEPS: StepData[] = useMemo( () => [ { - key: Step.Keys, + key: Step.keys, title: "Create an API Key", description: "An API key is required to use thirdweb's services through the SDK and CLI.", @@ -194,7 +196,7 @@ export const OnboardingSteps: React.FC = ({ canSkip: true, }, { - key: Step.Payment, + key: Step.payment, title: "Add Payment Method", description: "Add your payment method to ensure no disruption to thirdweb services when you exceed free monthly limits.", @@ -203,31 +205,29 @@ export const OnboardingSteps: React.FC = ({ canSkip: true, }, { - key: Step.OptimismCredits, + key: Step.optimismCredits, title: "Apply to join the Optimism Superchain App Accelerator!", description: ( - - - Successful applicants will receive gas grants which can be used - across all supported{" "} - - Optimism Superchain networks - - . These can be used with our Account Abstraction tools to sponsor - gas fees for any on-chain activity. - - +

+ Successful applicants will receive gas grants which can be used + across all supported{" "} + + Optimism Superchain networks + + . These can be used with our Account Abstraction tools to sponsor + gas fees for any on-chain activity. +

), cta: "Apply now", onClick: () => { trackEvent({ category: "onboardingChecklist", action: "clicked", - data: { step: Step.OptimismCredits }, + data: { step: Step.optimismCredits }, }); }, href: "/dashboard/settings/gas-credits", @@ -237,7 +237,7 @@ export const OnboardingSteps: React.FC = ({ rightImageLight: require("../../../public/assets/dashboard/optimism-credits-light.png"), }, { - key: Step.Docs, + key: Step.docs, title: "Explore Docs", description: "Read our documentation to learn what you can build with contracts, payments, wallets, and infrastructure.", @@ -266,28 +266,15 @@ export const OnboardingSteps: React.FC = ({ } = STEPS.find((s) => s.key === currentStep) as StepData; return ( - -
- {title} - {description} -
+
+
+

{title}

+

{description}

+
+ +
{isLoggedIn ? ( )} + {canSkip && (
- {rightImageDark && !isMobile && theme === "dark" && ( - + + {rightImageDark && theme === "dark" && ( + )} - {rightImageLight && !isMobile && theme === "light" && ( - + + {rightImageLight && theme === "light" && ( + )} - +
); }; diff --git a/apps/dashboard/src/page-id.ts b/apps/dashboard/src/page-id.ts index 1ccf2098398..9db6c4ca7c3 100644 --- a/apps/dashboard/src/page-id.ts +++ b/apps/dashboard/src/page-id.ts @@ -90,8 +90,6 @@ export enum PageId { // --------------------------------------------------------------------------- // general product pages // --------------------------------------------------------------------------- - // thirdweb.com/dashboard - Dashboard = "dashboard", // thirdweb.com/dashboard/infrastructure/storage DashboardSettingsStorage = "dashboard-storage", diff --git a/apps/dashboard/src/pages/dashboard/index.tsx b/apps/dashboard/src/pages/dashboard/index.tsx deleted file mode 100644 index 62b2c53bbcd..00000000000 --- a/apps/dashboard/src/pages/dashboard/index.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { Flex, GridItem, SimpleGrid } from "@chakra-ui/react"; -import { AppLayout } from "components/app-layouts/app"; -import { Changelog, type ChangelogItem } from "components/dashboard/Changelog"; -import { HomeProductCard } from "components/dashboard/HomeProductCard"; -import { OnboardingSteps } from "components/onboarding/Steps"; -import { PRODUCTS } from "components/product-pages/common/nav/data"; -import type { GetStaticProps, InferGetStaticPropsType } from "next"; -import { PageId } from "page-id"; -import { Heading } from "tw-components"; -import type { ThirdwebNextPage } from "utils/types"; - -const TRACKING_CATEGORY = "dashboard"; - -const Dashboard: ThirdwebNextPage = ( - props: InferGetStaticPropsType, -) => { - return ( - - {/* Any announcements: */} - - - Get started quickly -
- - - {["connect", "contracts", "infrastructure"].map((section) => { - const products = PRODUCTS.filter( - (p) => p.section === section && !!p.dashboardLink, - ); - - return ( - - - {section === "infrastructure" ? "Engine" : section} - - - {products.map((product) => ( - - ))} - - - ); - })} - -
-
- - Latest changes - - -
-
- ); -}; - -Dashboard.getLayout = (page, props) => {page}; -Dashboard.pageId = PageId.Dashboard; - -// server-side -type DashboardProps = { - changelog: ChangelogItem[]; -}; - -export const getStaticProps: GetStaticProps = async () => { - const res = await fetch( - "https://thirdweb.ghost.io/ghost/api/content/posts/?key=49c62b5137df1c17ab6b9e46e3&fields=title,url,published_at&filter=tag:changelog&visibility:public&limit=5", - ); - const json = await res.json(); - - return { - props: { changelog: json.posts }, - // revalidate once an hour - revalidate: 3600, - }; -}; - -export default Dashboard;