+
+
+
-
-
- {uploadedFiles.length > 0 && (
-
-
-
- Uploaded Files ({uploadedFiles.length})
-
- {uploadedFiles.length > 1 && (
-
- )}
-
-
-
- {uploadedFiles.map((file, index) => (
-
+
+
+ Brown
+ Blue
+ Hazel
+ Gray
+
+
+
+
+
+
+
+
+ {uploadedFiles.length > 0 && (
+
+
+
+ Uploaded Files ({uploadedFiles.length})
+
+ {uploadedFiles.length > 1 && (
+
+ )}
+
+
+
+ {uploadedFiles.map((file, index) => (
+
-
-
-
-
-
- {file.name}
-
-
-
-
- {new Date(
- file.timestamp
- ).toLocaleTimeString()}
-
-
-
- )}
-
-
-
-
-
-
- {modelTraining ? (
- <>
- {trainingStatus
- ? `Training: ${trainingStatus}...`
- : "Training..."}
- >
- ) : (
- <>Train Model (20 credits)>
- )}
-
-
-
-
-
-
- Image Preview
- Preview of your uploaded images
-
-
- {previewFiles.length > 0 ? (
-
-
- {previewFiles.map((file, index) => {
- const imageUrl = URL.createObjectURL(file);
- return (
-
-
-
-
- URL.revokeObjectURL(imageUrl)}
- />
+ title="Remove file"
+ >
+
+
-
- {file.name}
-
+
+ ))}
+
+
+
+ )}
+
+
+
+
+
+
+ {modelTraining ? (
+ <>
+ {trainingStatus
+ ? `Training: ${trainingStatus}...`
+ : "Training..."}
+ >
+ ) : (
+ <>Train Model (20 credits)>
+ )}
+
+
+
+
+
+
+ Image Preview
+ Preview of your uploaded images
+
+
+ {previewFiles.length > 0 ? (
+
+
+ {previewFiles.map((file, index) => {
+ const imageUrl = URL.createObjectURL(file);
+ return (
+
+
+
+
+ URL.revokeObjectURL(imageUrl)}
+ />
+
+ {file.name}
+
-
- );
- })}
-
-
-
-
- ) : (
-
-
-
-
-
No images uploaded
-
- Upload images using the form on the left to see previews
- here
-
+
+
+ );
+ })}
+
+
+
+
+ ) : (
+
+
No images uploaded
+
+ Upload images using the form on the left to see previews
+ here
+
+
+ )}
+
+
);
diff --git a/apps/web/components/home/Hero.tsx b/apps/web/components/home/Hero.tsx
deleted file mode 100644
index 5d643039..00000000
--- a/apps/web/components/home/Hero.tsx
+++ /dev/null
@@ -1,128 +0,0 @@
-"use client";
-
-import { BackgroundEffects } from "./BackgroundEffects";
-import { HeroHeader } from "./HeroHeader";
-import { Features } from "./Features";
-import { Testimonials } from "./Testimonials";
-import { ImageCarousel } from "./ImageCarousel";
-import { SignInButton, SignedOut } from "@clerk/nextjs";
-import { Button } from "@/components/ui/button";
-import { ArrowRight, CheckCircle, Clock, Sparkles } from "lucide-react";
-import { motion } from "framer-motion";
-import { ScrollIndicator } from "./ScrollIndicator";
-import { StatsSection } from "./StatsSection";
-import { PricingSection } from "./PricingSection";
-import { HowItWorks } from "./HowItWorks";
-// import { TrustedBy } from "./TrustedBy";
-
-export function Hero() {
- return (
-
-
-
-
-
-
-
-
- {/* */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Start Your AI Portrait Journey Today
-
-
- Join thousands of creators who have already transformed their
- photos with our AI technology.
-
-
-
-
-
-
-
- Get Started Free
-
-
-
-
-
- document
- .getElementById("features")
- ?.scrollIntoView({ behavior: "smooth" })
- }
- >
- Learn More
-
-
-
-
-
-
-
-
- No credit card required
-
- •
-
-
- Free credits to start
-
- •
-
-
- Cancel anytime
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/web/components/home/HeroHeader.tsx b/apps/web/components/home/HeroHeader.tsx
deleted file mode 100644
index f2348c54..00000000
--- a/apps/web/components/home/HeroHeader.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-"use client";
-
-import { SignInButton, SignedIn, SignedOut } from "@clerk/nextjs";
-import { Button } from "@/components/ui/button";
-import { useRouter } from "next/navigation";
-import { motion } from "framer-motion";
-import { ArrowRight, Sparkles, Zap } from "lucide-react";
-
-export function HeroHeader() {
- const router = useRouter();
-
- return (
-
-
-
-
- Next-Gen AI Portrait Generation
-
-
-
- Powered by 100xDevs
-
-
-
-
- Transform Your Photos with{" "}
-
- AI Magic
-
-
-
-
-
-
-
-
- Start Creating Now
-
-
-
-
-
-
- router.push("/dashboard")}
- className="group relative px-8 py-6 text-lg bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white rounded-full transition-all duration-300 shadow-lg hover:shadow-xl hover:-translate-y-1"
- >
- Go to Dashboard
-
-
-
-
-
- );
-}
diff --git a/apps/web/components/home/HowItWorks.tsx b/apps/web/components/home/HowItWorks.tsx
deleted file mode 100644
index a24f57df..00000000
--- a/apps/web/components/home/HowItWorks.tsx
+++ /dev/null
@@ -1,76 +0,0 @@
-"use client";
-
-import { motion } from "framer-motion";
-import { useInView } from "framer-motion";
-import { useRef } from "react";
-import { Upload, Wand2, Download } from "lucide-react";
-
-const steps = [
- {
- icon:
,
- title: "Upload Your Photo",
- description: "Start by uploading any portrait photo you'd like to enhance",
- },
- {
- icon:
,
- title: "AI Magic",
- description:
- "Our advanced AI transforms your photo into stunning portraits",
- },
- {
- icon:
,
- title: "Download & Share",
- description: "Get your enhanced portraits in multiple styles instantly",
- },
-];
-
-export function HowItWorks() {
- const ref = useRef(null);
- const isInView = useInView(ref, { once: true });
-
- return (
-
-
-
- How It{" "}
-
- Works
-
-
-
- Transform your photos into stunning AI-powered portraits in three
- simple steps
-
-
-
-
- {steps.map((step, index) => (
-
-
-
-
- {step.icon}
-
-
- {step.title}
-
-
{step.description}
-
-
- ))}
-
-
- );
-}
diff --git a/apps/web/components/home/ImageCarousel.tsx b/apps/web/components/home/ImageCarousel.tsx
deleted file mode 100644
index d43ed044..00000000
--- a/apps/web/components/home/ImageCarousel.tsx
+++ /dev/null
@@ -1,185 +0,0 @@
-"use client";
-
-import { motion } from "framer-motion";
-import { ArrowRight, ArrowLeft, Sparkles } from "lucide-react";
-import useEmblaCarousel from "embla-carousel-react";
-import Autoplay from "embla-carousel-autoplay";
-import { useEffect, useState, useCallback } from "react";
-import { carouselImages } from "./data";
-import Image from "next/image";
-
-export function ImageCarousel() {
- const [emblaRef, emblaApi] = useEmblaCarousel(
- {
- loop: true,
- align: "center",
- skipSnaps: false,
- dragFree: true,
- },
- [
- Autoplay({
- delay: 4000,
- stopOnInteraction: false,
- stopOnMouseEnter: true,
- }),
- ]
- );
-
- const [selectedIndex, setSelectedIndex] = useState(0);
- const [scrollSnaps, setScrollSnaps] = useState
([]);
-
- const scrollPrev = useCallback(
- () => emblaApi && emblaApi.scrollPrev(),
- [emblaApi]
- );
- const scrollNext = useCallback(
- () => emblaApi && emblaApi.scrollNext(),
- [emblaApi]
- );
- const scrollTo = useCallback(
- (index: number) => emblaApi && emblaApi.scrollTo(index),
- [emblaApi]
- );
-
- const onSelect = useCallback(() => {
- if (!emblaApi) return;
- setSelectedIndex(emblaApi.selectedScrollSnap());
- }, [emblaApi]);
-
- useEffect(() => {
- if (!emblaApi) return;
- onSelect();
- setScrollSnaps(emblaApi.scrollSnapList());
- emblaApi.on("select", onSelect);
- emblaApi.on("reInit", onSelect);
- }, [emblaApi, onSelect]);
-
- return (
-
- {/* Floating Elements */}
-
-
-
-
-
- {carouselImages.map((image, index) => (
-
-
-
-
-
-
-
-
-
-
- {typeof image === "object" && "style" in image
- ? image.style
- : ""}
-
-
-
- {typeof image === "object" && "title" in image
- ? image.title
- : ""}
-
-
- {typeof image === "object" && "description" in image
- ? image.description
- : ""}
-
-
-
-
-
- ))}
-
-
-
- {/* Navigation */}
-
-
-
-
-
-
- {scrollSnaps.map((_, i) => (
-
scrollTo(i)}
- >
-
- {i === selectedIndex && (
-
- )}
-
-
- ))}
-
-
-
-
-
-
-
-
- );
-}
diff --git a/apps/web/components/home/PricingSection.tsx b/apps/web/components/home/PricingSection.tsx
deleted file mode 100644
index 3c50ff87..00000000
--- a/apps/web/components/home/PricingSection.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-"use client";
-
-import { motion } from "framer-motion";
-import { useInView } from "framer-motion";
-import { useRef } from "react";
-import { Button } from "@/components/ui/button";
-import { Check } from "lucide-react";
-import { plans } from "./data";
-
-export function PricingSection() {
- const ref = useRef(null);
- const isInView = useInView(ref, { once: true });
-
- return (
-
-
-
- Simple,{" "}
-
- Transparent
- {" "}
- Pricing
-
-
- Choose the perfect plan for your needs. No hidden fees.
-
-
-
-
- {plans.map((plan, index) => (
-
-
-
-
{plan.name}
-
{plan.price}
-
- {plan.features.map((feature) => (
- -
-
- {feature}
-
- ))}
-
-
- Get Started
-
-
-
-
- ))}
-
-
- );
-}
diff --git a/apps/web/components/home/BackgroundEffects.tsx b/apps/web/components/landing/BackgroundEffects.tsx
similarity index 100%
rename from apps/web/components/home/BackgroundEffects.tsx
rename to apps/web/components/landing/BackgroundEffects.tsx
diff --git a/apps/web/components/home/Features.tsx b/apps/web/components/landing/Features.tsx
similarity index 100%
rename from apps/web/components/home/Features.tsx
rename to apps/web/components/landing/Features.tsx
diff --git a/apps/web/components/landing/Header.tsx b/apps/web/components/landing/Header.tsx
new file mode 100644
index 00000000..44078b2b
--- /dev/null
+++ b/apps/web/components/landing/Header.tsx
@@ -0,0 +1,36 @@
+"use client";
+
+import { SignInButton, SignedOut } from "@clerk/nextjs";
+import { Button } from "@/components/ui/button";
+import { PlayIcon } from "lucide-react";
+
+// think if putting dashboard button if user is signed in
+
+export function Header() {
+ return (
+ <>
+
+
Transform your photos with photo.ai
+
+
+ Photo.AI transforms your ideas into breathtaking, high-quality images effortlessly powered by AI, designed for limitless creativity.
+
+
+
+
+
+
+
+
+ Generate now
+
+
+
+
+ >
+
+ );
+}
diff --git a/apps/web/components/landing/HowItWorks.tsx b/apps/web/components/landing/HowItWorks.tsx
new file mode 100644
index 00000000..511c1acf
--- /dev/null
+++ b/apps/web/components/landing/HowItWorks.tsx
@@ -0,0 +1,27 @@
+"use client";
+
+import { workingStep } from "@/lib/constants";
+
+export function HowItWorks() {
+ return (
+
+
+ {workingStep.map((step) => (
+
+
+
+
+
+
{step.title}
+
+ {step.description}
+
+
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/apps/web/components/landing/ImageStack.tsx b/apps/web/components/landing/ImageStack.tsx
new file mode 100644
index 00000000..ec19e9af
--- /dev/null
+++ b/apps/web/components/landing/ImageStack.tsx
@@ -0,0 +1,30 @@
+"use client";
+
+import { stackImages } from '@/lib/constants'
+import { cn } from '@/lib/utils'
+import Image from 'next/image'
+
+export const ImageStack = () => {
+ return (
+
+ {stackImages.map((image, _idx) => (
+
+
+
+ ))}
+
+ )
+}
+
diff --git a/apps/web/components/landing/Landing.tsx b/apps/web/components/landing/Landing.tsx
new file mode 100644
index 00000000..fed0ed39
--- /dev/null
+++ b/apps/web/components/landing/Landing.tsx
@@ -0,0 +1,68 @@
+"use client";
+
+import { BackgroundEffects } from "./BackgroundEffects";
+import { Header } from "./Header";
+import { ImageStack } from "./ImageStack";
+import { PricingSection } from "./PricingSection";
+import { HowItWorks } from "./HowItWorks";
+import { Appbar } from "@/components/Appbar"
+import { Footer } from "@/components/Footer"
+
+// background effects is useful components, figure out where to use it
+
+const H2 = ({ children }: { children: React.ReactNode }) => (
+ {children}
+)
+
+const P = ({ children }: { children: React.ReactNode }) => (
+
+ {children}
+
+)
+
+export function Landing() {
+ return (
+ <>
+
+
+
+
+
+
+ {/*
*/}
+
+ {/*
*/}
+
+ {/*
*/}
+
+ {/*
*/}
+
+ {/*
*/}
+
+
+
+
Just upload. Photo.ai enhances your images.
+
Explore the features that transform your photos into stunning visuals with AI-powered precision.
+
+
+
+
+
+
+
+
Just pick a plan. Unlock instantly.
+
No matter your needs, we have the right plan for you. Choose one and start creating today!
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/apps/web/components/landing/PricingSection.tsx b/apps/web/components/landing/PricingSection.tsx
new file mode 100644
index 00000000..96fdabfb
--- /dev/null
+++ b/apps/web/components/landing/PricingSection.tsx
@@ -0,0 +1,48 @@
+"use client";
+
+import { plans } from "@/lib/constants";
+import { Button } from '@/components/ui/button'
+import { CircleCheck } from 'lucide-react'
+import { cn } from '@/lib/utils'
+
+export function PricingSection() {
+ return (
+
+
+ {plans.map((plan) => (
+
+
+
+
+
{plan.price}
+ {plan.features.map((feature, idx) => (
+
+ ))}
+
+
+
+
+ Get Started
+
+
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/web/components/home/ScrollIndicator.tsx b/apps/web/components/landing/ScrollIndicator.tsx
similarity index 100%
rename from apps/web/components/home/ScrollIndicator.tsx
rename to apps/web/components/landing/ScrollIndicator.tsx
diff --git a/apps/web/components/home/StatsSection.tsx b/apps/web/components/landing/StatsSection.tsx
similarity index 100%
rename from apps/web/components/home/StatsSection.tsx
rename to apps/web/components/landing/StatsSection.tsx
diff --git a/apps/web/components/home/Testimonials.tsx b/apps/web/components/landing/Testimonials.tsx
similarity index 100%
rename from apps/web/components/home/Testimonials.tsx
rename to apps/web/components/landing/Testimonials.tsx
diff --git a/apps/web/components/home/TrustedBy.tsx b/apps/web/components/landing/TrustedBy.tsx
similarity index 100%
rename from apps/web/components/home/TrustedBy.tsx
rename to apps/web/components/landing/TrustedBy.tsx
diff --git a/apps/web/components/home/data.ts b/apps/web/components/landing/data.ts
similarity index 100%
rename from apps/web/components/home/data.ts
rename to apps/web/components/landing/data.ts
diff --git a/apps/web/components/ui/textarea.tsx b/apps/web/components/ui/textarea.tsx
index e56b0aff..96cf758f 100644
--- a/apps/web/components/ui/textarea.tsx
+++ b/apps/web/components/ui/textarea.tsx
@@ -9,7 +9,7 @@ const Textarea = React.forwardRef<
return (