diff --git a/apps/chat-with-pdf/app/components/sidebar/app-sidebar.tsx b/apps/chat-with-pdf/app/components/sidebar/app-sidebar.tsx index 807306a..fd080c3 100644 --- a/apps/chat-with-pdf/app/components/sidebar/app-sidebar.tsx +++ b/apps/chat-with-pdf/app/components/sidebar/app-sidebar.tsx @@ -1,24 +1,13 @@ "use client"; import { - AudioWaveform, BadgeCheck, Bell, - BookOpen, - Bot, ChevronsUpDown, - Command, CreditCard, - Frame, - GalleryVerticalEnd, LogOut, - Map, - PieChart, Plus, - Settings2, Sparkles, - SparklesIcon, - SquareTerminal, } from "lucide-react"; import { createClient } from "@/lib/supabase/client"; diff --git a/apps/landing/.eslintrc.json b/apps/landing/.eslintrc.json deleted file mode 100644 index f5fcd8e..0000000 --- a/apps/landing/.eslintrc.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "root": true, - "extends": [ - "@makify/eslint-config" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "project": true - } -} \ No newline at end of file diff --git a/apps/landing/actions/resend-create-contact.ts b/apps/landing/actions/resend-create-contact.ts new file mode 100644 index 0000000..184e562 --- /dev/null +++ b/apps/landing/actions/resend-create-contact.ts @@ -0,0 +1,35 @@ +"use server"; + +import { Ratelimit } from "@upstash/ratelimit"; +import { kv } from "@vercel/kv"; +import { headers } from "next/headers"; +import { Resend } from "resend"; + +const resend = new Resend(process.env.RESEND_API_KEY); + +const ratelimit = new Ratelimit({ + redis: kv, + // rate limit to 10 requests per 24 hours + limiter: Ratelimit.slidingWindow(10, "24h"), +}); + +export async function resendCreateContact(email: string) { + const ip = headers().get("x-forwarded-for") ?? "127.0.0.1"; + const { success } = await ratelimit.limit(ip); + + if (!success) { + return { + data: null, + error: { + message: + "You already registered too many emails. Please try again later.", + }, + }; + } + + const res = await resend.contacts.create({ + email, + audienceId: process.env.RESEND_GENERAL_AUDIENCE_ID!, + }); + return res; +} diff --git a/apps/landing/app/api/auth/[...nextauth]/route.ts b/apps/landing/app/api/auth/[...nextauth]/route.ts deleted file mode 100644 index b39b376..0000000 --- a/apps/landing/app/api/auth/[...nextauth]/route.ts +++ /dev/null @@ -1,18 +0,0 @@ -import NextAuth, { NextAuthOptions } from "next-auth"; -import { PrismaAdapter } from "@next-auth/prisma-adapter"; -import prisma from "@/lib/prisma"; -import GoogleProvider from "next-auth/providers/google"; - -export const authOptions: NextAuthOptions = { - adapter: PrismaAdapter(prisma), - providers: [ - GoogleProvider({ - clientId: process.env.GOOGLE_CLIENT_ID as string, - clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, - }), - ], -}; - -const handler = NextAuth(authOptions); - -export { handler as GET, handler as POST }; diff --git a/apps/landing/app/components/hero.tsx b/apps/landing/app/components/hero.tsx new file mode 100644 index 0000000..e9b38d1 --- /dev/null +++ b/apps/landing/app/components/hero.tsx @@ -0,0 +1,62 @@ +"use client"; + +import { TextRotate } from "@/components/shared/text-rotate"; +import { WaitListForm } from "@/components/wait-list-form"; +import LogoHorizontal from "@/public/logo-horizontal.svg"; +import { useEffect, useState } from "react"; + +const dynamicTexts = ["optimize it", "simplify it", "accelerate it"]; + +export default function Hero() { + const [currentDynamicText, setCurrentDynamicText] = useState( + dynamicTexts[0]!, + ); + + useEffect(handleTextChange, []); + + function handleTextChange() { + const timeout = setTimeout(() => { + // Change the text incrementally + setCurrentDynamicText((_currentDynamicText) => { + const currentIndex = dynamicTexts.indexOf(_currentDynamicText); + const nextIndex = + currentIndex + 1 >= dynamicTexts.length ? 0 : currentIndex + 1; + return dynamicTexts[nextIndex]!; + }); + handleTextChange(); + }, 5000); + + return () => clearTimeout(timeout); + } + + return ( +
+
+
+
+ +
+

+ Let AI{" "} +
+
+ + {currentDynamicText} + +
+
+

+

+ Makify is a collection of tools with AI to make your life easier than + ever. Join the waitlist to get early access. +

+
+ +
+
+
+ ); +} diff --git a/apps/landing/app/components/layout/nav.tsx b/apps/landing/app/components/layout/nav.tsx new file mode 100644 index 0000000..3963691 --- /dev/null +++ b/apps/landing/app/components/layout/nav.tsx @@ -0,0 +1,5 @@ +import Navbar from "./navbar"; + +export default async function Nav() { + return ; +} diff --git a/apps/landing/app/components/layout/navbar.tsx b/apps/landing/app/components/layout/navbar.tsx new file mode 100644 index 0000000..8b04851 --- /dev/null +++ b/apps/landing/app/components/layout/navbar.tsx @@ -0,0 +1,52 @@ +import { Button } from "@makify/ui"; +import Link from "next/link"; + +export default function NavBar() { + return ( +
+
+
+
+
+ + +
+
+
+
+ ); +} diff --git a/apps/landing/app/components/shared/background-beams-with-collision.tsx.tsx b/apps/landing/app/components/shared/background-beams-with-collision.tsx.tsx new file mode 100644 index 0000000..e74f38a --- /dev/null +++ b/apps/landing/app/components/shared/background-beams-with-collision.tsx.tsx @@ -0,0 +1,260 @@ +"use client"; + +import { cn } from "@makify/ui/lib/utils"; +import { motion, AnimatePresence } from "framer-motion"; +import React, { useRef, useState, useEffect } from "react"; + +export const BackgroundBeamsWithCollision = ({ + children, + className, +}: { + children: React.ReactNode; + className?: string; +}) => { + const containerRef = useRef(null); + const parentRef = useRef(null); + + const beams = [ + { + initialX: 10, + translateX: 10, + duration: 7, + repeatDelay: 3, + delay: 2, + }, + { + initialX: 600, + translateX: 600, + duration: 3, + repeatDelay: 3, + delay: 4, + }, + { + initialX: 100, + translateX: 100, + duration: 7, + repeatDelay: 7, + className: "h-6", + }, + { + initialX: 400, + translateX: 400, + duration: 5, + repeatDelay: 14, + delay: 4, + }, + { + initialX: 800, + translateX: 800, + duration: 11, + repeatDelay: 2, + className: "h-20", + }, + { + initialX: 1000, + translateX: 1000, + duration: 4, + repeatDelay: 2, + className: "h-12", + }, + { + initialX: 1200, + translateX: 1200, + duration: 6, + repeatDelay: 4, + delay: 2, + className: "h-6", + }, + ]; + + return ( +
+ {beams.map((beam) => ( + } + parentRef={parentRef as React.RefObject} + /> + ))} + + {children} +
+
+ ); +}; + +const CollisionMechanism = React.forwardRef< + HTMLDivElement, + { + containerRef: React.RefObject; + parentRef: React.RefObject; + beamOptions?: { + initialX?: number; + translateX?: number; + initialY?: number; + translateY?: number; + rotate?: number; + className?: string; + duration?: number; + delay?: number; + repeatDelay?: number; + }; + } +>(({ parentRef, containerRef, beamOptions = {} }, ref) => { + const beamRef = useRef(null); + const [collision, setCollision] = useState<{ + detected: boolean; + coordinates: { x: number; y: number } | null; + }>({ + detected: false, + coordinates: null, + }); + const [beamKey, setBeamKey] = useState(0); + const [cycleCollisionDetected, setCycleCollisionDetected] = useState(false); + + useEffect(() => { + const checkCollision = () => { + if ( + beamRef.current && + containerRef.current && + parentRef.current && + !cycleCollisionDetected + ) { + const beamRect = beamRef.current.getBoundingClientRect(); + const containerRect = containerRef.current.getBoundingClientRect(); + const parentRect = parentRef.current.getBoundingClientRect(); + + if (beamRect.bottom >= containerRect.top) { + const relativeX = + beamRect.left - parentRect.left + beamRect.width / 2; + const relativeY = beamRect.bottom - parentRect.top; + + setCollision({ + detected: true, + coordinates: { + x: relativeX, + y: relativeY, + }, + }); + setCycleCollisionDetected(true); + } + } + }; + + const animationInterval = setInterval(checkCollision, 50); + + return () => clearInterval(animationInterval); + }, [cycleCollisionDetected, containerRef]); + + useEffect(() => { + if (collision.detected && collision.coordinates) { + setTimeout(() => { + setCollision({ detected: false, coordinates: null }); + setCycleCollisionDetected(false); + }, 2000); + + setTimeout(() => { + setBeamKey((prevKey) => prevKey + 1); + }, 2000); + } + }, [collision]); + + return ( + <> + + + {collision.detected && collision.coordinates && ( + + )} + + + ); +}); + +CollisionMechanism.displayName = "CollisionMechanism"; + +const Explosion = ({ ...props }: React.HTMLProps) => { + const spans = Array.from({ length: 20 }, (_, index) => ({ + id: index, + initialX: 0, + initialY: 0, + directionX: Math.floor(Math.random() * 80 - 40), + directionY: Math.floor(Math.random() * -50 - 10), + })); + + return ( +
+ + {spans.map((span) => ( + + ))} +
+ ); +}; diff --git a/apps/landing/app/components/shared/text-morph.tsx b/apps/landing/app/components/shared/text-morph.tsx new file mode 100644 index 0000000..19e6da2 --- /dev/null +++ b/apps/landing/app/components/shared/text-morph.tsx @@ -0,0 +1,61 @@ +"use client"; + +import { cn } from "@makify/ui/lib/utils"; +import { AnimatePresence, motion } from "framer-motion"; +import { useMemo, useId } from "react"; + +type TextMorphProps = { + children: string; + as?: React.ElementType; + className?: string; + style?: React.CSSProperties; +}; + +export function TextMorph({ + children, + as: Component = "p", + className, + style, +}: TextMorphProps) { + const uniqueId = useId(); + + const characters = useMemo(() => { + const charCounts: Record = {}; + + return children.split("").map((char, index) => { + const lowerChar = char.toLowerCase(); + charCounts[lowerChar] = (charCounts[lowerChar] || 0) + 1; + + return { + id: `${uniqueId}-${lowerChar}${charCounts[lowerChar]}`, + label: index === 0 ? char.toUpperCase() : lowerChar, + }; + }); + }, [children, uniqueId]); + + return ( + + + {characters.map((character) => ( + + ))} + + + ); +} diff --git a/apps/landing/app/components/shared/text-rotate.tsx b/apps/landing/app/components/shared/text-rotate.tsx new file mode 100644 index 0000000..dd875ed --- /dev/null +++ b/apps/landing/app/components/shared/text-rotate.tsx @@ -0,0 +1,35 @@ +import { cn } from "@makify/ui/lib/utils"; +import { AnimatePresence, motion } from "framer-motion"; + +type TextRotateProps = { + children: string; + as?: React.ElementType; + className?: string; + style?: React.CSSProperties; +}; + +export const TextRotate = ({ + children, + as: Component = "span", + className, + style, +}: TextRotateProps) => { + return ( + + + {children} + + {children} + + + + ); +}; diff --git a/apps/landing/app/components/shared/text-scramble.tsx b/apps/landing/app/components/shared/text-scramble.tsx new file mode 100644 index 0000000..3f8d290 --- /dev/null +++ b/apps/landing/app/components/shared/text-scramble.tsx @@ -0,0 +1,51 @@ +import { cn } from "@makify/ui/lib/utils"; +import React, { useState, useEffect } from "react"; + +type TextScrambleProps = { + children: string; + as?: React.ElementType; + className?: string; + style?: React.CSSProperties; +}; + +export function TextScramble({ + children, + as: Component = "p", + className, + style, +}: TextScrambleProps) { + const [text, setText] = useState(""); + const finalText = children; + const chars = "!<>-_\\/[]{}—=+*^?#________"; + + useEffect(() => { + let iteration = 0; + const interval = setInterval(() => { + setText((prevText) => + finalText + .split("") + .map((letter, index) => { + if (index < iteration) { + return finalText[index]; + } + return chars[Math.floor(Math.random() * chars.length)]; + }) + .join(""), + ); + + if (iteration >= finalText.length) { + clearInterval(interval); + } + + iteration += 1 / 3; + }, 30); + + return () => clearInterval(interval); + }, [children]); + + return ( + + {text} + + ); +} diff --git a/apps/landing/app/components/wait-list-form.tsx b/apps/landing/app/components/wait-list-form.tsx new file mode 100644 index 0000000..531148d --- /dev/null +++ b/apps/landing/app/components/wait-list-form.tsx @@ -0,0 +1,77 @@ +import { Button, Form, FormField, Input, useToast } from "@makify/ui"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { ArrowRight, Loader2 } from "lucide-react"; +import { cn } from "@makify/ui/lib/utils"; +import { resendCreateContact } from "actions/resend-create-contact"; + +const waitListFormSchema = z.object({ + email: z.string().email(), +}); + +type WaitListFormProps = { + className?: string; +}; + +export function WaitListForm({ className }: WaitListFormProps) { + const waitListForm = useForm>({ + resolver: zodResolver(waitListFormSchema), + }); + + const { toast } = useToast(); + + async function onSubmit(data: z.infer) { + const { data: resendData, error } = await resendCreateContact(data.email); + toast({ + title: error + ? "Ups something went wrong" + : "We added you to the waitlist", + description: error ? error?.message : "We'll be in touch soon", + }); + + if (resendData) { + waitListForm.reset({ email: "" }); + } + } + + return ( +
+ + ( + + )} + /> + + + + ); +} diff --git a/apps/landing/app/favicon.ico b/apps/landing/app/favicon.ico deleted file mode 100644 index 7afc8d3..0000000 Binary files a/apps/landing/app/favicon.ico and /dev/null differ diff --git a/apps/landing/app/layout.tsx b/apps/landing/app/layout.tsx index ab4541d..77f0503 100644 --- a/apps/landing/app/layout.tsx +++ b/apps/landing/app/layout.tsx @@ -1,17 +1,36 @@ import "./globals.css"; import "@makify/ui/globals.css"; -import { Analytics } from "@vercel/analytics/react"; -import cx from "classnames"; import { sfPro, inter } from "./fonts"; -import Nav from "@/components/layout/nav"; -import { Suspense } from "react"; +import Nav from "@/app/components/layout/nav"; +import { BackgroundBeamsWithCollision } from "./components/shared/background-beams-with-collision.tsx"; +import { cn } from "@makify/ui/lib/utils"; +import { Toaster } from "@makify/ui"; +import PlausibleProvider from "next-plausible"; export const metadata = { title: "Makify - Tools that make your life easier", description: "Makify is all you need to make your life easier. It includes such useful tools as a chat with your PDF app, a media file sharing app, and a QR code generator.", - metadataBase: new URL("https://precedent.dev"), - themeColor: "#FFF", + icons: { + icon: [ + { + rel: "icon", + url: "/icon2.svg", + media: "(prefers-color-scheme: dark)", + type: "image/svg+xml", + }, + { + rel: "icon", + url: "/icon2.svg", + media: "(prefers-color-scheme: light)", + type: "image/svg+xml", + }, + { + rel: "apple-touch-icon", + url: "/icon2.svg", + }, + ], + }, }; export default async function RootLayout({ @@ -20,16 +39,18 @@ export default async function RootLayout({ children: React.ReactNode; }) { return ( - - -
- -
- ); -} diff --git a/apps/landing/components/home/demo-modal.tsx b/apps/landing/components/home/demo-modal.tsx deleted file mode 100644 index 2bdcc5f..0000000 --- a/apps/landing/components/home/demo-modal.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import Modal from "@/components/shared/modal"; -import { - useState, - Dispatch, - SetStateAction, - useCallback, - useMemo, -} from "react"; -import Image from "next/image"; - -const DemoModal = ({ - showDemoModal, - setShowDemoModal, -}: { - showDemoModal: boolean; - setShowDemoModal: Dispatch>; -}) => { - return ( - -
-
- - Precedent Logo - -

Precedent

-

- Precedent is an opinionated collection of components, hooks, and - utilities for your Next.js project. -

-
-
- - ); -}; - -export function useDemoModal() { - const [showDemoModal, setShowDemoModal] = useState(false); - - const DemoModalCallback = useCallback(() => { - return ( - - ); - }, [showDemoModal, setShowDemoModal]); - - return useMemo( - () => ({ setShowDemoModal, DemoModal: DemoModalCallback }), - [setShowDemoModal, DemoModalCallback], - ); -} diff --git a/apps/landing/components/home/web-vitals.tsx b/apps/landing/components/home/web-vitals.tsx deleted file mode 100644 index 3d87512..0000000 --- a/apps/landing/components/home/web-vitals.tsx +++ /dev/null @@ -1,39 +0,0 @@ -"use client"; - -import { motion } from "framer-motion"; -import CountingNumbers from "@/components/shared/counting-numbers"; - -export default function WebVitals() { - return ( -
- - - - -
- ); -} diff --git a/apps/landing/components/layout/footer.tsx b/apps/landing/components/layout/footer.tsx deleted file mode 100644 index b2fee1b..0000000 --- a/apps/landing/components/layout/footer.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { BuyMeACoffee } from "../shared/icons"; - -export default function Footer() { - return ( -
-

- A project by{" "} - - Steven Tey - -

- - -

Buy me a coffee

-
-
- ); -} diff --git a/apps/landing/components/layout/nav.tsx b/apps/landing/components/layout/nav.tsx deleted file mode 100644 index ba62b01..0000000 --- a/apps/landing/components/layout/nav.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import Navbar from "./navbar"; -import { getServerSession } from "next-auth/next"; -import { authOptions } from "@/app/api/auth/[...nextauth]/route"; - -export default async function Nav() { - const session = await getServerSession(authOptions); - return ; -} diff --git a/apps/landing/components/layout/navbar.tsx b/apps/landing/components/layout/navbar.tsx deleted file mode 100644 index a3e5681..0000000 --- a/apps/landing/components/layout/navbar.tsx +++ /dev/null @@ -1,130 +0,0 @@ -"use client"; - -import Image from "next/image"; -import Link from "next/link"; -import useScroll from "@/lib/hooks/use-scroll"; -import { useSignInModal } from "./sign-in-modal"; -import UserDropdown from "./user-dropdown"; -import { Session } from "next-auth"; -import { - NavigationMenu, - NavigationMenuContent, - NavigationMenuItem, - NavigationMenuLink, - NavigationMenuList, - NavigationMenuTrigger, - navigationMenuTriggerStyle, -} from "@makify/ui"; -import { forwardRef } from "react"; -import { cn } from "@/lib/utils"; - -export default function NavBar({ session }: { session: Session | null }) { - const { SignInModal, setShowSignInModal } = useSignInModal(); - const scrolled = useScroll(50); - - return ( - <> - -
-
-
- - Precedent logo -

Makify

- - - - - - Tools - - -
    - - Discover an interactive way to read your PDFs. - - - Upload any kind of file and share it. - - - Turn it into a QR code any kind of text. - -
-
-
- - - - Pricing - - - -
-
-
-
- {session ? ( - - ) : ( - - )} -
-
-
- - ); -} - -const ListItem = forwardRef< - React.ElementRef<"a">, - React.ComponentPropsWithoutRef<"a"> ->(({ className, title, children, ...props }, ref) => { - return ( -
  • - - -
    {title}
    -

    - {children} -

    -
    -
    -
  • - ); -}); -ListItem.displayName = "ListItem"; diff --git a/apps/landing/components/layout/sign-in-modal.tsx b/apps/landing/components/layout/sign-in-modal.tsx deleted file mode 100644 index 17b5021..0000000 --- a/apps/landing/components/layout/sign-in-modal.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import Modal from "@/components/shared/modal"; -import { signIn } from "next-auth/react"; -import { - useState, - Dispatch, - SetStateAction, - useCallback, - useMemo, -} from "react"; -import { LoadingDots, Google } from "@/components/shared/icons"; -import Image from "next/image"; - -const SignInModal = ({ - showSignInModal, - setShowSignInModal, -}: { - showSignInModal: boolean; - setShowSignInModal: Dispatch>; -}) => { - const [signInClicked, setSignInClicked] = useState(false); - - return ( - -
    -
    - - Logo - -

    Sign In

    -

    - This is strictly for demo purposes - only your email and profile - picture will be stored. -

    -
    - -
    - -
    -
    -
    - ); -}; - -export function useSignInModal() { - const [showSignInModal, setShowSignInModal] = useState(false); - - const SignInModalCallback = useCallback(() => { - return ( - - ); - }, [showSignInModal, setShowSignInModal]); - - return useMemo( - () => ({ setShowSignInModal, SignInModal: SignInModalCallback }), - [setShowSignInModal, SignInModalCallback], - ); -} diff --git a/apps/landing/components/layout/user-dropdown.tsx b/apps/landing/components/layout/user-dropdown.tsx deleted file mode 100644 index d64ec66..0000000 --- a/apps/landing/components/layout/user-dropdown.tsx +++ /dev/null @@ -1,65 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { signOut } from "next-auth/react"; -import { LayoutDashboard, LogOut } from "lucide-react"; -import Popover from "@/components/shared/popover"; -import Image from "next/image"; -import { Session } from "next-auth"; - -export default function UserDropdown({ session }: { session: Session }) { - const { email, image } = session?.user || {}; - const [openPopover, setOpenPopover] = useState(false); - - if (!email) return null; - - return ( -
    - -
    - {session?.user?.name && ( -

    - {session?.user?.name} -

    - )} -

    - {session?.user?.email} -

    -
    - - -
    - } - align="end" - openPopover={openPopover} - setOpenPopover={setOpenPopover} - > - - - - ); -} diff --git a/apps/landing/components/shared/counting-numbers.tsx b/apps/landing/components/shared/counting-numbers.tsx deleted file mode 100644 index bfd3a37..0000000 --- a/apps/landing/components/shared/counting-numbers.tsx +++ /dev/null @@ -1,40 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; - -export default function CountingNumbers({ - value, - className, - start = 0, - duration = 800, -}: { - value: number; - className: string; - start?: number; - duration?: number; -}) { - const [count, setCount] = useState(start); - - useEffect(() => { - let startTime: number | undefined; - const animateCount = (timestamp: number) => { - if (!startTime) startTime = timestamp; - const timePassed = timestamp - startTime; - const progress = timePassed / duration; - const currentCount = easeOutQuad(progress, 0, value, 1); - if (currentCount >= value) { - setCount(value); - return; - } - setCount(currentCount); - requestAnimationFrame(animateCount); - }; - requestAnimationFrame(animateCount); - }, [value, duration]); - - return

    {Intl.NumberFormat().format(count)}

    ; -} -const easeOutQuad = (t: number, b: number, c: number, d: number) => { - t = t > d ? d : t / d; - return Math.round(-c * t * (t - 2) + b); -}; diff --git a/apps/landing/components/shared/icons/buymeacoffee.tsx b/apps/landing/components/shared/icons/buymeacoffee.tsx deleted file mode 100644 index e52c1e6..0000000 --- a/apps/landing/components/shared/icons/buymeacoffee.tsx +++ /dev/null @@ -1,69 +0,0 @@ -export default function BuyMeACoffee({ className }: { className?: string }) { - return ( - - - - - - - - - - - - - - - - - ); -} diff --git a/apps/landing/components/shared/icons/expanding-arrow.tsx b/apps/landing/components/shared/icons/expanding-arrow.tsx deleted file mode 100644 index 819fd74..0000000 --- a/apps/landing/components/shared/icons/expanding-arrow.tsx +++ /dev/null @@ -1,36 +0,0 @@ -export default function ExpandingArrow({ className }: { className?: string }) { - return ( -
    - - - - - - -
    - ); -} diff --git a/apps/landing/components/shared/icons/github.tsx b/apps/landing/components/shared/icons/github.tsx deleted file mode 100644 index ff13f39..0000000 --- a/apps/landing/components/shared/icons/github.tsx +++ /dev/null @@ -1,14 +0,0 @@ -export default function Github({ className }: { className?: string }) { - return ( - - - - ); -} diff --git a/apps/landing/components/shared/icons/google.tsx b/apps/landing/components/shared/icons/google.tsx deleted file mode 100644 index 660f7b7..0000000 --- a/apps/landing/components/shared/icons/google.tsx +++ /dev/null @@ -1,47 +0,0 @@ -export default function Google({ className }: { className: string }) { - return ( - - - - - - - - - - - - - - - - - - {" "} - - ); -} diff --git a/apps/landing/components/shared/icons/index.tsx b/apps/landing/components/shared/icons/index.tsx deleted file mode 100644 index cbe7390..0000000 --- a/apps/landing/components/shared/icons/index.tsx +++ /dev/null @@ -1,8 +0,0 @@ -export { default as LoadingDots } from "./loading-dots"; -export { default as LoadingCircle } from "./loading-circle"; -export { default as LoadingSpinner } from "./loading-spinner"; -export { default as ExpandingArrow } from "./expanding-arrow"; -export { default as Github } from "./github"; -export { default as Twitter } from "./twitter"; -export { default as Google } from "./google"; -export { default as BuyMeACoffee } from "./buymeacoffee"; diff --git a/apps/landing/components/shared/icons/loading-circle.tsx b/apps/landing/components/shared/icons/loading-circle.tsx deleted file mode 100644 index 4649275..0000000 --- a/apps/landing/components/shared/icons/loading-circle.tsx +++ /dev/null @@ -1,20 +0,0 @@ -export default function LoadingCircle() { - return ( - - ); -} diff --git a/apps/landing/components/shared/icons/loading-dots.module.css b/apps/landing/components/shared/icons/loading-dots.module.css deleted file mode 100644 index 3b63902..0000000 --- a/apps/landing/components/shared/icons/loading-dots.module.css +++ /dev/null @@ -1,40 +0,0 @@ -.loading { - display: inline-flex; - align-items: center; -} - -.loading .spacer { - margin-right: 2px; -} - -.loading span { - animation-name: blink; - animation-duration: 1.4s; - animation-iteration-count: infinite; - animation-fill-mode: both; - width: 5px; - height: 5px; - border-radius: 50%; - display: inline-block; - margin: 0 1px; -} - -.loading span:nth-of-type(2) { - animation-delay: 0.2s; -} - -.loading span:nth-of-type(3) { - animation-delay: 0.4s; -} - -@keyframes blink { - 0% { - opacity: 0.2; - } - 20% { - opacity: 1; - } - 100% { - opacity: 0.2; - } -} diff --git a/apps/landing/components/shared/icons/loading-dots.tsx b/apps/landing/components/shared/icons/loading-dots.tsx deleted file mode 100644 index 23ebed0..0000000 --- a/apps/landing/components/shared/icons/loading-dots.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import styles from "./loading-dots.module.css"; - -const LoadingDots = ({ color = "#000" }: { color?: string }) => { - return ( - - - - - - ); -}; - -export default LoadingDots; diff --git a/apps/landing/components/shared/icons/loading-spinner.module.css b/apps/landing/components/shared/icons/loading-spinner.module.css deleted file mode 100644 index 8dc5706..0000000 --- a/apps/landing/components/shared/icons/loading-spinner.module.css +++ /dev/null @@ -1,79 +0,0 @@ -.spinner { - color: gray; - display: inline-block; - position: relative; - width: 80px; - height: 80px; - transform: scale(0.3) translateX(-95px); -} -.spinner div { - transform-origin: 40px 40px; - animation: spinner 1.2s linear infinite; -} -.spinner div:after { - content: " "; - display: block; - position: absolute; - top: 3px; - left: 37px; - width: 6px; - height: 20px; - border-radius: 20%; - background: black; -} -.spinner div:nth-child(1) { - transform: rotate(0deg); - animation-delay: -1.1s; -} -.spinner div:nth-child(2) { - transform: rotate(30deg); - animation-delay: -1s; -} -.spinner div:nth-child(3) { - transform: rotate(60deg); - animation-delay: -0.9s; -} -.spinner div:nth-child(4) { - transform: rotate(90deg); - animation-delay: -0.8s; -} -.spinner div:nth-child(5) { - transform: rotate(120deg); - animation-delay: -0.7s; -} -.spinner div:nth-child(6) { - transform: rotate(150deg); - animation-delay: -0.6s; -} -.spinner div:nth-child(7) { - transform: rotate(180deg); - animation-delay: -0.5s; -} -.spinner div:nth-child(8) { - transform: rotate(210deg); - animation-delay: -0.4s; -} -.spinner div:nth-child(9) { - transform: rotate(240deg); - animation-delay: -0.3s; -} -.spinner div:nth-child(10) { - transform: rotate(270deg); - animation-delay: -0.2s; -} -.spinner div:nth-child(11) { - transform: rotate(300deg); - animation-delay: -0.1s; -} -.spinner div:nth-child(12) { - transform: rotate(330deg); - animation-delay: 0s; -} -@keyframes spinner { - 0% { - opacity: 1; - } - 100% { - opacity: 0; - } -} diff --git a/apps/landing/components/shared/icons/loading-spinner.tsx b/apps/landing/components/shared/icons/loading-spinner.tsx deleted file mode 100644 index 1c8e704..0000000 --- a/apps/landing/components/shared/icons/loading-spinner.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import styles from "./loading-spinner.module.css"; - -export default function LoadingSpinner() { - return ( -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - ); -} diff --git a/apps/landing/components/shared/icons/twitter.tsx b/apps/landing/components/shared/icons/twitter.tsx deleted file mode 100644 index 693309d..0000000 --- a/apps/landing/components/shared/icons/twitter.tsx +++ /dev/null @@ -1,14 +0,0 @@ -export default function Twitter({ className }: { className?: string }) { - return ( - - - - ); -} diff --git a/apps/landing/components/shared/modal.tsx b/apps/landing/components/shared/modal.tsx deleted file mode 100644 index 6b0f5f8..0000000 --- a/apps/landing/components/shared/modal.tsx +++ /dev/null @@ -1,64 +0,0 @@ -"use client"; - -import { Dispatch, SetStateAction } from "react"; -import { cn } from "@/lib/utils"; -import { Drawer } from "vaul"; -import * as Dialog from "@radix-ui/react-dialog"; -import useMediaQuery from "@/lib/hooks/use-media-query"; - -export default function Modal({ - children, - className, - showModal, - setShowModal, -}: { - children: React.ReactNode; - className?: string; - showModal: boolean; - setShowModal: Dispatch>; -}) { - const { isMobile } = useMediaQuery(); - - if (isMobile) { - return ( - - - - -
    -
    -
    - {children} - - - - - ); - } - return ( - - - - e.preventDefault()} - onCloseAutoFocus={(e) => e.preventDefault()} - className={cn( - "animate-scale-in fixed inset-0 z-40 m-auto max-h-fit w-full max-w-md overflow-hidden border border-gray-200 bg-white p-0 shadow-xl md:rounded-2xl", - className, - )} - > - {children} - - - - ); -} diff --git a/apps/landing/components/shared/popover.tsx b/apps/landing/components/shared/popover.tsx deleted file mode 100644 index 928fc5f..0000000 --- a/apps/landing/components/shared/popover.tsx +++ /dev/null @@ -1,60 +0,0 @@ -"use client"; - -import { Dispatch, ReactNode, SetStateAction } from "react"; -import * as PopoverPrimitive from "@radix-ui/react-popover"; -import { Drawer } from "vaul"; -import useMediaQuery from "@/lib/hooks/use-media-query"; - -export default function Popover({ - children, - content, - align = "center", - openPopover, - setOpenPopover, -}: { - children: ReactNode; - content: ReactNode | string; - align?: "center" | "start" | "end"; - openPopover: boolean; - setOpenPopover: Dispatch>; - mobileOnly?: boolean; -}) { - const { isMobile } = useMediaQuery(); - - if (isMobile) { - return ( - -
    {children}
    - - - -
    -
    -
    -
    - {content} -
    - - - - - ); - } - - return ( - - - {children} - - - - {content} - - - - ); -} diff --git a/apps/landing/components/shared/tooltip.tsx b/apps/landing/components/shared/tooltip.tsx deleted file mode 100644 index d7cb4d3..0000000 --- a/apps/landing/components/shared/tooltip.tsx +++ /dev/null @@ -1,77 +0,0 @@ -"use client"; - -import { ReactNode } from "react"; -import * as TooltipPrimitive from "@radix-ui/react-tooltip"; -import { Drawer } from "vaul"; -import useMediaQuery from "@/lib/hooks/use-media-query"; - -export default function Tooltip({ - children, - content, - fullWidth, -}: { - children: ReactNode; - content: ReactNode | string; - fullWidth?: boolean; -}) { - const { isMobile } = useMediaQuery(); - - if (isMobile) { - return ( - - { - e.stopPropagation(); - }} - > - {children} - - - - -
    -
    -
    -
    - {typeof content === "string" ? ( - - {content} - - ) : ( - content - )} -
    - - - - - ); - } - return ( - - - - {children} - - {/* - We don't use TooltipPrimitive.Portal here because for some reason it - prevents you from selecting the contents of a tooltip when used inside a modal - */} - - {typeof content === "string" ? ( -
    - {content} -
    - ) : ( - content - )} -
    -
    -
    - ); -} diff --git a/apps/landing/lib/hooks/use-intersection-observer.ts b/apps/landing/lib/hooks/use-intersection-observer.ts index 516428d..1dfb63d 100644 --- a/apps/landing/lib/hooks/use-intersection-observer.ts +++ b/apps/landing/lib/hooks/use-intersection-observer.ts @@ -5,7 +5,7 @@ interface Args extends IntersectionObserverInit { } function useIntersectionObserver( - elementRef: RefObject, + elementRef: RefObject, { threshold = 0, root = null, diff --git a/apps/landing/lib/prisma.ts b/apps/landing/lib/prisma.ts deleted file mode 100644 index 10082dc..0000000 --- a/apps/landing/lib/prisma.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { PrismaClient } from "@prisma/client"; - -declare global { - var prisma: PrismaClient | undefined; -} - -const prisma = global.prisma || new PrismaClient(); - -if (process.env.NODE_ENV === "development") global.prisma = prisma; - -export default prisma; diff --git a/apps/landing/lib/utils.ts b/apps/landing/lib/utils.ts deleted file mode 100644 index 365058c..0000000 --- a/apps/landing/lib/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { type ClassValue, clsx } from "clsx"; -import { twMerge } from "tailwind-merge"; - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); -} diff --git a/apps/landing/next.config.js b/apps/landing/next.config.js index 48e73b1..f2fe77a 100644 --- a/apps/landing/next.config.js +++ b/apps/landing/next.config.js @@ -1,5 +1,31 @@ /** @type {import('next').NextConfig} */ const nextConfig = { + webpack: function (config) { + /** + * Critical: prevents " ⨯ ./node_modules/canvas/build/Release/canvas.node + * Module parse failed: Unexpected character '�' (1:0)" error + */ + config.resolve.alias.canvas = false; + + // You may not need this, it's just to support moduleResolution: 'node16' + config.resolve.extensionAlias = { + '.js': ['.js', '.ts', '.tsx'], + }; + + config.module.rules.push({ + test: /\.svg$/, + use: ['@svgr/webpack'], + }); + + return config; + }, + experimental: { + turbo: { + loaders: { + '.svg': ['@svgr/webpack'], + } + } + }, transpilePackages: ['@makify/ui'], reactStrictMode: true, swcMinify: true, diff --git a/apps/landing/package.json b/apps/landing/package.json index bae2a68..a161957 100644 --- a/apps/landing/package.json +++ b/apps/landing/package.json @@ -3,8 +3,8 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "prisma generate && next dev", - "build": "prisma generate && prisma db push && next build", + "dev": "next dev", + "build": "next build", "format:write": "prettier --write \"**/*.{css,js,json,jsx,ts,tsx}\"", "format": "prettier \"**/*.{css,js,json,jsx,ts,tsx}\"", "start": "next start", @@ -12,50 +12,26 @@ }, "dependencies": { "@makify/ui": "workspace:*", - "@next-auth/prisma-adapter": "1.0.7", - "@prisma/client": "4.16.2", - "@radix-ui/react-dialog": "1.0.5", - "@radix-ui/react-icons": "1.3.0", - "@radix-ui/react-navigation-menu": "1.1.4", - "@radix-ui/react-popover": "1.0.7", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-tooltip": "1.0.7", - "@vercel/analytics": "0.1.11", - "@vercel/og": "0.0.26", - "class-variance-authority": "0.7.0", - "classnames": "2.3.2", - "clsx": "2.1.0", - "eslint": "8.31.0", - "focus-trap-react": "10.2.2", - "framer-motion": "8.5.5", + "@upstash/ratelimit": "2.0.1", + "@vercel/kv": "2.0.0", + "framer-motion": "11.3.2", "lucide-react": "0.408.0", - "ms": "2.1.3", + "react-hook-form": "7.52.1", "next": "14.2.5", - "next-auth": "4.22.1", + "next-plausible": "3.12.2", "react": "18.3.1", "react-dom": "18.3.1", - "react-markdown": "8.0.7", - "tailwind-merge": "2.2.2", - "typescript": "5.2.2", - "use-debounce": "9.0.4", - "vaul": "0.6.8" + "resend": "4.0.0" }, "devDependencies": { - "@makify/eslint-config": "workspace:*", - "@makify/typescript-config": "workspace:*", - "@tailwindcss/forms": "0.5.6", - "@tailwindcss/line-clamp": "0.4.4", - "@tailwindcss/typography": "0.5.10", - "@types/node": "18.11.18", - "@types/ms": "0.7.32", + "@svgr/webpack": "^8.1.0", + "@types/node": "20", "@types/react": "18.2.61", "@types/react-dom": "18.2.19", "autoprefixer": "10.4.16", - "concurrently": "7.6.0", - "eslint-config-next": "14.2.5", "postcss": "8.4.31", - "prisma": "4.16.2", "tailwindcss": "3.4.14", - "tailwindcss-animate": "1.0.7" + "tailwindcss-animate": "1.0.7", + "zod": "3.23.8" } } \ No newline at end of file diff --git a/apps/landing/prisma/schema.prisma b/apps/landing/prisma/schema.prisma deleted file mode 100644 index a942483..0000000 --- a/apps/landing/prisma/schema.prisma +++ /dev/null @@ -1,61 +0,0 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - -generator client { - provider = "prisma-client-js" - previewFeatures = ["jsonProtocol"] -} - -datasource db { - provider = "postgresql" - url = env("POSTGRES_PRISMA_URL") // uses connection pooling - directUrl = env("POSTGRES_URL_NON_POOLING") // uses a direct connection -} - - -model Account { - id String @id @default(cuid()) - userId String - type String - provider String - providerAccountId String - refresh_token String? @db.Text - access_token String? @db.Text - expires_at Int? - token_type String? - scope String? - id_token String? @db.Text - session_state String? - - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - - @@unique([provider, providerAccountId]) - @@index([userId]) -} - -model Session { - id String @id @default(cuid()) - sessionToken String @unique - userId String - expires DateTime - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - @@index([userId]) -} - -model User { - id String @id @default(cuid()) - name String? - email String? @unique - emailVerified DateTime? - image String? - accounts Account[] - sessions Session[] -} - -model VerificationToken { - identifier String - token String @unique - expires DateTime - - @@unique([identifier, token]) -} diff --git a/apps/landing/public/icon1.svg b/apps/landing/public/icon1.svg new file mode 100644 index 0000000..b14f9b9 --- /dev/null +++ b/apps/landing/public/icon1.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/icon2.svg b/apps/landing/public/icon2.svg new file mode 100644 index 0000000..a674b7b --- /dev/null +++ b/apps/landing/public/icon2.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/logo-horizontal.svg b/apps/landing/public/logo-horizontal.svg new file mode 100644 index 0000000..111eb8d --- /dev/null +++ b/apps/landing/public/logo-horizontal.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/landing/public/logo.svg b/apps/landing/public/logo.svg new file mode 100644 index 0000000..5b2c77c --- /dev/null +++ b/apps/landing/public/logo.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + diff --git a/apps/landing/tsconfig.json b/apps/landing/tsconfig.json index a1f3e4c..1b2fe37 100644 --- a/apps/landing/tsconfig.json +++ b/apps/landing/tsconfig.json @@ -12,7 +12,7 @@ "baseUrl": ".", "paths": { "@/components/*": [ - "components/*" + "app/components/*" ], "@/pages/*": [ "pages/*" @@ -26,6 +26,9 @@ "@/styles/*": [ "styles/*" ], + "@/public/*": [ + "public/*" + ] }, "strict": true, "forceConsistentCasingInFileNames": true, diff --git a/apps/landing/turbo.json b/apps/landing/turbo.json new file mode 100644 index 0000000..d6d9e80 --- /dev/null +++ b/apps/landing/turbo.json @@ -0,0 +1,13 @@ +{ + "extends": [ + "//" + ], + "tasks": { + "build": { + "outputs": [ + ".next/**", + "!.next/cache/**" + ] + } + } +} \ No newline at end of file diff --git a/apps/landing/vercel.json b/apps/landing/vercel.json new file mode 100644 index 0000000..9bf517e --- /dev/null +++ b/apps/landing/vercel.json @@ -0,0 +1,12 @@ +{ + "rewrites": [ + { + "source": "/js/script.js", + "destination": "https://plausible.io/js/script.js" + }, + { + "source": "/api/event", + "destination": "https://plausible.io/api/event" + } + ] +} \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index fd38789..becb1fc 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/packages/ui/components/drawer.tsx b/packages/ui/components/drawer.tsx index 855ba81..72f7f11 100644 --- a/packages/ui/components/drawer.tsx +++ b/packages/ui/components/drawer.tsx @@ -1,9 +1,9 @@ -"use client" +"use client"; -import * as React from "react" -import { Drawer as DrawerPrimitive } from "vaul" +import * as React from "react"; +import { Drawer as DrawerPrimitive } from "vaul"; -import { cn } from "@makify/ui/lib/utils" +import { cn } from "@makify/ui/lib/utils"; const Drawer = ({ shouldScaleBackground = true, @@ -13,14 +13,14 @@ const Drawer = ({ shouldScaleBackground={shouldScaleBackground} {...props} /> -) -Drawer.displayName = "Drawer" +); +Drawer.displayName = "Drawer"; -const DrawerTrigger = DrawerPrimitive.Trigger +const DrawerTrigger = DrawerPrimitive.Trigger; -const DrawerPortal = DrawerPrimitive.Portal +const DrawerPortal = DrawerPrimitive.Portal; -const DrawerClose = DrawerPrimitive.Close +const DrawerClose = DrawerPrimitive.Close; const DrawerOverlay = React.forwardRef< React.ElementRef, @@ -31,8 +31,8 @@ const DrawerOverlay = React.forwardRef< className={cn("fixed inset-0 z-50 bg-black/80", className)} {...props} /> -)) -DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName +)); +DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName; const DrawerContent = React.forwardRef< React.ElementRef, @@ -43,17 +43,17 @@ const DrawerContent = React.forwardRef< -
    +
    {children} -)) -DrawerContent.displayName = "DrawerContent" +)); +DrawerContent.displayName = "DrawerContent"; const DrawerHeader = ({ className, @@ -63,8 +63,8 @@ const DrawerHeader = ({ className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)} {...props} /> -) -DrawerHeader.displayName = "DrawerHeader" +); +DrawerHeader.displayName = "DrawerHeader"; const DrawerFooter = ({ className, @@ -74,8 +74,8 @@ const DrawerFooter = ({ className={cn("mt-auto flex flex-col gap-2 p-4", className)} {...props} /> -) -DrawerFooter.displayName = "DrawerFooter" +); +DrawerFooter.displayName = "DrawerFooter"; const DrawerTitle = React.forwardRef< React.ElementRef, @@ -85,12 +85,12 @@ const DrawerTitle = React.forwardRef< ref={ref} className={cn( "text-lg font-semibold leading-none tracking-tight", - className + className, )} {...props} /> -)) -DrawerTitle.displayName = DrawerPrimitive.Title.displayName +)); +DrawerTitle.displayName = DrawerPrimitive.Title.displayName; const DrawerDescription = React.forwardRef< React.ElementRef, @@ -98,11 +98,11 @@ const DrawerDescription = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -DrawerDescription.displayName = DrawerPrimitive.Description.displayName +)); +DrawerDescription.displayName = DrawerPrimitive.Description.displayName; export { Drawer, @@ -115,4 +115,4 @@ export { DrawerFooter, DrawerTitle, DrawerDescription, -} +}; diff --git a/packages/ui/package.json b/packages/ui/package.json index 8cc8e0c..151ef3e 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -35,6 +35,7 @@ "@radix-ui/react-dropdown-menu": "2.1.1", "@radix-ui/react-icons": "1.3.0", "@radix-ui/react-label": "2.1.0", + "@radix-ui/react-navigation-menu": "1.2.1", "@radix-ui/react-popover": "1.1.1", "@radix-ui/react-scroll-area": "^1.1.0", "@radix-ui/react-select": "2.1.1",