Skip to content

Commit 35a4cc0

Browse files
Merge pull request #4 from Shitanshukumar607/add-hero-section-n-footer
Add UploadPage component and integrate Footer; update Navbar and Hero Section
2 parents 887dee6 + a393c51 commit 35a4cc0

File tree

9 files changed

+322
-39
lines changed

9 files changed

+322
-39
lines changed

app/generate/page.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
5+
export default function UploadPage() {
6+
const [file, setFile] = useState<File | null>(null);
7+
const [data, setData] = useState<string | null>(null);
8+
9+
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
10+
if (e.target.files) {
11+
setFile(e.target.files[0]);
12+
}
13+
};
14+
15+
const handleUpload = async () => {
16+
if (!file) return;
17+
18+
const formData = new FormData();
19+
formData.append("image", file);
20+
21+
const res = await fetch("/api/upload", {
22+
method: "POST",
23+
body: formData,
24+
});
25+
26+
const data = await res.json();
27+
setData(data.url + "\n\n" + data.svg);
28+
console.log("Response:", data);
29+
};
30+
31+
return (
32+
<div>
33+
<input type="file" accept="image/*" onChange={handleFileChange} />
34+
<button onClick={handleUpload}>Upload</button>
35+
<p>{data}</p>
36+
</div>
37+
);
38+
}

app/layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Navbar from "@/components/NavbarComponents/Navbar";
22
import { ThemeProvider } from "@/components/NavbarComponents/ThemeProvider";
33
import { Space_Grotesk } from "next/font/google";
44
import "./globals.css";
5+
import Footer from "@/components/Footer/Footer";
56

