Skip to content

Commit d7c6596

Browse files
committed
feat(faqs): create faqs components and integrate them into the faqs page
1 parent 87a360d commit d7c6596

File tree

4 files changed

+204
-7
lines changed

4 files changed

+204
-7
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"use client";
2+
3+
import type { Faq } from "@/types";
4+
5+
import { Accordion, AccordionItem } from "@heroui/react";
6+
import { CircleHelp, ChevronLeft } from "lucide-react";
7+
8+
type Props = {
9+
faq: Faq;
10+
};
11+
12+
export const FaqItem = ({ faq }: Props) => {
13+
return (
14+
<Accordion variant="bordered" className="group">
15+
<AccordionItem
16+
key={faq.id}
17+
title={faq.question}
18+
HeadingComponent="h2"
19+
aria-label={faq.question}
20+
startContent={
21+
<CircleHelp className="stroke-neutral-950 dark:stroke-white" />
22+
}
23+
indicator={({ isOpen }) =>
24+
isOpen ? (
25+
<ChevronLeft className="stroke-neutral-950 dark:stroke-white" />
26+
) : (
27+
<ChevronLeft className="stroke-neutral-200 dark:stroke-neutral-800 group-hover:stroke-neutral-950 dark:group-hover:stroke-white" />
28+
)
29+
}
30+
className="text-xl"
31+
>
32+
<p className="text-sm text-neutral-500 pb-4">{faq.answer}</p>
33+
</AccordionItem>
34+
</Accordion>
35+
);
36+
};
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"use client";
2+
3+
import type { Faq } from "@/types";
4+
5+
import { motion } from "framer-motion";
6+
import { useAnimateOnView } from "@/hooks/use-animate-on-view";
7+
import { FaqItem } from "./faq-item";
8+
9+
type Props = {
10+
faqs: Faq[];
11+
};
12+
13+
export default function ({ faqs }: Props) {
14+
const firstColumnAnimation = useAnimateOnView(0.1, false);
15+
const secondColumnAnimation = useAnimateOnView(0.1, false);
16+
17+
const middleIndex = Math.ceil(faqs.length / 2);
18+
const firstColumnFaqs = faqs.slice(0, middleIndex);
19+
const secondColumnFaqs = faqs.slice(middleIndex);
20+
21+
return (
22+
<>
23+
<section className="hidden lg:grid sm:grid-cols-2 gap-4">
24+
<div className="grid grid-col-1 gap-4" ref={firstColumnAnimation.ref}>
25+
{firstColumnFaqs.map((faq, idx) => (
26+
<motion.div
27+
key={faq.id}
28+
variants={firstColumnAnimation.itemVariants}
29+
initial="hidden"
30+
animate={firstColumnAnimation.controls}
31+
transition={{
32+
duration: 0.3,
33+
delay: idx * 0.1,
34+
ease: "easeOut",
35+
}}
36+
>
37+
<FaqItem faq={faq} />
38+
</motion.div>
39+
))}
40+
</div>
41+
<div className="grid grid-col-1 gap-4" ref={firstColumnAnimation.ref}>
42+
{secondColumnFaqs.map((faq, idx) => (
43+
<motion.div
44+
key={faq.id}
45+
variants={firstColumnAnimation.itemVariants}
46+
initial="hidden"
47+
animate={firstColumnAnimation.controls}
48+
transition={{
49+
duration: 0.3,
50+
delay: idx * 0.1,
51+
ease: "easeOut",
52+
}}
53+
>
54+
<FaqItem faq={faq} />
55+
</motion.div>
56+
))}
57+
</div>
58+
</section>
59+
<section className="lg:hidden px-2 xl:px-0">
60+
<div className="grid grid-col-1 gap-4" ref={secondColumnAnimation.ref}>
61+
{faqs.map((faq, idx) => (
62+
<motion.div
63+
key={faq.id}
64+
variants={secondColumnAnimation.itemVariants}
65+
initial="hidden"
66+
animate={secondColumnAnimation.controls}
67+
transition={{
68+
duration: 0.3,
69+
delay: idx * 0.1,
70+
ease: "easeOut",
71+
}}
72+
>
73+
<FaqItem faq={faq} />
74+
</motion.div>
75+
))}
76+
</div>
77+
</section>
78+
</>
79+
);
80+
}

src/app/(public)/faqs/page.tsx

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,91 @@ import type { Metadata } from "next";
22

