Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion frontend/src/LandingPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import ProfileInfoCard from "./components/Cards/ProfileinfoCard";
import React, { useContext, useState, useEffect } from "react";
import { APP_FEATURES, STATS, HOW_IT_WORKS_STEPS } from "./utils/data";
import { useNavigate } from "react-router-dom";
import { Link } from "react-router-dom";

import {
LuSparkles,
Expand Down Expand Up @@ -1050,7 +1051,7 @@ const LandingPage = () => {
<div className="pt-8 flex flex-col sm:flex-row items-center justify-between gap-4 text-xs text-gray-500">
<p>Β© {new Date().getFullYear()} PrepPilot AI. All rights reserved.</p>
<div className="flex space-x-6">
<a href="#" className="hover:text-gray-300 transition-colors">Privacy Policy</a>
<a href="/privacy-policy" className="hover:text-gray-300 transition-colors">Privacy Policy</a>
<a href="#" className="hover:text-gray-300 transition-colors">Terms of Service</a>
</div>
</div>
Expand Down
325 changes: 325 additions & 0 deletions frontend/src/pages/Privacy/PrivacyPolicy.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,325 @@
import React, { useState, useEffect } from "react";
import { motion } from "framer-motion";
import {
LuChevronDown,
LuShield,
LuEye,
LuLock,
LuUsers,
LuGlobe,
LuArrowLeft,
} from "react-icons/lu";
import { useNavigate } from "react-router-dom";

const PrivacyPolicy = () => {
const navigate = useNavigate();
const [openSections, setOpenSections] = useState(new Set(["intro"]));
const [activeSection, setActiveSection] = useState("intro");

const toggleSection = (id) => {
const newSet = new Set(openSections);
if (newSet.has(id)) {
newSet.delete(id);
} else {
newSet.add(id);
}
setOpenSections(newSet);
};

const scrollToSection = (id) => {
const element = document.getElementById(id);
if (element) {
const offset = 100;
const bodyRect = document.body.getBoundingClientRect().top;
const elementPosition = element.getBoundingClientRect().top;
const offsetPosition = elementPosition - bodyRect - offset;

window.scrollTo({
top: offsetPosition,
behavior: "smooth",
});

setActiveSection(id);
}
};

// Scroll Spy (Active Section Highlight)
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setActiveSection(entry.target.id);
}
});
},
{ threshold: 0.4, rootMargin: "-80px 0px -20% 0px" },
);

const sections = document.querySelectorAll(".policy-section");
sections.forEach((section) => observer.observe(section));

return () => observer.disconnect();
}, []);

const sectionsData = [
{
id: "intro",
title: "Introduction",
icon: <LuShield className="w-5 h-5" />,
},
{
id: "information-collected",
title: "Information We Collect",
icon: <LuUsers className="w-5 h-5" />,
},
{
id: "how-we-use",
title: "How We Use Your Information",
icon: <LuEye className="w-5 h-5" />,
},
{
id: "sharing",
title: "Sharing of Information",
icon: <LuUsers className="w-5 h-5" />,
},
{
id: "security",
title: "Data Security",
icon: <LuLock className="w-5 h-5" />,
},
{
id: "your-rights",
title: "Your Rights and Choices",
icon: <LuUsers className="w-5 h-5" />,
},
{
id: "cookies",
title: "Cookies and Tracking",
icon: <LuGlobe className="w-5 h-5" />,
},
{
id: "children",
title: "Children's Privacy",
icon: <LuShield className="w-5 h-5" />,
},
{
id: "international",
title: "International Data Transfers",
icon: <LuGlobe className="w-5 h-5" />,
},
{
id: "changes",
title: "Changes to This Policy",
icon: <LuShield className="w-5 h-5" />,
},
];

