Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

features/credit card banner #514

Merged
merged 7 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/deploy-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@akashnetwork/akashjs": "^0.10.0",
"@akashnetwork/env-loader": "*",
"@akashnetwork/http-sdk": "*",
"@akashnetwork/logging": "*",
"@akashnetwork/network-store": "*",
"@akashnetwork/ui": "*",
"@auth0/nextjs-auth0": "^3.5.0",
Expand Down
14 changes: 14 additions & 0 deletions apps/deploy-web/src/components/layout/CreditCardBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useWallet } from "@src/context/WalletProvider/WalletProvider";
import { ConnectManagedWalletButton } from "../wallet/ConnectManagedWalletButton";

export function CreditCardBanner() {
const { hasManagedWallet } = useWallet();

return (
<div className="fixed top-0 z-10 flex h-[40px] w-full items-center justify-center space-x-4 bg-primary px-3 py-2">
<span className="text-sm font-semibold text-white">Credit Card payments are now available!</span>

{!hasManagedWallet && <ConnectManagedWalletButton className="mb-2 mr-2 w-full md:mb-0 md:w-auto" size="sm" />}
</div>
);
}
87 changes: 38 additions & 49 deletions apps/deploy-web/src/components/layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import { useMediaQuery, useTheme as useMuiTheme } from "@mui/material";
import { ACCOUNT_BAR_HEIGHT } from "@src/config/ui.config";
import { useSettings } from "@src/context/SettingsProvider";
import { useWallet } from "@src/context/WalletProvider";
import { useHasCreditCardBanner } from "@src/hooks/useHasCreditCardBanner";
import { LinearLoadingSkeleton } from "../shared/LinearLoadingSkeleton";
import { CreditCardBanner } from "./CreditCardBanner";
import { Nav } from "./Nav";
import { Sidebar } from "./Sidebar";
import { WelcomeModal } from "./WelcomeModal";

