Skip to content

Commit fa826b5

Browse files
committed
Add Teams UI Banners (#4936)
## Problem solved Short description of the bug fixed or feature added <!-- start pr-codex --> --- ## PR-Codex overview This PR introduces a new `DashboardTypeCookieSetter` component to manage dashboard type cookies and updates various parts of the application to utilize this functionality. It also adds new banner components for improved user notifications. ### Detailed summary - Added `DashboardTypeCookieSetter` component to set dashboard type cookies. - Integrated `DashboardTypeCookieSetter` in `AppShell`, `RootTeamLayout`, and `AccountLayout`. - Created `TeamsUIBanner` and `TryTeamsUIBanner` components for user announcements. - Updated routing logic in `login/page.tsx` based on cookie value. - Modified `AnnouncementBanner` for better usability and appearance. - Added a new button in `HeroSection` to redirect based on cookie value. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent dce2ca0 commit fa826b5

File tree

11 files changed

+147
-87
lines changed

11 files changed

+147
-87
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"use client";
2+
3+
import { useEffect } from "react";
4+
import { setCookie } from "../../stores/SyncStoreToCookies";
5+
6+
export function DashboardTypeCookieSetter(props: {
7+
type: "team" | "old";
8+
}) {
9+
// eslint-disable-next-line no-restricted-syntax
10+
useEffect(() => {
11+
setCookie("x-dashboard-type", props.type);
12+
}, [props.type]);
13+
14+
return null;
15+
}

apps/dashboard/src/app/(dashboard)/layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { AppFooter } from "@/components/blocks/app-footer";
22
import { ErrorProvider } from "../../contexts/error-handler";
3+
import { TryTeamsUIBanner } from "../components/DashboardTypeBanner";
34
import { DashboardHeader } from "../components/Header/DashboardHeader";
45

56
export default function DashboardLayout(props: { children: React.ReactNode }) {
67
return (
78
<ErrorProvider>
89
<div className="flex min-h-screen flex-col bg-background">
10+
<TryTeamsUIBanner />
911
<DashboardHeader />
1012
<main className="grow">{props.children}</main>
1113
<AppFooter />

apps/dashboard/src/app/account/layout.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { getProjects } from "@/api/projects";
22
import { getTeams } from "@/api/team";
3+
import { DashboardTypeCookieSetter } from "@/components/DashboardTypeCookieSetter";
34
import { SidebarLayout } from "@/components/blocks/SidebarLayout";
45
import { AppFooter } from "@/components/blocks/app-footer";
56
import type React from "react";
7+
import { TeamsUIBanner } from "../components/DashboardTypeBanner";
68
import { TWAutoConnect } from "../components/autoconnect";
79
import { AccountHeader } from "./components/AccountHeader";
810

@@ -11,6 +13,7 @@ export default async function AccountLayout(props: {
1113
}) {
1214
return (
1315
<div className="flex min-h-screen flex-col bg-background">
16+
<TeamsUIBanner />
1417
<div className="flex grow flex-col">
1518
<HeaderAndNav />
1619
<div className="border-border border-b py-10">
@@ -42,6 +45,7 @@ export default async function AccountLayout(props: {
4245
</SidebarLayout>
4346
</div>
4447
<TWAutoConnect />
48+
<DashboardTypeCookieSetter type="team" />
4549
<AppFooter />
4650
</div>
4751
);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { AnnouncementBanner } from "../../components/notices/AnnouncementBanner";
2+
3+
export function TeamsUIBanner() {
4+
return (
5+
<AnnouncementBanner
6+
label="You are exploring the new dashboard. Click here to go back to the legacy dashboard."
7+
href="/dashboard"
8+
trackingLabel="old-dashboard"
9+
/>
10+
);
11+
}
12+
13+
export function TryTeamsUIBanner() {
14+
return (
15+
<AnnouncementBanner
16+
label="Explore the new teams dashboard. Now in Beta."
17+
href="/team"
18+
trackingLabel="team-dashboard"
19+
/>
20+
);
21+
}

apps/dashboard/src/app/login/page.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { useTheme } from "next-themes";
77
import { useSearchParams } from "next/navigation";
88
import { Suspense, useEffect, useState } from "react";
99
import { ConnectEmbed } from "thirdweb/react";
10+
import { getCookie } from "../../stores/SyncStoreToCookies";
1011
import { ThirdwebMiniLogo } from "../components/ThirdwebMiniLogo";
1112
import { getSDKTheme } from "../components/sdk-component-theme";
1213
import { doLogin, doLogout, getLoginPayload, isLoggedIn } from "./auth-actions";
@@ -48,7 +49,12 @@ function CustomConnectEmmbed() {
4849
if (nextSearchParam && isValidRedirectPath(nextSearchParam)) {
4950
router.replace(nextSearchParam);
5051
} else {
51-
router.replace("/dashboard");
52+
const dashboardType = getCookie("x-dashboard-type");
53+
if (dashboardType === "team") {
54+
router.replace("/team");
55+
} else {
56+
router.replace("/dashboard");
57+
}
5258
}
5359
}
5460

apps/dashboard/src/app/team/[team_slug]/layout.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { DashboardTypeCookieSetter } from "@/components/DashboardTypeCookieSetter";
12
import { AppFooter } from "@/components/blocks/app-footer";
3+
import { TeamsUIBanner } from "../../components/DashboardTypeBanner";
24
import { TWAutoConnect } from "../../components/autoconnect";
35

46
export default function RootTeamLayout(props: {
@@ -7,9 +9,11 @@ export default function RootTeamLayout(props: {
79
}) {
810
return (
911
<div className="flex min-h-screen flex-col">
12+
<TeamsUIBanner />
1013
<div className="flex grow flex-col">{props.children}</div>
1114
<TWAutoConnect />
1215
<AppFooter />
16+
<DashboardTypeCookieSetter type="team" />
1317
</div>
1418
);
1519
}

apps/dashboard/src/components/homepage/sections/HeroSection.tsx

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { Flex, Icon, SimpleGrid } from "@chakra-ui/react";
22
import { ChakraNextImage } from "components/Image";
33
import { HomepageSection } from "components/product-pages/homepage/HomepageSection";
4+
import { useEffect, useState } from "react";
45
import { BsFillLightningChargeFill } from "react-icons/bs";
56
import { Heading, Text, TrackedLink, TrackedLinkButton } from "tw-components";
7+
import { getCookie } from "../../../stores/SyncStoreToCookies";
68
import { Aurora } from "../Aurora";
79
import styles from "../category/categories.module.css";
810
import { OpenSource } from "../open-source/OpenSource";
@@ -49,25 +51,7 @@ export const HeroSection = ({ TRACKING_CATEGORY }: HeroSectionProps) => {
4951
flexDirection={{ base: "column", sm: "row" }}
5052
gap={{ base: 4, md: 6 }}
5153
>
52-
<TrackedLinkButton
53-
leftIcon={<Icon as={BsFillLightningChargeFill} boxSize={4} />}
54-
py={6}
55-
px={8}
56-
w="full"
57-
bgColor="white"
58-
_hover={{
59-
bgColor: "white",
60-
opacity: 0.8,
61-
}}
62-
color="black"
63-
href="/dashboard"
64-
category={TRACKING_CATEGORY}
65-
label="get-started"
66-
fontWeight="bold"
67-
maxW={{ base: "full", sm: "fit-content" }}
68-
>
69-
Get started
70-
</TrackedLinkButton>
54+
<GetStartedButtonLink trackingCategory={TRACKING_CATEGORY} />
7155

7256
<TrackedLinkButton
7357
variant="outline"
@@ -153,3 +137,39 @@ export const HeroSection = ({ TRACKING_CATEGORY }: HeroSectionProps) => {
153137
</HomepageSection>
154138
);
155139
};
140+
141+
function GetStartedButtonLink(props: {
142+
trackingCategory: string;
143+
showTeamLayout?: boolean;
144+
}) {
145+
// using state+effect here to avoid hydration errors
146+
const [showTeamLayout, setShowTeamLayout] = useState(false);
147+
// eslint-disable-next-line no-restricted-syntax
148+
useEffect(() => {
149+
if (getCookie("x-dashboard-type") === "team") {
150+
setShowTeamLayout(true);
151+
}
152+
}, []);
153+
154+
return (
155+
<TrackedLinkButton
156+
leftIcon={<Icon as={BsFillLightningChargeFill} boxSize={4} />}
157+
py={6}
158+
px={8}
159+
w="full"
160+
bgColor="white"
161+
_hover={{
162+
bgColor: "white",
163+
opacity: 0.8,
164+
}}
165+
color="black"
166+
href={showTeamLayout ? "/team" : "/dashboard"}
167+
category={props.trackingCategory}
168+
label="get-started"
169+
fontWeight="bold"
170+
maxW={{ base: "full", sm: "fit-content" }}
171+
>
172+
Get started
173+
</TrackedLinkButton>
174+
);
175+
}

apps/dashboard/src/components/layout/app-shell/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { DashboardTypeCookieSetter } from "@/components/DashboardTypeCookieSetter";
12
import { AppFooter } from "@/components/blocks/app-footer";
23
import { cn } from "@/lib/utils";
34
import { Container } from "@chakra-ui/react";
@@ -21,6 +22,7 @@ export const AppShell: ComponentWithChildren<AppShellProps> = ({
2122
}) => {
2223
return (
2324
<div className="bg-background">
25+
<DashboardTypeCookieSetter type="old" />
2426
<DashboardHeader />
2527
<main
2628
className={cn("min-h-screen py-6 md:pt-10 md:pb-20", mainClassName)}
Lines changed: 38 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,51 @@
1-
import { Box, Container, Flex, Icon, IconButton } from "@chakra-ui/react";
1+
"use client";
2+
import { Button } from "@/components/ui/button";
3+
import { TrackedLinkTW } from "@/components/ui/tracked-link";
24
import { useLocalStorage } from "hooks/useLocalStorage";
3-
import { FiArrowRight, FiX } from "react-icons/fi";
4-
import { Heading, TrackedLink } from "tw-components";
5+
import { ChevronRightIcon, XIcon } from "lucide-react";
56

6-
export const AnnouncementBanner = () => {
7+
export function AnnouncementBanner(props: {
8+
href: string;
9+
label: string;
10+
trackingLabel: string;
11+
}) {
712
const [hasDismissedAnnouncement, setHasDismissedAnnouncement] =
8-
useLocalStorage("dismissed-modular-contracts-announcement", false, true);
13+
useLocalStorage(`dismissed-${props.trackingLabel}`, false, true);
914

1015
if (hasDismissedAnnouncement) {
1116
return null;
1217
}
1318

1419
return (
15-
<Box
16-
position="sticky"
17-
zIndex="10"
18-
py={3}
19-
bgImage="linear-gradient(145.96deg, #410AB6 5.07%, #7bdefe 100%)"
20+
<div
21+
className="fade-in-0 relative w-full animate-in bg-background py-2.5 pr-14 duration-400"
22+
style={{
23+
backgroundImage:
24+
"linear-gradient(145deg, hsl(290deg 85% 50%), hsl(220deg 85% 50%))",
25+
}}
2026
>
21-
<Flex
22-
w="full"
23-
justifyContent="space-between"
24-
alignItems="center"
25-
gap={{ base: 1, md: 2 }}
26-
px={4}
27+
<TrackedLinkTW
28+
href={props.href}
29+
category="announcement"
30+
label={props.trackingLabel}
31+
target={props.href.startsWith("http") ? "_blank" : undefined}
32+
className="container flex cursor-pointer items-center gap-2 lg:justify-center"
2733
>
28-
<Box display={{ base: "none", md: "block" }} />
29-
<TrackedLink
30-
href="https://thirdweb.com/explore/modular-contracts"
31-
category="announcement"
32-
label="onchain-olympics"
33-
isExternal
34-
>
35-
<Container maxW="container.page" display="flex" px={0}>
36-
<Flex
37-
cursor="pointer"
38-
mx="auto"
39-
align="center"
40-
gap={{ base: 0.5, md: 2 }}
41-
color="white"
42-
>
43-
<Heading
44-
size="label.lg"
45-
as="p"
46-
lineHeight={{ base: 1.5, md: undefined }}
47-
color="white"
48-
fontWeight={500}
49-
>
50-
Modular Contracts Beta: Secure, Customizable, and Easy to
51-
Integrate contracts
52-
</Heading>
53-
<Icon display={{ base: "none", md: "block" }} as={FiArrowRight} />
54-
</Flex>
55-
</Container>
56-
</TrackedLink>
34+
<span className="inline-block font-semibold text-white leading-normal hover:underline">
35+
{props.label}
36+
</span>
37+
<ChevronRightIcon className="hidden size-5 opacity-80 lg:block" />
38+
</TrackedLinkTW>
5739

58-
<IconButton
59-
size="xs"
60-
aria-label="Close announcement"
61-
icon={<FiX />}
62-
colorScheme="blackAlpha"
63-
color={{ base: "white", md: "black" }}
64-
variant="ghost"
65-
onClick={() => setHasDismissedAnnouncement(true)}
66-
/>
67-
</Flex>
68-
</Box>
40+
<Button
41+
size="icon"
42+
variant="ghost"
43+
onClick={() => setHasDismissedAnnouncement(true)}
44+
aria-label="Close announcement"
45+
className="-translate-y-1/2 !text-white absolute top-1/2 right-2 h-auto w-auto p-2 hover:bg-white/15"
46+
>
47+
<XIcon className="size-5" />
48+
</Button>
49+
</div>
6950
);
70-
};
51+
}

apps/dashboard/src/pages/_app.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import type { ThirdwebNextPage } from "utils/types";
2222
import chakraTheme from "../theme";
2323
import "@/styles/globals.css";
2424
import { DashboardRouterTopProgressBar } from "@/lib/DashboardRouter";
25-
import { AnnouncementBanner } from "../components/notices/AnnouncementBanner";
25+
import { TryTeamsUIBanner } from "../app/components/DashboardTypeBanner";
2626

2727
const inter = interConstructor({
2828
subsets: ["latin"],
@@ -261,9 +261,10 @@ const ConsoleApp = memo(function ConsoleApp({
261261

262262
<DashboardRouterTopProgressBar />
263263

264+
<TryTeamsUIBanner />
265+
264266
<TailwindTheme>
265267
<ChakraProvider theme={chakraThemeWithFonts}>
266-
<AnnouncementBanner />
267268
{isFallback && Component.fallback
268269
? Component.fallback
269270
: getLayout(<Component {...pageProps} />, pageProps)}

0 commit comments

Comments
 (0)