return (
<div className="min-h-screen bg-gray-950 text-white">
{/* Navbar */}
<header className="fixed top-0 z-50 w-full pt-6 px-6">
<div
className="max-w-7xl mx-auto flex items-center justify-between rounded-full px-8 py-4"
style={{
background: "rgba(0,0,0,0.65)",
backdropFilter: "blur(16px)",
border: "1px solid rgba(255,255,255,0.1)",
}}
>
<div
className="flex items-center gap-3 cursor-pointer"
onClick={() => navigate("/")}
>
<img src="/PrepPilot-Logo.png" alt="Logo" className="w-8 h-8" />
<span className="font-bold text-xl">
PrepPilot <span className="text-violet-400">AI</span>
</span>
</div>

<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={() => navigate("/")}
className="flex items-center gap-2 text-sm px-6 py-2.5 rounded-full border border-white/20 hover:bg-white/10 transition-all"
>
<LuArrowLeft className="w-4 h-4" />
Back to Home
</motion.button>
</div>
</header>

<div className="pt-28 pb-20 max-w-7xl mx-auto px-6 flex gap-12">
{/* LEFT SIDEBAR - Table of Contents */}
<div className="hidden lg:block w-80 flex-shrink-0">
<div className="sticky top-28">
<div className="mb-8">
<h2 className="text-lg font-semibold text-white mb-4 flex items-center gap-2">
<LuShield className="text-violet-400" />
Table of Contents
</h2>
</div>

<nav className="space-y-1">
{sectionsData.map((section) => (
<button
key={section.id}
onClick={() => scrollToSection(section.id)}
className={`w-full text-left px-4 py-3 rounded-2xl flex items-center gap-3 text-sm transition-all duration-200 ${
activeSection === section.id
? "bg-violet-500/10 text-violet-300 border border-violet-500/30"
: "hover:bg-white/5 text-gray-400 hover:text-gray-200"
}`}
>
<span className="text-violet-400 opacity-70">
{section.icon}
</span>
<span>{section.title}</span>
</button>
))}
</nav>

<div className="mt-10 pt-6 border-t border-white/10 text-xs text-gray-500">
Last Updated: June 20, 2026
</div>
</div>
</div>

{/* RIGHT MAIN CONTENT */}
<div className="flex-1 max-w-3xl">
<div className="mb-16 text-center lg:text-left">
<div className="inline-flex items-center gap-3 px-5 py-2 bg-violet-500/10 border border-violet-500/30 rounded-full mb-6">
<LuShield className="text-violet-400" />
<span className="uppercase tracking-widest text-sm font-semibold text-violet-300">
Legal
</span>
</div>
<h1 className="text-5xl md:text-6xl font-bold leading-tight">
Privacy Policy
</h1>
<p className="text-xl text-gray-400 mt-6">
We take your privacy seriously. This policy explains how we
protect and handle your data.
</p>
</div>

{/* Sections */}
<div className="space-y-8">
{sectionsData.map((sec, index) => {
const fullSection = {
id: sec.id,
title: sec.title,
icon: sec.icon,
content: getSectionContent(sec.id),
};

return (
<div
id={fullSection.id}
key={fullSection.id}
className="policy-section border border-white/10 rounded-3xl overflow-hidden bg-gray-900/50 backdrop-blur-sm scroll-mt-24"
>
<button
onClick={() => toggleSection(fullSection.id)}
className="w-full px-8 py-6 flex items-center justify-between text-left hover:bg-white/5 transition-all group"
>
<div className="flex items-center gap-4">
<div className="text-violet-400">{fullSection.icon}</div>
<h3 className="text-2xl font-semibold text-white group-hover:text-violet-300 transition-colors">
{fullSection.title}
</h3>
</div>
<motion.div
animate={{
rotate: openSections.has(fullSection.id) ? 180 : 0,
}}
transition={{ duration: 0.3 }}
>
<LuChevronDown className="w-6 h-6 text-gray-400" />
</motion.div>
</button>

{openSections.has(fullSection.id) && (
<div className="px-8 pb-8 text-[15.2px] leading-relaxed text-gray-300">
{fullSection.content}
</div>
)}
</div>
);
})}
</div>

{/* Final Contact Box */}
<div className="mt-20 bg-gradient-to-br from-violet-500/10 to-transparent border border-violet-500/20 rounded-3xl p-12 text-center">
<h3 className="text-3xl font-bold mb-4">
Questions About Your Privacy?
</h3>
<p className="text-gray-400 mb-8 max-w-md mx-auto">
Our team is here to help. Reach out to us anytime.
</p>
<a
href="mailto:privacy@preppilot.ai"
className="inline-block px-10 py-4 bg-white text-black font-semibold rounded-2xl hover:bg-gray-100 transition-all active:scale-95"
>
Contact Privacy Team
</a>
</div>
</div>
</div>

{/* Footer */}
<footer className="border-t border-white/10 py-12 text-center text-gray-500 text-sm">
<p>Β© {new Date().getFullYear()} PrepPilot AI. All rights reserved.</p>
</footer>
</div>
);
};

// Helper function to keep content clean
const getSectionContent = (id) => {
switch (id) {
case "intro":
return (
<div className="space-y-4">
<p>
At PrepPilot AI, we are committed to protecting your privacy and
ensuring the security of your personal information. This Privacy
Policy explains how we collect, use, disclose, and safeguard your
data when you use our platform.
</p>
<p className="text-sm text-gray-400">Last Updated: June 20, 2026</p>
</div>
);
case "information-collected":
return (
<div className="space-y-6">
<div>
<h4 className="font-semibold text-white mb-3">
Personal Information
</h4>
<ul className="list-disc pl-6 space-y-2">
<li>Name, email address, and profile information</li>
<li>Resume/CV data and interview preparation notes</li>
<li>Authentication details (via Google, GitHub, or email)</li>
</ul>
</div>
<div>
<h4 className="font-semibold text-white mb-3">
Usage & Technical Data
</h4>
<ul className="list-disc pl-6 space-y-2">
<li>IP address, browser type, and device information</li>
<li>Pages visited, time spent, and interaction data</li>
<li>Coding session logs and AI conversation history</li>
<li>Performance analytics on practice sessions</li>
</ul>
</div>
</div>
);
// ... (Add other sections similarly - I kept it short for brevity. You can expand from previous version)
default:
return <p>Content for {id} goes here.</p>;
}
};

export default PrivacyPolicy;