type Props = {
isLoading?: boolean;
Expand Down Expand Up @@ -49,12 +50,12 @@ const Layout: React.FunctionComponent<Props> = ({ children, isLoading, isUsingSe

const LayoutApp: React.FunctionComponent<Props> = ({ children, isLoading, isUsingSettings, isUsingWallet, disableContainer, containerClassName = "" }) => {
const muiTheme = useMuiTheme();
const [isShowingWelcome, setIsShowingWelcome] = useState(false);
const [isNavOpen, setIsNavOpen] = useState(true);
const [isMobileOpen, setIsMobileOpen] = useState(false);
const { refreshNodeStatuses, isSettingsInit } = useSettings();
const { isWalletLoaded } = useWallet();
const smallScreen = useMediaQuery(muiTheme.breakpoints.down("md"));
const hasCreditCardBanner = useHasCreditCardBanner();

useEffect(() => {
const _isNavOpen = localStorage.getItem("isNavOpen");
Expand All @@ -71,22 +72,6 @@ const LayoutApp: React.FunctionComponent<Props> = ({ children, isLoading, isUsin
clearInterval(refreshNodeIntervalId);
};
}, [refreshNodeStatuses]);

useEffect(() => {
const agreedToTerms = localStorage.getItem("agreedToTerms") === "true";

if (!agreedToTerms) {
setIsShowingWelcome(true);
}

// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const onWelcomeClose = () => {
localStorage.setItem("agreedToTerms", "true");
setIsShowingWelcome(false);
};

const onOpenMenuClick = () => {
setIsNavOpen(prev => {
const newValue = !prev;
Expand All @@ -102,42 +87,46 @@ const LayoutApp: React.FunctionComponent<Props> = ({ children, isLoading, isUsin
};

return (
<>
<WelcomeModal open={isShowingWelcome} onClose={onWelcomeClose} />

<div className="h-full">
<div className="h-full w-full" style={{ marginTop: `${ACCOUNT_BAR_HEIGHT}px` }}>
<div className="h-full">
<Nav isMobileOpen={isMobileOpen} handleDrawerToggle={handleDrawerToggle} />

<div className="block h-full w-full flex-grow rounded-none md:flex">
<Sidebar onOpenMenuClick={onOpenMenuClick} isNavOpen={isNavOpen} handleDrawerToggle={handleDrawerToggle} isMobileOpen={isMobileOpen} />

<div
className={cn("ease ml-0 h-full flex-grow transition-[margin-left] duration-300", {
["md:ml-[240px]"]: isNavOpen,
["md:ml-[57px]"]: !isNavOpen
})}
>
{isLoading !== undefined && <LinearLoadingSkeleton isLoading={isLoading} />}

<ErrorBoundary FallbackComponent={ErrorFallback}>
{!isUsingSettings || isSettingsInit ? (
!isUsingWallet || isWalletLoaded ? (
<div className={cn({ ["container pb-8 pt-4"]: !disableContainer }, containerClassName)}>{children}</div>
) : (
<Loading text="Loading wallet..." />
)
<div className="flex h-full">
{hasCreditCardBanner && <CreditCardBanner />}

<div className="w-full flex-1" style={{ marginTop: `${ACCOUNT_BAR_HEIGHT + (hasCreditCardBanner ? 40 : 0)}px` }}>
<div className="h-full">
<Nav isMobileOpen={isMobileOpen} handleDrawerToggle={handleDrawerToggle} className={{ "top-[40px]": hasCreditCardBanner }} />

<div className="block h-full w-full flex-grow rounded-none md:flex">
<Sidebar
onOpenMenuClick={onOpenMenuClick}
isNavOpen={isNavOpen}
handleDrawerToggle={handleDrawerToggle}
isMobileOpen={isMobileOpen}
mdDrawerClassName={{ ["h-[calc(100%-40px)] mt-[97px]"]: hasCreditCardBanner }}
/>

<div
className={cn("ease ml-0 h-full flex-grow transition-[margin-left] duration-300", {
["md:ml-[240px]"]: isNavOpen,
["md:ml-[57px]"]: !isNavOpen
})}
>
{isLoading !== undefined && <LinearLoadingSkeleton isLoading={isLoading} />}

<ErrorBoundary FallbackComponent={ErrorFallback}>
{!isUsingSettings || isSettingsInit ? (
!isUsingWallet || isWalletLoaded ? (
<div className={cn({ ["container pb-8 pt-4"]: !disableContainer }, containerClassName)}>{children}</div>
) : (
<Loading text="Loading settings..." />
)}
</ErrorBoundary>
</div>
<Loading text="Loading wallet..." />
)
) : (
<Loading text="Loading settings..." />
)}
</ErrorBoundary>
</div>
</div>
</div>
</div>
</>
</div>
);
};

Expand Down
7 changes: 5 additions & 2 deletions apps/deploy-web/src/components/layout/Nav.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";
import { Button, buttonVariants } from "@akashnetwork/ui/components";
import { cn } from "@akashnetwork/ui/utils";
import type { ClassValue } from "clsx";
import { Menu, Xmark } from "iconoir-react";
import { useAtom } from "jotai";
import Link from "next/link";
Expand All @@ -16,17 +17,19 @@ import { WalletStatus } from "./WalletStatus";

export const Nav = ({
isMobileOpen,
handleDrawerToggle
handleDrawerToggle,
className
}: React.PropsWithChildren<{
isMobileOpen: boolean;
handleDrawerToggle: () => void;
className?: ClassValue;
}>) => {
const theme = useCookieTheme();
const [isSignedInWithTrial] = useAtom(walletStore.isSignedInWithTrial);
const { user } = useCustomUser();

return (
<header className="fixed top-0 z-50 w-full border-b border-border bg-popover dark:bg-background">
<header className={cn("fixed top-0 z-50 w-full border-b border-border bg-popover dark:bg-background", className)}>
<div className="flex h-14 items-center justify-between pl-4 pr-4">
{!!theme && (
<Link className="flex items-center" href="/">
Expand Down
16 changes: 11 additions & 5 deletions apps/deploy-web/src/components/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { cn } from "@akashnetwork/ui/utils";
import Drawer from "@mui/material/Drawer";
import { useTheme as useMuiTheme } from "@mui/material/styles";
import useMediaQuery from "@mui/material/useMediaQuery";
import type { ClassValue } from "clsx";
import { Discord, Github, Menu, MenuScale, Rocket, X as TwitterX, Youtube } from "iconoir-react";
import { Cloud, HelpCircle, Home, MultiplePages, OpenInWindow, Server, Settings, Tools } from "iconoir-react";
import { useAtom } from "jotai";
Expand All @@ -29,12 +30,13 @@ type Props = {
handleDrawerToggle: () => void;
onOpenMenuClick: () => void;
isNavOpen: boolean;
mdDrawerClassName?: ClassValue;
};

const DRAWER_WIDTH = 240;
const CLOSED_DRAWER_WIDTH = 57;

export const Sidebar: React.FunctionComponent<Props> = ({ isMobileOpen, handleDrawerToggle, isNavOpen, onOpenMenuClick }) => {
export const Sidebar: React.FunctionComponent<Props> = ({ isMobileOpen, handleDrawerToggle, isNavOpen, onOpenMenuClick, mdDrawerClassName }) => {
const [isHovering, setIsHovering] = useState(false);
const _isNavOpen = isNavOpen || isHovering;
const [, setDeploySdl] = useAtom(sdlStore.deploySdl);
Expand Down Expand Up @@ -301,10 +303,14 @@ export const Sidebar: React.FunctionComponent<Props> = ({ isMobileOpen, handleDr
onMouseEnter={onDrawerHover}
onMouseLeave={() => setIsHovering(false)}
PaperProps={{
className: cn("border-none ease z-[1000] bg-header/95 transition-[width] duration-300 box-border overflow-hidden mt-[57px]", {
["md:w-[240px]"]: _isNavOpen,
["md:w-[57px]"]: !_isNavOpen
})
className: cn(
"border-none ease z-[1000] bg-header/95 transition-[width] duration-300 box-border overflow-hidden mt-[57px]",
{
["md:w-[240px]"]: _isNavOpen,
["md:w-[57px]"]: !_isNavOpen
},
mdDrawerClassName
)
}}
open
>
Expand Down
63 changes: 0 additions & 63 deletions apps/deploy-web/src/components/layout/WelcomeModal.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,14 @@ export const ConnectManagedWalletButton: React.FunctionComponent<Props> = ({ cla
const { connectManagedWallet, hasManagedWallet, isWalletLoading } = useWallet();

return (
<Button variant="outline" onClick={connectManagedWallet} className={cn("border-primary bg-primary/10 dark:bg-primary", className)} {...rest} disabled={isWalletLoading}>
{isWalletLoading ? <Spinner size="small" className="mr-2" /> : <Rocket className="text-xs" />}
<Button
variant="outline"
onClick={connectManagedWallet}
className={cn("border-primary bg-primary/10 dark:bg-primary", className)}
{...rest}
disabled={isWalletLoading}
>
{isWalletLoading ? <Spinner size="small" className="mr-2" /> : <Rocket className="rotate-45 text-xs" />}
<span className="m-2 whitespace-nowrap">{hasManagedWallet ? "Switch to USD Payments" : "Start Trial"}</span>
</Button>
);
Expand Down
32 changes: 32 additions & 0 deletions apps/deploy-web/src/hooks/useHasCreditCardBanner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useEffect, useMemo, useState } from "react";

import { browserEnvConfig } from "@src/config/browser-env.config";
import { useWallet } from "@src/context/WalletProvider";
import { useUser } from "./useUser";

const withBilling = browserEnvConfig.NEXT_PUBLIC_BILLING_ENABLED;

export function useHasCreditCardBanner() {
const user = useUser();
const [isBannerVisible, setIsBannerVisible] = useState(false);
const [isInitialized, setIsInitialized] = useState(false);
const { hasManagedWallet, isWalletLoading } = useWallet();
const shouldShowBanner = useMemo(
() => isInitialized && withBilling && !hasManagedWallet && !isWalletLoading,
[isInitialized, hasManagedWallet, isWalletLoading]
);

useEffect(() => {
if (user?.id) {
setIsInitialized(true);
}
}, [user?.id]);

useEffect(() => {
if (shouldShowBanner) {
setIsBannerVisible(true);
}
}, [shouldShowBanner]);

return isBannerVisible;
}
19 changes: 15 additions & 4 deletions apps/deploy-web/src/pages/new-deployment/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { LoggerService } from "@akashnetwork/logging";
import { z } from "zod";

import { NewDeploymentContainer, NewDeploymentContainerProps } from "@src/components/new-deployment/NewDeploymentContainer";
Expand All @@ -13,16 +14,26 @@ const contextSchema = z.object({
})
});

const logger = LoggerService.forContext(NewDeploymentContainer.name);

export const getServerSideProps = getValidatedServerSideProps<NewDeploymentContainerProps, typeof contextSchema>(contextSchema, async ({ query }) => {
if (!query.templateId) {
return { props: {} };
}

const template = await services.template.findById(query.templateId);
try {
const template = await services.template.findById(query.templateId);

if (template && query.templateId) {
return { props: { template, templateId: query.templateId } };
if (template && query.templateId) {
return { props: { template, templateId: query.templateId } };
}
} catch (error) {
if (error?.response?.status === 404) {
logger.info(`Template not found: ${query.templateId}`);
} else {
logger.error(error);
}
}

return { props: {} };
return { props: { templateId: query.templateId } };
});
3 changes: 2 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/ui/components/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "../utils/cn";

const buttonVariants = cva(
"ring-offset-background focus-visible:ring-ring inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
"ring-offset-background focus-visible:ring-ring relative inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
Expand Down
Loading