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
4 changes: 3 additions & 1 deletion frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ import ContestLeetCodePage from "./pages/ContestLeetCodePage";
import ContestAtCoderPage from "./pages/ContestAtCoderPage";
import TermsPage from "./pages/TermsPage";
import PrivacyPage from "./pages/PrivacyPage";
import FAQPage from "./pages/FAQPage";
import NotFoundPage from "./pages/NotFoundPage";
import ForgotPassword from "./components/auth/ForgotPassword";
import AccountCenterPage from "./pages/AccountCenterPage";
import GitHubIntelligencePage from "./pages/GitHubIntelligencePage";
import GitHubCallbackPage from "./pages/GitHubCallbackPage";
import ProtectedRoute from "./components/shared/ProtectedRoute";
import PublicRoute from "./components/shared/PublicRoute";
import FAQSection from "./components/explore/FAQSection";

export default function App() {
return (
<AuthProvider>
Expand Down Expand Up @@ -101,6 +102,7 @@ export default function App() {
/>
<Route path="/terms" element={<TermsPage/>} />
<Route path="/privacy" element={<PrivacyPage/>} />
<Route path="/faq" element={<FAQPage />} />
<Route path="*" element={<NotFoundPage />} />
</Routes>
</MainLayout>
Expand Down
50 changes: 23 additions & 27 deletions frontend/src/components/explore/FAQSection.jsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,6 @@
import { useState } from "react";
import { Link } from "react-router-dom";

const faqs = [
{
q: "What is CodeLens?",
a: "CodeLens is a platform that helps developers track their coding progress across different platforms like Codeforces, LeetCode, and GitHub in one place.",
},
{
q: "What problem does CodeLens solve?",
a: "It brings all your coding activity into one place so you can understand your progress, stay consistent, and know what to improve next.",
},
{
q: "Is my repository data private?",
a: "CodeLens only uses publicly available data from connected platforms and does not store any sensitive information.",
},
{
q: "How can I contribute to CodeLens?",
a: "You can contribute by solving issues, adding features, improving UI, or fixing bugs. Check the repository and start with beginner-friendly issues.",
},
];
import { faqs } from "../../data/faqs";

export default function FAQSection() {
const [openIdx, setOpenIdx] = useState(null);
Expand All @@ -35,22 +17,29 @@ export default function FAQSection() {
<div className="space-y-6 sm:space-y-8 w-full">
{faqs.map((item, index) => {
const isOpen = openIdx === index;
const buttonId = `landing-faq-button-${item.id}`;
const panelId = `landing-faq-panel-${item.id}`;

return (
<div
key={index}
key={item.id}
className="border-4 border-black shadow-[8px_8px_0_0_rgba(0,0,0,1)] md:shadow-[12px_12px_0_0_rgba(0,0,0,1)] bg-white w-full transition-transform md:hover:-translate-y-1"
>
<button
type="button"
onClick={() => toggle(index)}
className="w-full flex items-center justify-between gap-4 p-6 sm:p-8 text-left"
aria-expanded={isOpen}
aria-controls={panelId}
id={buttonId}
>
<h3 className="text-xl sm:text-2xl font-black uppercase tracking-tighter text-black leading-tight">
Q: {item.q}
</h3>

<span
className="text-2xl font-black shrink-0 transition-transform duration-200"
aria-hidden="true"
style={{
transform: isOpen ? "rotate(45deg)" : "rotate(0deg)",
}}
Expand All @@ -60,12 +49,19 @@ export default function FAQSection() {
</button>

<div
className="overflow-hidden transition-all duration-300 ease-in-out"
style={{ maxHeight: isOpen ? "500px" : "0px" }}
id={panelId}
role="region"
aria-labelledby={buttonId}
aria-hidden={!isOpen}
className={`grid transition-all duration-300 ease-in-out ${
isOpen ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
}`}
>
<p className="font-bold uppercase tracking-widest text-xs sm:text-sm text-black leading-relaxed px-6 sm:px-8 pb-6 sm:pb-8">
A: {item.a}
</p>
<div className="overflow-hidden">
<p className="font-bold uppercase tracking-widest text-xs sm:text-sm text-black leading-relaxed px-6 sm:px-8 pb-6 sm:pb-8">
Comment thread
coderabbitai[bot] marked this conversation as resolved.
A: {item.a}
</p>
</div>
</div>
</div>
);
Expand All @@ -76,12 +72,12 @@ export default function FAQSection() {
<div className="mt-12 flex justify-center">
<Link
to="/faq"
className="border-3 border-black px-5 py-2 text-sm sm:text-base font-black uppercase bg-white tracking-widest shadow-[5px_5px_0_0_rgba(0,0,0,1)] text-black transition-all duration-200 hover:bg-black hover:text-white hover:translate-x-1 hover:translate-y-1"
className="border-2 border-black px-5 py-2 text-sm sm:text-base font-black uppercase bg-white tracking-widest shadow-[5px_5px_0_0_rgba(0,0,0,1)] text-black transition-all duration-200 hover:bg-black hover:text-white hover:translate-x-1 hover:translate-y-1"
>
View All FAQs
</Link>
</div>
</div>
</div>
);
}
}
1 change: 1 addition & 0 deletions frontend/src/components/shared/Footer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export default function Footer() {
{[
{ to: "/dashboard", label: "Dashboard" },
{ to: "/explore", label: "Explore" },
{ to: "/faq", label: "FAQ" },
{ to: "/codeforces", label: "Codeforces" },
].map((l) => (
<Link
Expand Down
26 changes: 26 additions & 0 deletions frontend/src/data/faqs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export const faqs = [
{
id: "what-is-codelens",
category: "Platform tracking",
q: "What is CodeLens?",
a: "CodeLens is a platform that helps developers track their coding progress across different platforms like Codeforces, LeetCode, and GitHub in one place.",
},
{
id: "problem-solved",
category: "Platform tracking",
q: "What problem does CodeLens solve?",
a: "It brings all your coding activity into one place so you can understand your progress, stay consistent, and know what to improve next.",
},
{
id: "data-privacy",
category: "Data privacy",
q: "Is my repository data private?",
a: "CodeLens only uses publicly available data from connected platforms and does not store any sensitive information.",
},
{
id: "contribute",
category: "Open source",
q: "How can I contribute to CodeLens?",
a: "You can contribute by solving issues, adding features, improving UI, or fixing bugs. Check the repository and start with beginner-friendly issues.",
},
];
166 changes: 166 additions & 0 deletions frontend/src/pages/FAQPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import { faqs } from "../data/faqs";

const supportTopics = [...new Set(faqs.map((item) => item.category))];

export default function FAQPage() {
const [openIdx, setOpenIdx] = useState(null);
const itemRefs = useRef([]);
const navigate = useNavigate();

const handleGoBack = () => {
if (window.history.length > 1) {
navigate(-1);
return;
}

navigate("/explore");
};

const toggleFaq = (index, shouldScroll = false) => {
setOpenIdx((currentIndex) => (currentIndex === index ? null : index));

if (shouldScroll) {
window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
itemRefs.current[index]?.scrollIntoView({
behavior: "smooth",
block: "start",
});
});
});
}
};

return (
<main className="w-full overflow-x-hidden bg-white text-black">
<title>FAQ - CodeLens</title>
<section className="border-b-4 border-black px-6 py-20 sm:px-10 lg:px-16 lg:py-28">
<div className="mx-auto grid max-w-7xl gap-12 lg:grid-cols-[0.9fr_1.1fr] lg:items-end">
<div>
<p className="mb-5 text-xs font-black uppercase tracking-[0.28em] text-black">
Help Center / FAQ
</p>
<h1 className="text-5xl font-black uppercase leading-none tracking-tight text-black sm:text-7xl lg:text-8xl">
Answers Without The Noise.
</h1>
</div>

<div className="border-4 border-black bg-white p-6 shadow-[6px_6px_0_0_rgba(0,0,0,1)] sm:p-8 sm:shadow-[10px_10px_0_0_rgba(0,0,0,1)]">
<p className="text-base font-bold leading-relaxed text-black sm:text-lg">
Quick answers for developers connecting coding platforms, reading analytics,
protecting profile data, and contributing to CodeLens.
</p>
<div className="mt-8 grid grid-cols-1 gap-3 sm:grid-cols-2">
{supportTopics.map((topic) => (
<span
key={topic}
className="border-2 border-black px-3 py-3 text-center text-[11px] font-black uppercase tracking-widest"
>
{topic}
</span>
))}
</div>
</div>
</div>
</section>

<section className="px-6 py-16 sm:px-10 lg:px-16 lg:py-24">
<div className="mx-auto grid max-w-7xl gap-10 lg:grid-cols-[320px_1fr]">
<aside className="h-fit border-4 border-black bg-black p-6 text-white lg:sticky lg:top-24">
<h2 className="text-3xl font-black uppercase leading-none tracking-tight">
FAQ Index
</h2>
<div className="mt-6 flex flex-col gap-3">
{faqs.map((item, index) => (
<button
key={item.id}
type="button"
onClick={() => toggleFaq(index, true)}
className={`break-words border-2 px-4 py-3 text-left text-xs font-black uppercase tracking-widest transition-colors ${
openIdx === index
? "border-white bg-white text-black"
: "border-white text-white hover:bg-white hover:text-black"
}`}
>
{String(index + 1).padStart(2, "0")} / {item.q}
</button>
))}
</div>
</aside>

<div className="flex flex-col gap-6">
{faqs.map((item, index) => {
const isOpen = openIdx === index;
const buttonId = `faq-button-${item.id}`;
const panelId = `faq-panel-${item.id}`;

return (
<article
key={item.id}
ref={(element) => {
itemRefs.current[index] = element;
}}
className="border-4 border-black bg-white shadow-[6px_6px_0_0_rgba(0,0,0,1)] sm:shadow-[8px_8px_0_0_rgba(0,0,0,1)]"
>
<button
type="button"
onClick={() => toggleFaq(index)}
className="flex w-full items-center justify-between gap-6 p-6 text-left sm:p-8"
aria-expanded={isOpen}
aria-controls={panelId}
id={buttonId}
>
<span className="text-2xl font-black uppercase leading-tight tracking-tight sm:text-4xl">
{item.q}
</span>
<span className="text-4xl font-black leading-none" aria-hidden="true">
{isOpen ? "-" : "+"}
</span>
</button>

<div
id={panelId}
role="region"
aria-labelledby={buttonId}
aria-hidden={!isOpen}
className={`grid transition-all duration-300 ease-in-out ${
isOpen ? "grid-rows-[1fr] border-t-4 border-black" : "grid-rows-[0fr]"
}`}
>
<div className="overflow-hidden">
<p className="max-w-3xl px-6 py-6 text-base font-bold leading-relaxed text-black sm:px-8 sm:text-lg">
{item.a}
</p>
</div>
</div>
</article>
);
})}
</div>
</div>
</section>

<section className="border-t-4 border-black bg-black px-6 py-14 text-white sm:px-10 lg:px-16">
<div className="mx-auto flex max-w-7xl flex-col gap-8 lg:flex-row lg:items-center lg:justify-between">
<div>
<p className="mb-3 text-xs font-black uppercase tracking-[0.28em]">
All Caught Up?
</p>
<h2 className="text-4xl font-black uppercase leading-none tracking-tight sm:text-6xl">
Head Back To The Platform.
</h2>
</div>
<button
type="button"
onClick={handleGoBack}
className="inline-flex border-4 border-white px-8 py-5 text-sm font-black uppercase tracking-widest text-white transition-colors hover:bg-white hover:text-black"
>
Go Back
</button>
</div>
</section>
</main>
);
}