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
58 changes: 58 additions & 0 deletions frontend/src/components/contact/ContactChannelCards.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Mail, Bug, Lightbulb, ArrowUpRight } from "lucide-react";

const channels = [
{
icon: Mail,
title: "Email",
description: "Fire off an email and we'll get back to you β€” fast.",
href: "mailto:hello@codelens.dev",
},
{
icon: Bug,
title: "GitHub Issues",
description: "Found a bug? Report it and we'll squash it.",
href: "https://github.com/kunalverma2512/CodeLens/issues",
},
{
icon: Lightbulb,
title: "Feature Request",
description: "Got an idea? Drop it and help us shape the future.",
href: "https://github.com/kunalverma2512/CodeLens/discussions",
},
];

export default function ContactChannelCards() {
return (
<section className="w-full px-6 sm:px-10 lg:px-16 pb-16 lg:pb-24">
<div className="mx-auto max-w-7xl w-full">
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 lg:gap-5">
{channels.map((channel) => {
const Icon = channel.icon;
return (
<a
key={channel.title}
href={channel.href}
target="_blank"
rel="noopener noreferrer"
className="group relative flex flex-col border border-neutral-200 bg-white p-5 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:scale-[1.01] hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-900 focus-visible:ring-offset-2"
>
<div className="flex items-start justify-between">
<div className="flex h-10 w-10 items-center justify-center bg-neutral-100 text-neutral-600 transition-colors duration-200 group-hover:bg-neutral-200 group-hover:text-neutral-900">
<Icon className="h-5 w-5" />
</div>
<ArrowUpRight className="h-4 w-4 text-neutral-300 transition-all duration-200 group-hover:text-neutral-600 group-hover:translate-x-0.5 group-hover:-translate-y-0.5" />
</div>
<h3 className="mt-4 font-semibold text-neutral-900">
{channel.title}
</h3>
<p className="mt-1 text-sm text-neutral-500 leading-relaxed">
{channel.description}
</p>
</a>
);
})}
</div>
</div>
</section>
);
}
101 changes: 101 additions & 0 deletions frontend/src/components/contact/ContactFAQ.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { useState, useCallback } from "react";
import { ChevronDown } from "lucide-react";

const FAQS = [
{
q: "Is CodeLens free to use?",
a: "Yes β€” 100% free and open source. No hidden fees, no restrictions. Developer tools should be accessible to everyone, period.",
},
{
q: "How do I report a security vulnerability?",
a: "Found a flaw? Email security@codelens.dev or open a GitHub Security Advisory. Keep vulnerabilities private until we fix them.",
},
{
q: "Can I contribute?",
a: "Absolutely. CodeLens is open source and we welcome contributors with open arms. Head to our GitHub repo for guidelines, open issues, and the roadmap.",
},
{
q: "How do I request new integrations?",
a: "Open a feature request on GitHub. We listen to the community and prioritize what matters most to you.",
},
{
q: "Where is my data stored?",
a: "On secure servers with enterprise-grade encryption. Your data stays yours. Check our privacy policy for the full breakdown.",
},
];

export default function ContactFAQ() {
const [openIndex, setOpenIndex] = useState(null);

const toggle = useCallback(
(index) => {
setOpenIndex((prev) => (prev === index ? null : index));
},
[],
);

return (
<section className="w-full px-6 sm:px-10 lg:px-16 py-20 lg:py-28">
<div className="mx-auto max-w-7xl w-full">
<div className="max-w-2xl mx-auto text-center mb-12">
<p className="font-mono text-xs font-medium uppercase tracking-widest text-neutral-400 mb-4">
FAQ
</p>
<h2 className="text-4xl sm:text-5xl lg:text-6xl font-bold tracking-tight text-neutral-900">
Frequently asked questions
</h2>
</div>

<div className="max-w-3xl mx-auto space-y-3">
{FAQS.map((faq, index) => {
const isOpen = openIndex === index;
const panelId = `faq-panel-${index}`;
const buttonId = `faq-button-${index}`;

return (
<div
key={index}
className="border border-neutral-200 bg-white overflow-hidden transition-shadow duration-200 hover:shadow-sm"
>
<button
type="button"
id={buttonId}
onClick={() => toggle(index)}
className="flex w-full items-center justify-between gap-4 px-5 py-4 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-900 focus-visible:ring-inset"
aria-expanded={isOpen}
aria-controls={panelId}
>
<span className="text-sm font-medium text-neutral-900 pr-4">
{faq.q}
</span>
<ChevronDown
className={`h-4 w-4 shrink-0 text-neutral-400 transition-transform duration-200 ${
isOpen ? "rotate-180" : ""
}`}
aria-hidden="true"
/>
</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]" : "grid-rows-[0fr]"
}`}
>
<div className="overflow-hidden">
<p className="px-5 pb-4 text-sm text-neutral-500 leading-relaxed">
{faq.a}
</p>
</div>
</div>
</div>
);
})}
</div>
</div>
</section>
);
}
Loading