diff --git a/frontend/public/demos/youtube.mp4 b/frontend/public/demos/youtube.mp4 new file mode 100644 index 0000000..61c24e9 Binary files /dev/null and b/frontend/public/demos/youtube.mp4 differ diff --git a/frontend/src/components/hosted/AppContainer.tsx b/frontend/src/components/hosted/AppContainer.tsx index f2e8997..5cf0b69 100644 --- a/frontend/src/components/hosted/AppContainer.tsx +++ b/frontend/src/components/hosted/AppContainer.tsx @@ -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 ; + // If the user is not signed in, show the landing page + if (isLoaded && !isSignedIn) return ; + + // If the user is signed in, show the app return ( <> } /> - - - - - ); } diff --git a/frontend/src/components/hosted/LandingPage.tsx b/frontend/src/components/hosted/LandingPage.tsx new file mode 100644 index 0000000..2139856 --- /dev/null +++ b/frontend/src/components/hosted/LandingPage.tsx @@ -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 ( +
+ {/* Auth dialog */} + setIsAuthPopupOpen(value)} + > + + + + + + {/* Navbar */} + + + {/* Hero */} +
+
+

+ Build User Interfaces 10x Faster +

+

+ Convert any screenshot or design to clean code (with support for + most frameworks) +

+
+ + +
+
+
+ + {/* Logo wall */} +
+

+ #1 tool used by developers and designers from leading companies. Fully + open source with 34k+ stars on GitHub. +

+
+ {LOGOS.map((companyName) => ( + {companyName} + ))} +
+
+ + {/* Video section */} +
+
+ + {/* Footer */} +
+
+ ); +} + +export default LandingPage; diff --git a/frontend/src/components/hosted/LandingPage/Footer.tsx b/frontend/src/components/hosted/LandingPage/Footer.tsx new file mode 100644 index 0000000..9eea8f0 --- /dev/null +++ b/frontend/src/components/hosted/LandingPage/Footer.tsx @@ -0,0 +1,35 @@ +const Footer = () => { + return ( +
+
+ Screenshot to Code + + © {new Date().getFullYear()} WhimsyWorks, Inc. All rights reserved. + + {/*
+ Built with + + Screenshot to Code +
*/} +
+
+ Company +
WhimsyWorks Inc.
+
Made in NYC 🗽
+ + Github + + + Contact + + + Terms of Service + +
+
+ ); +}; +export default Footer; diff --git a/frontend/src/components/ui/dialog.tsx b/frontend/src/components/ui/dialog.tsx index 776ca68..e74e937 100644 --- a/frontend/src/components/ui/dialog.tsx +++ b/frontend/src/components/ui/dialog.tsx @@ -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, @@ -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, @@ -42,14 +42,14 @@ const DialogContent = React.forwardRef< {...props} > {children} - + {/* Close - + */} -)) -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, @@ -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, @@ -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, -} +};