diff --git a/apps/dashboard/src/@/components/blocks/MobileSidebar.tsx b/apps/dashboard/src/@/components/blocks/MobileSidebar.tsx index 03de6d1c826..0e469c295ce 100644 --- a/apps/dashboard/src/@/components/blocks/MobileSidebar.tsx +++ b/apps/dashboard/src/@/components/blocks/MobileSidebar.tsx @@ -19,30 +19,7 @@ export function MobileSidebar(props: { triggerClassName?: string; }) { const [isOpen, setIsOpen] = useState(false); - const pathname = usePathname(); - - const activeLink = useMemo(() => { - function isActive(link: SidebarBaseLink) { - if (link.exactMatch) { - return link.href === pathname; - } - return pathname?.startsWith(link.href); - } - - for (const link of props.links) { - if ("group" in link) { - for (const subLink of link.links) { - if (isActive(subLink)) { - return subLink; - } - } - } else { - if (isActive(link)) { - return link; - } - } - } - }, [props.links, pathname]); + const activeLink = useActiveSidebarLink(props.links); const defaultTrigger = ( + ); +}); +SidebarTrigger.displayName = "SidebarTrigger"; + +const SidebarRail = React.forwardRef< + HTMLButtonElement, + React.ComponentProps<"button"> +>(({ className, ...props }, ref) => { + const { toggleSidebar } = useSidebar(); + + return ( + - - +
+
+

Contracts

+
+
+ +
diff --git a/apps/dashboard/src/app/account/contracts/_components/DeployedContractsPage.tsx b/apps/dashboard/src/app/account/contracts/_components/DeployedContractsPage.tsx index 4ba073ad0c3..5c0dd8acb0a 100644 --- a/apps/dashboard/src/app/account/contracts/_components/DeployedContractsPage.tsx +++ b/apps/dashboard/src/app/account/contracts/_components/DeployedContractsPage.tsx @@ -12,13 +12,12 @@ export function DeployedContractsPage(props: { authToken: string; }) { return ( -
+
-
-
+
}> diff --git a/apps/dashboard/src/app/account/layout.tsx b/apps/dashboard/src/app/account/layout.tsx index 8e1764ba4d8..2bcd68c066b 100644 --- a/apps/dashboard/src/app/account/layout.tsx +++ b/apps/dashboard/src/app/account/layout.tsx @@ -4,6 +4,7 @@ import { AppFooter } from "@/components/blocks/app-footer"; import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; import type React from "react"; import { TabPathLinks } from "../../@/components/ui/tabs"; +import { AnnouncementBanner } from "../../components/notices/AnnouncementBanner"; import { getAuthTokenWalletAddress } from "../api/lib/getAuthToken"; import { TWAutoConnect } from "../components/autoconnect"; import { loginRedirect } from "../login/loginRedirect"; @@ -53,6 +54,7 @@ async function HeaderAndNav(props: { return (
+ - {children} diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/layout.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/layout.tsx index 6d7cc440ea9..a0a22d7ca21 100644 --- a/apps/dashboard/src/app/team/[team_slug]/(team)/layout.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/(team)/layout.tsx @@ -1,7 +1,9 @@ import { getProjects } from "@/api/projects"; import { getTeamNebulaWaitList, getTeams } from "@/api/team"; +import { AppFooter } from "@/components/blocks/app-footer"; import { TabPathLinks } from "@/components/ui/tabs"; import { redirect } from "next/navigation"; +import { AnnouncementBanner } from "../../../../components/notices/AnnouncementBanner"; import { getValidAccount } from "../../../account/settings/getAccount"; import { getAuthTokenWalletAddress } from "../../../api/lib/getAuthToken"; import { TeamHeaderLoggedIn } from "../../components/TeamHeader/team-header-logged-in.client"; @@ -42,6 +44,7 @@ export default async function TeamLayout(props: { return (
+
{props.children}
+
); } diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/ProjectSidebarLayout.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/ProjectSidebarLayout.tsx new file mode 100644 index 00000000000..b6d7762de81 --- /dev/null +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/components/ProjectSidebarLayout.tsx @@ -0,0 +1,97 @@ +"use client"; +import { FullWidthSidebarLayout } from "@/components/blocks/SidebarLayout"; +import { AppFooter } from "@/components/blocks/app-footer"; +import { + BookTextIcon, + BoxIcon, + HomeIcon, + SettingsIcon, + WalletIcon, +} from "lucide-react"; +import { ContractIcon } from "../../../../(dashboard)/(chain)/components/server/icons/ContractIcon"; +import { InsightIcon } from "../../../../(dashboard)/(chain)/components/server/icons/InsightIcon"; +import { PayIcon } from "../../../../(dashboard)/(chain)/components/server/icons/PayIcon"; +import { SmartAccountIcon } from "../../../../(dashboard)/(chain)/components/server/icons/SmartAccountIcon"; + +export function ProjectSidebarLayout(props: { + layoutPath: string; + children: React.ReactNode; +}) { + const { layoutPath, children } = props; + + const tracking = (label: string) => ({ + category: "project-sidebar", + action: "click", + label, + }); + + return ( + } + contentSidebarLinks={[ + { + href: layoutPath, + exactMatch: true, + label: "Overview", + icon: HomeIcon, + tracking: tracking("overview"), + }, + { + label: "In-App Wallets", + href: `${layoutPath}/connect/in-app-wallets`, + icon: WalletIcon, + tracking: tracking("in-app-wallets"), + }, + { + label: "Account Abstraction", + href: `${layoutPath}/connect/account-abstraction`, + icon: SmartAccountIcon, + tracking: tracking("account-abstraction"), + }, + { + label: "Universal Bridge", + href: `${layoutPath}/connect/universal-bridge`, + icon: PayIcon, + tracking: tracking("universal-bridge"), + }, + { + href: `${layoutPath}/contracts`, + label: "Contracts", + icon: ContractIcon, + tracking: tracking("contracts"), + }, + { + href: `${layoutPath}/insight`, + label: "Insight", + icon: InsightIcon, + tracking: tracking("insight"), + }, + ]} + footerSidebarLinks={[ + { + href: `${layoutPath}/settings`, + label: "Project Settings", + icon: SettingsIcon, + tracking: tracking("project-settings"), + }, + { + separator: true, + }, + { + href: "https://portal.thirdweb.com", + label: "Documentation", + icon: BookTextIcon, + tracking: tracking("documentation"), + }, + { + href: "https://playground.thirdweb.com/connect/sign-in/button", + label: "Playground", + icon: BoxIcon, + tracking: tracking("playground"), + }, + ]} + > + {children} + + ); +} diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/layout.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/layout.tsx deleted file mode 100644 index 7b8834f49eb..00000000000 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/layout.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import type { SidebarLink } from "@/components/blocks/Sidebar"; -import { SidebarLayout } from "@/components/blocks/SidebarLayout"; - -export default async function Layout(props: { - params: Promise<{ - team_slug: string; - project_slug: string; - }>; - children: React.ReactNode; -}) { - const { team_slug, project_slug } = await props.params; - - const links: SidebarLink[] = [ - { - label: "In-App Wallets", - href: `/team/${team_slug}/${project_slug}/connect/in-app-wallets`, - }, - { - label: "Account Abstraction", - href: `/team/${team_slug}/${project_slug}/connect/account-abstraction`, - }, - { - label: "Universal Bridge", - href: `/team/${team_slug}/${project_slug}/connect/universal-bridge`, - }, - { - label: "Playground", - href: "https://playground.thirdweb.com/connect/sign-in/button", - }, - ]; - - return {props.children}; -} diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/insight/page.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/insight/page.tsx index d8b6d4c7f90..30daf00d49a 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/insight/page.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/insight/page.tsx @@ -10,15 +10,11 @@ import Link from "next/link"; export default async function Page() { return (
-
-
-

- Insight -

-
-
+

+ Insight +

-
+
diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/layout.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/layout.tsx index eeb3c1ed4b2..42e106cf3e0 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/layout.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/layout.tsx @@ -1,11 +1,13 @@ import { getProjects } from "@/api/projects"; import { getTeams } from "@/api/team"; +import { SidebarProvider } from "@/components/ui/sidebar"; import { redirect } from "next/navigation"; +import { AnnouncementBanner } from "../../../../components/notices/AnnouncementBanner"; import { getValidAccount } from "../../../account/settings/getAccount"; import { getAuthTokenWalletAddress } from "../../../api/lib/getAuthToken"; import { TeamHeaderLoggedIn } from "../../components/TeamHeader/team-header-logged-in.client"; +import { ProjectSidebarLayout } from "./components/ProjectSidebarLayout"; import { SaveLastUsedProject } from "./components/SaveLastUsedProject"; -import { ProjectTabs } from "./tabs"; export default async function TeamLayout(props: { children: React.ReactNode; @@ -47,22 +49,26 @@ export default async function TeamLayout(props: { redirect(`/team/${params.team_slug}`); } + const layoutPath = `/team/${params.team_slug}/${params.project_slug}`; + return ( -
-
- - + +
+
+ + +
+ + {props.children} +
-
{props.children}
-
+ ); } diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/page.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/page.tsx index 5de6e1a3ea4..b62b254b309 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/page.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/page.tsx @@ -29,13 +29,14 @@ import { getChainMetadata, } from "thirdweb/chains"; import { type WalletId, getWalletInfo } from "thirdweb/wallets"; -import { AnalyticsHeader } from "../../components/Analytics/AnalyticsHeader"; import { CombinedBarChartCard } from "../../components/Analytics/CombinedBarChartCard"; import { PieChartCard } from "../../components/Analytics/PieChartCard"; import { ProjectFTUX } from "./components/ProjectFTUX/ProjectFTUX"; import { RpcMethodBarChartCard } from "./components/RpcMethodBarChartCard"; import { TransactionsCharts } from "./components/Transactions"; +import { RangeSelector } from "components/analytics/range-selector"; + interface PageParams { team_slug: string; project_slug: string; @@ -80,8 +81,8 @@ export default async function ProjectOverviewPage(props: PageProps) { return (
-
- +
{!isActive ? ( -
+
) : ( -
+
}> )} -
+
{walletConnections.status === "fulfilled" && walletConnections.value.length > 0 ? ( @@ -461,3 +462,23 @@ async function TotalSponsoredCard({ /> ); } + +export function Header(props: { + title: string; + interval: "day" | "week"; + range: Range; + showRangeSelector: boolean; +}) { + const { title, interval, range, showRangeSelector } = props; + + return ( +
+
+

+ {title} +

+
+ {showRangeSelector && } +
+ ); +} diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/layout.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/layout.tsx index 8b96f66e488..c8276a89e3c 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/layout.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/layout.tsx @@ -7,14 +7,10 @@ export default async function Layout(props: { }) { return (
-
-
-

- Project Settings -

-
-
-
{props.children}
+

+ Project Settings +

+
{props.children}
); } diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/tabs.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/tabs.tsx deleted file mode 100644 index 90da40e513d..00000000000 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/tabs.tsx +++ /dev/null @@ -1,41 +0,0 @@ -"use client"; - -import { TabPathLinks } from "@/components/ui/tabs"; - -export function ProjectTabs(props: { - layoutPath: string; -}) { - const { layoutPath } = props; - - return ( - path.startsWith(`${layoutPath}/connect`), - }, - { - path: `${layoutPath}/contracts`, - name: "Contracts", - }, - - { - path: `${layoutPath}/insight`, - name: "Insight", - }, - { - path: `${layoutPath}/settings`, - name: "Settings", - }, - ]} - /> - ); -} diff --git a/apps/dashboard/src/app/team/[team_slug]/layout.tsx b/apps/dashboard/src/app/team/[team_slug]/layout.tsx index 45464173d5c..1dcbe0e8613 100644 --- a/apps/dashboard/src/app/team/[team_slug]/layout.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/layout.tsx @@ -1,5 +1,4 @@ import { getTeamBySlug } from "@/api/team"; -import { AppFooter } from "@/components/blocks/app-footer"; import { redirect } from "next/navigation"; import { isTeamOnboardingComplete } from "../../login/onboarding/isOnboardingRequired"; import { SaveLastVisitedTeamPage } from "../components/last-visited-page/SaveLastVisitedPage"; @@ -37,7 +36,6 @@ export default async function RootTeamLayout(props: { {props.children}
-
); diff --git a/apps/dashboard/src/components/notices/AnnouncementBanner.tsx b/apps/dashboard/src/components/notices/AnnouncementBanner.tsx index 106eb5ce637..23736f63ef7 100644 --- a/apps/dashboard/src/components/notices/AnnouncementBanner.tsx +++ b/apps/dashboard/src/components/notices/AnnouncementBanner.tsx @@ -1,27 +1,19 @@ "use client"; + import { Button } from "@/components/ui/button"; import { TrackedLinkTW } from "@/components/ui/tracked-link"; import { useLocalStorage } from "hooks/useLocalStorage"; -import { CircleAlertIcon, XIcon } from "lucide-react"; -import { useSelectedLayoutSegment } from "next/navigation"; +import { XIcon } from "lucide-react"; -export function AnnouncementBanner(props: { +function AnnouncementBannerUI(props: { href: string; label: string; trackingLabel: string; }) { - const layoutSegment = useSelectedLayoutSegment(); const [hasDismissedAnnouncement, setHasDismissedAnnouncement] = useLocalStorage(`dismissed-${props.trackingLabel}`, false, true); - if ( - layoutSegment === "/_not-found" || - hasDismissedAnnouncement || - layoutSegment === "login" || - layoutSegment === "nebula-app" || - layoutSegment === "join" || - layoutSegment === "get-started" - ) { + if (hasDismissedAnnouncement) { return null; } @@ -34,7 +26,6 @@ export function AnnouncementBanner(props: { target={props.href.startsWith("http") ? "_blank" : undefined} className="ontainer flex cursor-pointer items-center gap-2 lg:justify-center " > - {props.label} @@ -53,9 +44,9 @@ export function AnnouncementBanner(props: { ); } -export function OrganizeContractsToProjectsBanner() { +export function AnnouncementBanner() { return ( -