67
const spaceGrotesk = Space_Grotesk({
78
variable: "--font-space-grotesk",
@@ -25,6 +26,7 @@ export default function RootLayout({
2526
>
2627
<Navbar />
2728
<main>{children}</main>
29+
<Footer />
2830
</ThemeProvider>
2931
</body>
3032
</html>

app/page.tsx

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,7 @@
11
"use client";
22

3-
import { useState } from "react";
3+
import HeroSection from "@/components/Home/HeroSection";
44

5-
export default function UploadPage() {
6-
const [file, setFile] = useState<File | null>(null);
7-
const [data, setData] = useState<string | null>(null);
8-
9-
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
10-
if (e.target.files) {
11-
setFile(e.target.files[0]);
12-
}
13-
};
14-
15-
const handleUpload = async () => {
16-
if (!file) return;
17-
18-
const formData = new FormData();
19-
formData.append("image", file);
20-
21-
const res = await fetch("/api/upload", {
22-
method: "POST",
23-
body: formData,
24-
});
25-
26-
const data = await res.json();
27-
setData(data.url + "\n\n" + data.svg);
28-
console.log("Response:", data);
29-
};
30-
31-
return (
32-
<div>
33-
<input type="file" accept="image/*" onChange={handleFileChange} />
34-
<button onClick={handleUpload}>Upload</button>
35-
<p>{data}</p>
36-
</div>
37-
);
5+
export default function Page() {
6+
return <HeroSection />;
387
}

components/Footer/Footer.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"use client";
2+
3+
import Link from "next/link";
4+
5+
const Footer = () => {
6+
const currentYear = new Date().getFullYear();
7+
8+
return (
9+
<footer className="z-50 bg-white dark:bg-neutral-900 border-t border-slate-200 dark:border-neutral-800 text-center w-full mx-auto px-4 sm:px-6 lg:px-8 pt-6 pb-6">
10+
<p className="text-xs text-slate-500 dark:text-slate-400">
11+
© {currentYear} SVG From Img. All rights reserved. | Made with ❤️ by{" "}
12+
<Link className="underline" href="https://github.com/ShitanshuKumar607">
13+
Shitanshu Kumar
14+
</Link>
15+
</p>
16+
</footer>
17+
);
18+
};
19+
20+
export default Footer;

components/Home/HeroSection.tsx

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
"use client";
2+
3+
import { Button } from "@/components/ui/button";
4+
import { motion, Variants } from "framer-motion";
5+
import { ArrowRight, Trophy } from "lucide-react";
6+
import Link from "next/link";
7+
8+
const container: Variants = {
9+
hidden: { opacity: 0 },
10+
show: {
11+
opacity: 1,
12+
transition: {
13+
staggerChildren: 0.1,
14+
delayChildren: 0.3,
15+
},
16+
},
17+
};
18+
19+
const item: Variants = {
20+
hidden: { opacity: 0, y: 20 },
21+
show: {
22+
opacity: 1,
23+
y: 0,
24+
transition: {
25+
type: "spring",
26+
stiffness: 100,
27+
damping: 15,
28+
},
29+
},
30+
};
31+
32+
const HeroSection = () => {
33+
return (
34+
<section className="relative overflow-hidden bg-zinc-50 dark:bg-neutral-900/80">
35+
{/* Accent Corner Lines */}
36+
<div className="hidden sm:block absolute top-10 left-10 h-16 w-16 border-t-2 border-l-2 border-emerald-400 dark:border-violet-500 opacity-60 rounded-tl-lg" />
37+
<div className="hidden sm:block absolute bottom-10 right-10 h-20 w-20 border-b-2 border-r-2 border-emerald-400 dark:border-violet-500 opacity-60 rounded-br-lg" />
38+
39+
{/* Geometric Shape Decorations */}
40+
<div className="absolute top-1/3 -left-6 w-24 h-24 border border-emerald-300 dark:border-violet-400/40 rounded-full opacity-30" />
41+
<div className="absolute bottom-1/4 -right-8 w-20 h-20 border border-emerald-300 dark:border-violet-400/40 rotate-12 opacity-30" />
42+
43+
{/* Hero Content */}
44+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-20 md:py-32 relative">
45+
<motion.div
46+
className="text-center"
47+
initial="hidden"
48+
animate="show"
49+
variants={container}
50+
>
51+
{/* Badge */}
52+
<motion.div
53+
variants={item}
54+
className="inline-flex items-center gap-2 bg-emerald-100 text-emerald-700 dark:bg-violet-700/30 dark:text-violet-300 text-sm font-medium px-4 py-2 rounded-full mb-6"
55+
>
56+
<Trophy className="h-4 w-4" />
57+
<span>Fast & Secure Converter</span>
58+
</motion.div>
59+
60+
{/* Heading */}
61+
<motion.h1
62+
className="text-4xl sm:text-6xl font-bold tracking-tight max-w-4xl mx-auto leading-tight mb-6 text-slate-900 dark:text-neutral-100"
63+
variants={item}
64+
>
65+
Your Ultimate
66+
<span className="text-emerald-600 dark:text-violet-500"> SVG </span>
67+
Companion
68+
</motion.h1>
69+
70+
{/* Subtext */}
71+
<motion.p
72+
className="text-lg text-slate-600 dark:text-neutral-400 max-w-3xl mx-auto lg:text-xl mb-10"
73+
variants={item}
74+
>
75+
Effortlessly transform your PNG, JPG, and other raster images into
76+
high-quality, editable SVG vectors with a single click. Perfect for
77+
any creator.
78+
</motion.p>
79+
80+
<motion.div
81+
className="flex flex-col sm:flex-row gap-4 justify-center"
82+
variants={{
83+
hidden: { opacity: 0, y: 20 },
84+
show: {
85+
opacity: 1,
86+
y: 0,
87+
transition: {
88+
staggerChildren: 0.1,
89+
delayChildren: 0.4,
90+
},
91+
},
92+
}}
93+
>
94+
<motion.div
95+
whileHover={{ scale: 1.05 }}
96+
whileTap={{ scale: 0.98 }}
97+
variants={item}
98+
>
99+
<Link href="/generate">
100+
<Button
101+
size="lg"
102+
className="gap-2 bg-emerald-600 text-white hover:bg-emerald-700 dark:bg-violet-600 dark:hover:bg-violet-700 transition-transform cursor-pointer"
103+
>
104+
Upload Image & Convert
105+
<ArrowRight className="h-5 w-5" />
106+
</Button>
107+
</Link>
108+
</motion.div>
109+
</motion.div>
110+
</motion.div>
111+
</div>
112+
</section>
113+
);
114+
};
115+
116+
export default HeroSection;

components/NavbarComponents/Navbar.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ export default function Navbar() {
2222
className="text-lg font-bold text-slate-800 dark:text-neutral-100"
2323
>
2424
SVG{" "}
25-
<span className="text-emerald-600 dark:text-violet-500">
26-
From Img
27-
</span>
25+
<span className="text-emerald-600 dark:text-violet-500">From</span>{" "}
26+
Img
2827
</Link>
2928
</div>
3029

components/ui/button.tsx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import * as React from "react";
2+
import { Slot } from "@radix-ui/react-slot";
3+
import { cva, type VariantProps } from "class-variance-authority";
4+
5+
import { cn } from "@/lib/utils";
6+
7+
const buttonVariants = cva(
8+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9+
{
10+
variants: {
11+
variant: {
12+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
13+
destructive:
14+
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
15+
outline:
16+
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
17+
secondary:
18+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
19+
ghost:
20+
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
21+
link: "text-primary underline-offset-4 hover:underline",
22+
},
23+
size: {
24+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
25+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
26+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
27+
icon: "size-9",
28+
"icon-sm": "size-8",
29+
"icon-lg": "size-10",
30+
},
31+
},
32+
defaultVariants: {
33+
variant: "default",
34+
size: "default",
35+
},
36+
},
37+
);
38+
39+
function Button({
40+
className,
41+
variant,
42+
size,
43+
asChild = false,
44+
...props
45+
}: React.ComponentProps<"button"> &
46+
VariantProps<typeof buttonVariants> & {
47+
asChild?: boolean;
48+
}) {
49+
const Comp = asChild ? Slot : "button";
50+
51+
return (
52+
<Comp
53+
data-slot="button"
54+
className={cn(buttonVariants({ variant, size, className }))}
55+
{...props}
56+
/>
57+
);
58+
}
59+
60+
export { Button, buttonVariants };

0 commit comments

Comments
 (0)