Merge pull request 'fahim-dev' (#3) from fahim-dev into main

Reviewed-on: https://git.planpostai.com/planpostai/planpost-landing-v2/pulls/3
This commit is contained in:
smfahim25 2025-03-03 08:31:04 +00:00
commit cf1118a026
5 changed files with 258 additions and 104 deletions

73
package-lock.json generated
View file

@ -12,9 +12,9 @@
"@radix-ui/react-label": "^2.1.2", "@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.1.3", "@radix-ui/react-switch": "^1.1.3",
"aos": "^2.3.4",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"framer-motion": "^12.4.7",
"lucide-react": "^0.476.0", "lucide-react": "^0.476.0",
"next": "15.1.7", "next": "15.1.7",
"react": "^19.0.0", "react": "^19.0.0",
@ -1682,17 +1682,6 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/aos": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/aos/-/aos-2.3.4.tgz",
"integrity": "sha512-zh/ahtR2yME4I51z8IttIt4lC1Nw0ktsFtmeDzID1m9naJnWXhCoARaCgNOGXb5CLy3zm+wqmRAEgMYB5E2HUw==",
"license": "MIT",
"dependencies": {
"classlist-polyfill": "^1.0.3",
"lodash.debounce": "^4.0.6",
"lodash.throttle": "^4.0.1"
}
},
"node_modules/arg": { "node_modules/arg": {
"version": "5.0.2", "version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
@ -2144,12 +2133,6 @@
"url": "https://polar.sh/cva" "url": "https://polar.sh/cva"
} }
}, },
"node_modules/classlist-polyfill": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/classlist-polyfill/-/classlist-polyfill-1.2.0.tgz",
"integrity": "sha512-GzIjNdcEtH4ieA2S8NmrSxv7DfEV5fmixQeyTmqmRmRJPGpRBaSnA2a0VrCjyT8iW8JjEdMbKzDotAJf+ajgaQ==",
"license": "Unlicense"
},
"node_modules/client-only": { "node_modules/client-only": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
@ -3228,6 +3211,33 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/framer-motion": {
"version": "12.4.7",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.4.7.tgz",
"integrity": "sha512-VhrcbtcAMXfxlrjeHPpWVu2+mkcoR31e02aNSR7OUS/hZAciKa8q6o3YN2mA1h+jjscRsSyKvX6E1CiY/7OLMw==",
"license": "MIT",
"dependencies": {
"motion-dom": "^12.4.5",
"motion-utils": "^12.0.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fsevents": { "node_modules/fsevents": {
"version": "2.3.3", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@ -4234,12 +4244,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
"license": "MIT"
},
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@ -4247,12 +4251,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash.throttle": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
"integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==",
"license": "MIT"
},
"node_modules/loose-envify": { "node_modules/loose-envify": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@ -4345,6 +4343,21 @@
"node": ">=16 || 14 >=14.17" "node": ">=16 || 14 >=14.17"
} }
}, },
"node_modules/motion-dom": {
"version": "12.4.5",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.4.5.tgz",
"integrity": "sha512-Q2xmhuyYug1CGTo0jdsL05EQ4RhIYXlggFS/yPhQQRNzbrhjKQ1tbjThx5Plv68aX31LsUQRq4uIkuDxdO5vRQ==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.0.0"
}
},
"node_modules/motion-utils": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.0.0.tgz",
"integrity": "sha512-MNFiBKbbqnmvOjkPyOKgHUp3Q6oiokLkI1bEwm5QA28cxMZrv0CbbBGDNmhF6DIXsi1pCQBSs0dX8xjeER1tmA==",
"license": "MIT"
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",

View file

@ -13,9 +13,9 @@
"@radix-ui/react-label": "^2.1.2", "@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-switch": "^1.1.3", "@radix-ui/react-switch": "^1.1.3",
"aos": "^2.3.4",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"framer-motion": "^12.4.7",
"lucide-react": "^0.476.0", "lucide-react": "^0.476.0",
"next": "15.1.7", "next": "15.1.7",
"react": "^19.0.0", "react": "^19.0.0",

View file

