add a new landing page

This commit is contained in:
Abi Raja 2024-01-29 12:36:03 -05:00
parent b04dba001d
commit ac858e252b
5 changed files with 213 additions and 62 deletions

Binary file not shown.

View File

@ -1,18 +1,16 @@
import { SignUp, useUser } from "@clerk/clerk-react";
import { useUser } from "@clerk/clerk-react";
import posthog from "posthog-js";
import App from "../../App";
import { useEffect, useRef, useState } from "react";
import { AlertDialog } from "@radix-ui/react-alert-dialog";
import { AlertDialogContent } from "../ui/alert-dialog";
import { useEffect, useRef } from "react";
import FullPageSpinner from "../custom-ui/FullPageSpinner";
import { useAuthenticatedFetch } from "./useAuthenticatedFetch";
import { useStore } from "../../store/store";
import AvatarDropdown from "./AvatarDropdown";
import { UserResponse } from "./types";
import { POSTHOG_HOST, POSTHOG_KEY, SAAS_BACKEND_URL } from "../../config";
import LandingPage from "./LandingPage";
function AppContainer() {
const [showPopup, setShowPopup] = useState(false);
const { isSignedIn, isLoaded } = useUser();
const setSubscriberTier = useStore((state) => state.setSubscriberTier);
@ -21,14 +19,7 @@ function AppContainer() {
const authenticatedFetch = useAuthenticatedFetch();
const isInitRequestInProgress = useRef(false);
// If Clerk is loaded and the user is not signed in, show the sign up popup
useEffect(() => {
if (isLoaded && !isSignedIn) {
setShowPopup(true);
}
}, [isSignedIn, isLoaded]);
// Get the current user
// Get information from our backend about the user (subscription status)
useEffect(() => {
const init = async () => {
// Make sure there's only one request in progress
@ -36,12 +27,17 @@ function AppContainer() {
if (isInitRequestInProgress.current) return;
isInitRequestInProgress.current = true;
// TODO: Handle when the user is not signed in
const user: UserResponse = await authenticatedFetch(
SAAS_BACKEND_URL + "/users/create",
"POST"
);
// If the user is not signed in, authenticatedFetch will return undefined
if (!user) {
isInitRequestInProgress.current = false;
return;
}
if (!user.subscriber_tier) {
setSubscriberTier("free");
} else {
@ -66,6 +62,10 @@ function AppContainer() {
// If Clerk is still loading, show a spinner
if (!isLoaded) return <FullPageSpinner />;
// If the user is not signed in, show the landing page
if (isLoaded && !isSignedIn) return <LandingPage />;
// If the user is signed in, show the app
return (
<>
<App
@ -75,31 +75,6 @@ function AppContainer() {
</div>
}
/>
<AlertDialog open={showPopup}>
<AlertDialogContent className="flex justify-center">
<SignUp
appearance={{
elements: {
card: {
boxShadow: "none",
borderRadius: "0",
border: "none",
backgroundColor: "transparent",
},
footer: {
display: "flex",
flexDirection: "column",
textAlign: "center",
},
footerAction: {
marginBottom: "5px",
},
},
layout: { privacyPageUrl: "https://a.picoapps.xyz/camera-write" },
}}
/>
</AlertDialogContent>
</AlertDialog>
</>
);
}

View File

@ -0,0 +1,141 @@
import { FaGithub } from "react-icons/fa";
import Footer from "./LandingPage/Footer";
import { Button } from "../ui/button";
import { SignUp } from "@clerk/clerk-react";
import { useState } from "react";
import { Dialog, DialogContent } from "../ui/dialog";
const LOGOS = ["microsoft", "amazon", "mit", "stanford", "bytedance", "baidu"];
function LandingPage() {
const [isAuthPopupOpen, setIsAuthPopupOpen] = useState(false);
const signIn = () => {
setIsAuthPopupOpen(true);
};
return (
<div className="w-full xl:w-[1000px] mx-auto mt-4">
{/* Auth dialog */}
<Dialog
open={isAuthPopupOpen}
onOpenChange={(value) => setIsAuthPopupOpen(value)}
>
<DialogContent className="flex justify-center">
<SignUp
appearance={{
elements: {
card: {
boxShadow: "none",
borderRadius: "0",
border: "none",
backgroundColor: "transparent",
},
footer: {
display: "flex",
flexDirection: "column",
textAlign: "center",
},
footerAction: {
marginBottom: "5px",
},
},
layout: { privacyPageUrl: "https://a.picoapps.xyz/camera-write" },
}}
/>
</DialogContent>
</Dialog>
{/* Navbar */}
<nav className="border-b border-gray-200 px-4 py-2">
<div className="flex justify-between items-center">
<div className="text-lg font-semibold">Screenshot to Code</div>
<div className="flex items-center space-x-4">
<Button variant="secondary" onClick={signIn}>
Sign in
</Button>
<Button onClick={signIn}>Get started</Button>
</div>
</div>
</nav>
{/* Hero */}
<header className="px-4 py-16">
<div className="mx-auto">
<h2 className="text-5xl font-bold leading-tight mb-6">
Build User Interfaces 10x Faster
</h2>
<p className="text-gray-600 text-xl mb-6">
Convert any screenshot or design to clean code (with support for
most frameworks)
</p>
<div className="flex space-x-4 flex-col sm:flex-row">
<Button size="lg" className="text-lg py-6 px-8" onClick={signIn}>
Get started
</Button>
<Button
variant="secondary"
onClick={() =>
window.open(
"https://github.com/abi/screenshot-to-code",
"_blank"
)
}
className="flex items-center space-x-2 text-gray-600 hover:text-gray-900 py-6 px-8"
>
<FaGithub size={24} />
<span>GitHub</span>
<span className="text-sm bg-gray-200 rounded-full px-2 py-1">
34,458
</span>
</Button>
</div>
</div>
</header>
{/* Logo wall */}
<div className="mx-auto mt-12 px-4 sm:px-0">
<p className="text-gray-600 text-xl mb-10 text-center">
#1 tool used by developers and designers from leading companies. Fully
open source with 34k+ stars on GitHub.
</p>
<div
className="mx-auto grid max-w-lg items-center gap-x-2
gap-y-10 sm:max-w-xl grid-cols-3 lg:mx-0 lg:max-w-none mt-10"
>
{LOGOS.map((companyName) => (
<img
key={companyName}
className="col-span-1 max-h-8 w-full object-contain
grayscale opacity-50 hover:opacity-100"
src={`https://picoapps.xyz/logos/${companyName}.png`}
alt={companyName}
width={120}
height={48}
/>
))}
</div>
</div>
{/* Video section */}
<div className="px-4 mt-20 mb-10 text-center">
<video
src="/demos/youtube.mp4"
className="max-w-lg mx-auto rounded-md w-full sm:w-auto"
autoPlay
loop
muted
/>
<div className="mt-6">
Watch Screenshot to Code convert a screenshot of YouTube to
HTML/Tailwind
</div>
</div>
{/* Footer */}
<Footer />
</div>
);
}
export default LandingPage;

View File

@ -0,0 +1,35 @@
const Footer = () => {
return (
<footer className="flex justify-between border-t border-gray-200 pt-4 mb-6 px-4 sm:px-0">
<div className="flex flex-col">
<span className="text-xl mb-2">Screenshot to Code</span>
<span className="text-xs">
© {new Date().getFullYear()} WhimsyWorks, Inc. All rights reserved.
</span>
{/* <div
className="bg-gray-800 text-white text-sm px-2 py-2
rounded-full flex items-center space-x-2"
>
<span>Built with</span>
<i className="fas fa-bolt text-yellow-400"></i>
<span>Screenshot to Code</span>
</div> */}
</div>
<div className="flex flex-col text-sm text-gray-600 mr-4">
<span className="uppercase">Company</span>
<div>WhimsyWorks Inc.</div>
<div>Made in NYC 🗽</div>
<a href="https://github.com/abi/screenshot-to-code" target="_blank">
Github
</a>
<a href="mailto:support@picoapps.xyz" target="_blank">
Contact
</a>
<a href="https://a.picoapps.xyz/camera-write" target="_blank">
Terms of Service
</a>
</div>
</footer>
);
};
export default Footer;

View File

@ -1,16 +1,16 @@
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { Cross2Icon } from "@radix-ui/react-icons"
import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog";
// import { Cross2Icon } from "@radix-ui/react-icons";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
const Dialog = DialogPrimitive.Root
const Dialog = DialogPrimitive.Root;
const DialogTrigger = DialogPrimitive.Trigger
const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal
const DialogPortal = DialogPrimitive.Portal;
const DialogClose = DialogPrimitive.Close
const DialogClose = DialogPrimitive.Close;
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
@ -24,8 +24,8 @@ const DialogOverlay = React.forwardRef<
)}
{...props}
/>
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
@ -42,14 +42,14 @@ const DialogContent = React.forwardRef<
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
{/* <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Close> */}
</DialogPrimitive.Content>
</DialogPortal>
))
DialogContent.displayName = DialogPrimitive.Content.displayName
));
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({
className,
@ -62,8 +62,8 @@ const DialogHeader = ({
)}
{...props}
/>
)
DialogHeader.displayName = "DialogHeader"
);
DialogHeader.displayName = "DialogHeader";
const DialogFooter = ({
className,
@ -76,8 +76,8 @@ const DialogFooter = ({
)}
{...props}
/>
)
DialogFooter.displayName = "DialogFooter"
);
DialogFooter.displayName = "DialogFooter";
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
@ -91,8 +91,8 @@ const DialogTitle = React.forwardRef<
)}
{...props}
/>
))
DialogTitle.displayName = DialogPrimitive.Title.displayName
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
@ -103,8 +103,8 @@ const DialogDescription = React.forwardRef<
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
DialogDescription.displayName = DialogPrimitive.Description.displayName
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
@ -117,4 +117,4 @@ export {
DialogFooter,
DialogTitle,
DialogDescription,
}
};