33
import { Heading } from "@/components/ui";
44
import { PROJECT_NAME } from "@/config/constants";
5+
import FaqList from "./_components/faq-list";
56

67
export const metadata: Metadata = {
78
title: `FAQs - ${PROJECT_NAME}`,
89
description: "Frequently asked questions",
910
};
1011

1112
export default function FaqsPage() {
13+
const faqs = [
14+
{
15+
id: "faq-1",
16+
question: "What is DevResources?",
17+
answer:
18+
"DevResources is a curated platform where developers can discover, share, and bookmark high-quality resources such as tools, articles, libraries, and tutorials.",
19+
created_at: "2023-05-01T00:00:00.000Z",
20+
},
21+
{
22+
id: "faq-2",
23+
question: "Do I need an account to use DevResources?",
24+
answer:
25+
"No account is required to browse resources. However, you’ll need to sign in to like, comment, or save resources to your personal collection.",
26+
created_at: "2023-05-01T00:00:00.000Z",
27+
},
28+
{
29+
id: "faq-3",
30+
question: "How are resources categorized?",
31+
answer:
32+
"Resources are organized by categories such as Frontend, Backend, DevOps, Machine Learning, Tools, and more. You can also filter by tags or search by keywords.",
33+
created_at: "2023-05-01T00:00:00.000Z",
34+
},
35+
{
36+
id: "faq-4",
37+
question: "Can I submit my own resources?",
38+
answer:
39+
"Yes, registered users can submit their favorite tools, articles, or libraries. All submissions are reviewed before being published to ensure quality.",
40+
created_at: "2023-05-01T00:00:00.000Z",
41+
},
42+
{
43+
id: "faq-5",
44+
question: "How does upvoting work?",
45+
answer:
46+
"Users can upvote resources they find useful. Resources with higher upvotes gain visibility on the homepage and in category listings.",
47+
created_at: "2023-05-01T00:00:00.000Z",
48+
},
49+
{
50+
id: "faq-6",
51+
question: "Is there an API available?",
52+
answer:
53+
"An API for public data is under development. In the meantime, you can contact us if you have specific data needs.",
54+
created_at: "2023-05-01T00:00:00.000Z",
55+
},
56+
{
57+
id: "faq-7",
58+
question: "Can I report inappropriate or broken resources?",
59+
answer:
60+
"Yes, every resource has a report button. Reports are reviewed by moderators, and content violating guidelines is removed promptly.",
61+
created_at: "2023-05-01T00:00:00.000Z",
62+
},
63+
{
64+
id: "faq-8",
65+
question: "Is DevResources free to use?",
66+
answer:
67+
"Yes, the platform is free. In the future, premium features like personal dashboards and advanced metrics may be introduced.",
68+
created_at: "2023-05-01T00:00:00.000Z",
69+
},
70+
{
71+
id: "faq-9",
72+
question: "How often is the content updated?",
73+
answer:
74+
"New resources are added daily. Community contributions and internal curation help keep the content fresh and relevant.",
75+
created_at: "2023-05-01T00:00:00.000Z",
76+
},
77+
{
78+
id: "faq-10",
79+
question: "Can I follow other users or creators?",
80+
answer:
81+
"Following functionality is planned. Right now, you can view a user’s public submissions and likes on their profile page.",
82+
created_at: "2023-05-01T00:00:00.000Z",
83+
},
84+
];
85+
1286
return (
13-
<main className="max-w-screen-xl mx-auto p-4">
87+
<main className="max-w-screen-xl mx-auto p-4 flex flex-col gap-4">
1488
<Heading title="FAQs" subtitle="Frequently asked questions" />
15-
<p>
16-
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quae tenetur
17-
itaque tempora. Nostrum ut architecto libero! Quasi, iure placeat
18-
doloribus tenetur ullam veniam esse vero nemo! Molestias inventore velit
19-
praesentium!
20-
</p>
89+
<FaqList faqs={faqs} />
2190
</main>
2291
);
2392
}

src/types/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ export type Resource = {
3434
created_at: string;
3535
};
3636

37+
export type ResourceList = Pick<
38+
Resource,
39+
"id" | "name" | "slug" | "image" | "tags" | "category"
40+
>;
41+
42+
export type Faq = {
43+
id: string;
44+
question: string;
45+
answer: string;
46+
created_at: string;
47+
};
48+
3749
export type CreateResourceInput = Omit<Resource, "id" | "created_at">;
3850

3951
export type UpdateResourceInput = Partial<

0 commit comments

Comments
 (0)