@ -1,6 +1,8 @@
"use client"; "use client";
import { X, Plus } from "lucide-react";
import { useState } from "react"; import { Plus } from "lucide-react";
import * as React from "react";
import { motion, AnimatePresence } from "framer-motion";
interface FAQItem { interface FAQItem {
id: string; id: string;
@ -36,7 +38,7 @@ const faqs: FAQItem[] = [
]; ];
export default function FAQSection() { export default function FAQSection() {
const [openItems, setOpenItems] = useState<Set<string>>(new Set()); const [openItems, setOpenItems] = React.useState<Set<string>>(new Set());
const handleItemClick = (itemId: string) => { const handleItemClick = (itemId: string) => {
const newOpenItems = new Set(openItems); const newOpenItems = new Set(openItems);
@ -57,27 +59,86 @@ export default function FAQSection() {
<div className="space-y-4"> <div className="space-y-4">
{faqs.map((faq) => ( {faqs.map((faq) => (
<div key={faq.id} className="bg-white rounded-lg overflow-hidden"> <motion.div
<button key={faq.id}
initial={false}
animate={openItems.has(faq.id) ? "open" : "closed"}
className="bg-white rounded-lg overflow-hidden"
>
<motion.button
onClick={() => handleItemClick(faq.id)} onClick={() => handleItemClick(faq.id)}
className="w-full text-left p-6 flex items-center justify-between hover:bg-gray-50" className="w-full text-left p-6 flex items-center justify-between hover:bg-gray-50"
whileHover={{ backgroundColor: "rgba(0, 0, 0, 0.02)" }}
transition={{ duration: 0.2 }}
> >
<span className="text-lg text-[#7c3aed] font-medium"> <span className="text-lg text-[#7c3aed] font-medium">
{faq.question} {faq.question}
</span> </span>
{openItems.has(faq.id) ? ( <motion.div
<X className="h-5 w-5 text-[#7c3aed]" /> variants={{
) : ( open: { rotate: 45 },
<Plus className="h-5 w-5 text-[#7c3aed]" /> closed: { rotate: 0 },
)} }}
</button> transition={{ duration: 0.2 }}
className="relative w-6 h-6 flex items-center justify-center"
>
<Plus className="h-5 w-5 text-[#7c3aed] absolute" />
</motion.div>
</motion.button>
{openItems.has(faq.id) && ( <AnimatePresence initial={false}>
<div className="px-6 pb-6"> {openItems.has(faq.id) && (
<p className="text-gray-600">{faq.answer}</p> <motion.div
</div> initial="collapsed"
)} animate="open"
</div> exit="collapsed"
variants={{
open: {
opacity: 1,
height: "auto",
transition: {
duration: 0.3,
ease: [0.4, 0, 0.2, 1],
},
},
collapsed: {
opacity: 0,
height: 0,
transition: {
duration: 0.3,
ease: [0.4, 0, 0.2, 1],
},
},
}}
>
<motion.div
variants={{
open: {
y: 0,
opacity: 1,
transition: {
duration: 0.3,
ease: [0.4, 0, 0.2, 1],
delay: 0.1,
},
},
collapsed: {
y: 20,
opacity: 0,
transition: {
duration: 0.3,
ease: [0.4, 0, 0.2, 1],
},
},
}}
className="px-6 pb-6"
>
<p className="text-gray-600">{faq.answer}</p>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</motion.div>
))} ))}
</div> </div>
</div> </div>

View file

@ -1,7 +1,11 @@
"use client";
import React from "react"; import React from "react";
import styles from "./HeroSection.module.css"; import styles from "./HeroSection.module.css";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import { motion } from "framer-motion";
const HeroSection: React.FC = () => { const HeroSection: React.FC = () => {
return ( return (
<section className="header"> <section className="header">
@ -9,34 +13,53 @@ const HeroSection: React.FC = () => {
<div className="absolute w-full flex flex-col md:flex-row"> <div className="absolute w-full flex flex-col md:flex-row">
{/* Left Background */} {/* Left Background */}
<div <div
className={`${styles.bgLeft} w-full md:w-1/2 h-[500px] md:h-[1075px]`} className={`${styles.bgLeft} w-full md:w-1/2 h-[410px] md:h-[1075px]`}
></div> ></div>
{/* Right Background */} {/* Right Background */}
<div className="relative w-full md:w-1/2 bg-[#F9F1FF] h-[400px] md:h-[1075px]"> <div className="relative w-full md:w-1/2 bg-[#F9F1FF] h-[400px] md:h-[1075px]">
<div <motion.div
initial={{ x: 100, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
transition={{
duration: 0.8,
ease: "easeOut",
delay: 0.2,
}}
className="absolute top-0 -left-[150px] mt-0 md:mt-40 ml-16 h-[542px] flex flex-col justify-start items-start gap-2.5 z-50" className="absolute top-0 -left-[150px] mt-0 md:mt-40 ml-16 h-[542px] flex flex-col justify-start items-start gap-2.5 z-50"
data-aos="fade-left"
data-aos-duration="2000"
> >
<Image <motion.div
src="/assets/images/hero-temp-slider.png" whileHover={{
alt="Hero Slider" scale: 1.02,
width={750} transition: { duration: 0.3 },
height={542} }}
priority >
/> <Image
</div> src="/assets/images/hero-temp-slider.png"
alt="Hero Slider"
width={750}
height={542}
priority
className="drop-shadow-2xl"
/>
</motion.div>
</motion.div>
</div> </div>
{/* Main Content */} {/* Main Content */}
<div <motion.div
initial={{ y: 50, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{
duration: 0.6,
ease: "easeOut",
}}
className={`${styles.glass} absolute my-20 md:my-40 w-full md:w-7/12 z-10`} className={`${styles.glass} absolute my-20 md:my-40 w-full md:w-7/12 z-10`}
> >
<div className="p-8 md:p-16"> <div className="p-8 md:p-16">
<div className="text-2xl md:text-4xl lg:text-6xl leading-5xl font-normal font-['Gendy'] "> <div className="text-2xl md:text-4xl lg:text-6xl leading-5xl font-normal font-['Gendy']">
<span className="text-[#ff256d] md:text-white ">R</span> <span className="text-[#ff256d] md:text-white">R</span>
<span className="text-[#252525] "> <span className="text-[#252525]">
evolutionize Your Social Media Strategy with a Personalized evolutionize Your Social Media Strategy with a Personalized
</span> </span>
<br /> <br />
@ -49,7 +72,12 @@ const HeroSection: React.FC = () => {
</p> </p>
<div className="flex"> <div className="flex">
<div className="flex mt-5 items-center"> <motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.4, duration: 0.6 }}
className="flex mt-5 items-center"
>
<div> <div>
<p className="text-[#252525] text-[10px] md:text-xs font-normal"> <p className="text-[#252525] text-[10px] md:text-xs font-normal">
Customers Rating Customers Rating
@ -61,37 +89,63 @@ const HeroSection: React.FC = () => {
(8,279 reviews) (8,279 reviews)
</p> </p>
</div> </div>
</div> </motion.div>
{/* CTA Button */} {/* CTA Button */}
<Link <motion.div
href="https://dashboard.planpostai.com/" initial={{ opacity: 0, x: 20 }}
className="ml-5 mt-5 px-7 py-3.5 bg-[#ff2b85] rounded-xl shadow-md border border-white flex items-center gap-2.5 text-white text-xl font-bold" animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.6, duration: 0.6 }}
> >
Generate <Link
<Image href="https://dashboard.planpostai.com/"
src="/assets/svg/generate-icon.svg" className="ml-5 mt-5 px-7 py-3.5 bg-[#ff2b85] rounded-xl shadow-md border border-white flex items-center gap-2.5 text-white text-xl font-bold"
alt="Generate Icon" >
className="w-6 h-6" Generate
height={24} <Image
width={24} src="/assets/svg/generate-icon.svg"
/> alt="Generate Icon"
</Link> className="w-6 h-6"
height={24}
width={24}
/>
</Link>
</motion.div>
</div> </div>
{/* Customer Reviews */}
</div> </div>
</div> </motion.div>
</div> </div>
{/* Social Icons */} {/* Social Icons */}
<div className="absolute top-[142px] left-[77px] social-icons-pp z-20"> <motion.div
<Image initial={{ scale: 0.8, opacity: 0 }}
src="/assets/images/social-icons.png" animate={{ scale: 1, opacity: 1 }}
alt="Social Icons" transition={{
height={500} duration: 0.8,
width={500} ease: [0.4, 0, 0.2, 1],
/> delay: 0.4,
</div> }}
className="absolute top-[142px] left-[77px] social-icons-pp z-20"
>
<motion.div
animate={{
y: [0, -10, 0],
}}
transition={{
duration: 4,
repeat: Infinity,
repeatType: "reverse",
ease: "easeInOut",
}}
>
<Image
src="/assets/images/social-icons.png"
alt="Social Icons"
height={500}
width={500}
/>
</motion.div>
</motion.div>
</div> </div>
</section> </section>
); );

View file

@ -1,24 +1,50 @@
"use client";
import React, { useRef } from "react";
import Image from "next/image"; import Image from "next/image";
import React from "react"; import { motion, useInView } from "framer-motion";
const SocialSection: React.FC = () => { const SocialSection: React.FC = () => {
const sectionRef = useRef<HTMLDivElement>(null);
const isInView = useInView(sectionRef, {
once: false,
amount: 0.3,
});
const fadeInUp = {
hidden: {
opacity: 0,
y: 60,
},
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.8,
ease: "easeOut",
},
},
};
return ( return (
<section <section ref={sectionRef} id="feature" className="social">
id="feature"
className="social"
data-aos="fade-up"
data-aos-duration="1500"
>
<div className="h-0 md:h-[180px] lg:h-[250px] flex justify-center relative"> <div className="h-0 md:h-[180px] lg:h-[250px] flex justify-center relative">
<div className="absolute -top-[200px]"> <motion.div
<Image className="absolute -top-[200px]"
src="/assets/images/temp-example.png" variants={fadeInUp}
alt="Example Image" initial="hidden"
width={1200} animate={isInView ? "visible" : "hidden"}
height={300} >
priority <div className="relative rounded-lg overflow-hidden">
/> <Image
</div> src="/assets/images/temp-example.png"
alt="Example Image"
width={1200}
height={300}
priority
className="transform transition duration-700 hover:scale-105"
/>
</div>
</motion.div>
</div> </div>
</section> </section>
); );