Skip to content

Commit e8952fa

Browse files
homepage
1 parent 50d6c4a commit e8952fa

File tree

15 files changed

+768
-20
lines changed

15 files changed

+768
-20
lines changed

.eslintrc.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,11 @@
55
"plugin:tailwindcss/recommended",
66
"prettier",
77
"next/typescript"
8-
]
8+
],
9+
"plugins": ["tailwindcss"],
10+
"rules": {
11+
"tailwindcss/classnames-order": "warn",
12+
"tailwindcss/enforces-shorthand": "warn",
13+
"tailwindcss/no-custom-classname": "warn"
14+
}
915
}

app/(root)/(home)/page.tsx

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,105 @@
11
"use client";
22

3+
import QuestionCard from "@/components/cards/QuestionCard";
4+
import HomeFilters from "@/components/home/HomeFilters";
5+
import { Filter } from "@/components/shared/Filter";
6+
import NoResult from "@/components/shared/NoResult";
7+
import { LocalSearchbar } from "@/components/shared/search/LocalSearchbar";
8+
import { Button } from "@/components/ui/button";
9+
import { HomePageFilters } from "@/constants/filters";
10+
import Link from "next/link";
11+
12+
const questions = [
13+
{
14+
_id: "1",
15+
title: "Cascading Deletes in SQLAlchemy?",
16+
tags: [
17+
{ _id: "1", name: "python" },
18+
{ _id: "2", name: "sql" },
19+
],
20+
author: {
21+
_id: "1",
22+
name: "John Doe",
23+
picture: "https://example.com/johndoe.jpg",
24+
},
25+
upvotes: 12311213421,
26+
views: 500552,
27+
answers: [{}],
28+
createdAt: new Date("2024-09-01T12:00:00.000Z"),
29+
},
30+
{
31+
_id: "2",
32+
title: "How to center a div?",
33+
tags: [
34+
{ _id: "1", name: "css" },
35+
{ _id: "2", name: "html" },
36+
],
37+
author: {
38+
_id: "2",
39+
name: "Jane Doe",
40+
picture: "https://example.com/janedoe.jpg",
41+
},
42+
upvotes: 20,
43+
views: 200,
44+
answers: [{}],
45+
createdAt: new Date("2021-09-01T12:00:00.000Z"),
46+
},
47+
];
48+
349
export default function Home() {
4-
return <header></header>;
50+
return (
51+
<>
52+
<div className="flex w-full flex-col-reverse justify-between gap-4 sm:flex-row sm:items-center">
53+
<h1 className="h1-bold text-dark100_light900">All Questions</h1>
54+
<Link href={`/ask-question`} className="flex justify-end max-sm:w-full">
55+
<Button className="primary-gradient min-h-[46px] px-4 py-3 !text-light-900">
56+
Ask a Questions
57+
</Button>
58+
</Link>
59+
</div>
60+
<div className="mt-11 flex justify-between gap-5 max-sm:flex-col sm:items-center">
61+
<LocalSearchbar
62+
route="/"
63+
iconPosition="left"
64+
imgSrc="/assets/icons/search.svg"
65+
placeholder="Search for questions"
66+
otherClasses="flex-1"
67+
/>
68+
<Filter
69+
filters={HomePageFilters}
70+
otherClasses="min-h-[56px] sm:min-w-[170px]"
71+
containerClasses="hidden max-md:flex"
72+
/>
73+
</div>
74+
75+
<HomeFilters />
76+
77+
<div className="mt-10 flex w-full flex-col gap-6">
78+
{questions.length > 0 ? (
79+
questions.map((question) => (
80+
<QuestionCard
81+
key={question._id}
82+
_id={question._id}
83+
title={question.title}
84+
tags={question.tags}
85+
author={question.author}
86+
upvotes={question.upvotes}
87+
views={question.views}
88+
answers={question.answers}
89+
createdAt={question.createdAt}
90+
/>
91+
))
92+
) : (
93+
<NoResult
94+
title="There's no question to show"
95+
description=" Be the first to break the silence! Ask a question and kickstart the
96+
discussion. Our query could be the next big thing others learn from. Get
97+
involved!"
98+
link="/ask-question"
99+
linkTitle="Ask a Question"
100+
/>
101+
)}
102+
</div>
103+
</>
104+
);
5105
}

components/cards/QuestionCard.tsx

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import Link from "next/link";
2+
import React from "react";
3+
import RenderTag from "../shared/RenderTag";
4+
import Metric from "../shared/Metric";
5+
import { formatandDivideNumber, getTimestamp } from "@/lib/utils";
6+
7+
interface QuestionProps {
8+
_id: string;
9+
title: string;
10+
tags: {
11+
_id: string;
12+
name: string;
13+
}[];
14+
author: {
15+
_id: string;
16+
name: string;
17+
picture: string;
18+
};
19+
upvotes: number;
20+
views: number;
21+
answers: Array<object>;
22+
createdAt: Date;
23+
}
24+
25+
const QuestionCard = ({
26+
_id,
27+
title,
28+
tags,
29+
author,
30+
upvotes,
31+
views,
32+
answers,
33+
createdAt,
34+
}: QuestionProps) => {
35+
return (
36+
<div className="card-wrapper rounded-[10px] p-9 sm:px-11">
37+
<div className="flex flex-col-reverse items-start justify-between gap-5 sm:flex-row">
38+
<div>
39+
<span className="subtle-regular text-dark400_light700 line-clamp-1 flex sm:hidden">
40+
{getTimestamp(createdAt)}
41+
</span>
42+
<Link href={`/question/${_id}`}>
43+
<h3 className="sm:h3-semibold base-semibold text-dark200_light900 line-clamp-1 flex-1">
44+
{title}
45+
</h3>
46+
</Link>
47+
</div>
48+
</div>
49+
<div className="mt-3.5 flex flex-wrap gap-2">
50+
{tags.map((tag) => (
51+
<RenderTag key={tag._id} _id={tag._id} name={tag.name} />
52+
))}
53+
</div>
54+
55+
<div className="flex-between mt-6 w-full flex-wrap gap-3">
56+
<Metric
57+
imgUrl="/assets/icons/avatar.svg"
58+
alt="user"
59+
value={author.name}
60+
title={` - asked ${getTimestamp(createdAt)}`}
61+
href={`/profile/${author._id}`}
62+
isAuthor
63+
textStyles="body-medium text-dark400_light800"
64+
/>
65+
<Metric
66+
imgUrl="/assets/icons/message.svg"
67+
alt="message"
68+
value={answers.length}
69+
title=" Answers"
70+
textStyles="small-medium text-dark400_light800"
71+
/>
72+
<Metric
73+
imgUrl="/assets/icons/eye.svg"
74+
alt="eye"
75+
value={formatandDivideNumber(views)}
76+
title=" Views"
77+
textStyles="small-medium text-dark400_light800"
78+
/>
79+
</div>
80+
</div>
81+
);
82+
};
83+
84+
export default QuestionCard;

components/home/HomeFilters.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"use client";
2+
3+
import { HomePageFilters } from "@/constants/filters";
4+
import React from "react";
5+
import { Button } from "../ui/button";
6+
7+
const HomeFilters = () => {
8+
const active = "newest";
9+
return (
10+
<div className="mt-10 hidden flex-wrap gap-3 md:flex">
11+
{HomePageFilters.map((item) => (
12+
<Button
13+
key={item.value}
14+
onClick={() => {}}
15+
className={`body-medium rounded-lg pg-6 py-3 capitalize shadow-none ${
16+
active === item.value
17+
? "bg-primary-100 text-primary-500"
18+
: "bg-light-800 text-light-500"
19+
}
20+
`}
21+
>
22+
{item.name}
23+
</Button>
24+
))}
25+
</div>
26+
);
27+
};
28+
29+
export default HomeFilters;

components/shared/Filter.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"use client";
2+
3+
import {
4+
Select,
5+
SelectContent,
6+
SelectGroup,
7+
SelectItem,
8+
SelectTrigger,
9+
SelectValue,
10+
} from "@/components/ui/select";
11+
12+
interface Props {
13+
filters: {
14+
name: string;
15+
value: string;
16+
}[];
17+
otherClasses?: string;
18+
containerClasses?: string;
19+
}
20+
21+
export const Filter = ({ filters, otherClasses, containerClasses }: Props) => {
22+
return (
23+
<div className={`relative ${containerClasses}`}>
24+
<Select>
25+
<SelectTrigger
26+
className={`${otherClasses} body-regular light-border background-light800_dark300 text-dark500_light700 border px-5 py-2.5`}
27+
>
28+
<div className="line-clamp-1">
29+
<SelectValue placeholder="Select a Filter" />
30+
</div>
31+
</SelectTrigger>
32+
<SelectContent>
33+
<SelectGroup>
34+
{filters.map((item) => (
35+
<SelectItem key={item.value} value={item.value}>
36+
{item.name}
37+
</SelectItem>
38+
))}
39+
</SelectGroup>
40+
</SelectContent>
41+
</Select>
42+
</div>
43+
);
44+
};

components/shared/Metric.tsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import Image from "next/image";
2+
import Link from "next/link";
3+
import React from "react";
4+
5+
interface MetricProps {
6+
imgUrl: string;
7+
alt: string;
8+
value: string | number;
9+
title: string;
10+
href?: string;
11+
textStyles?: string;
12+
isAuthor?: boolean;
13+
}
14+
15+
const Metric = ({
16+
imgUrl,
17+
alt,
18+
value,
19+
title,
20+
href,
21+
textStyles,
22+
isAuthor,
23+
}: MetricProps) => {
24+
const metricContent = (
25+
<>
26+
<Image
27+
src={imgUrl}
28+
width={16}
29+
height={16}
30+
alt={alt}
31+
className={`object-contain ${href ? "rounded-full" : ""}`}
32+
/>
33+
<p className={`${textStyles} flex items-center gap-1`}>
34+
{value}
35+
36+
<span
37+
className={`small-regular line-clamp-1 ${isAuthor ? "max-sm:hidden" : ""}`}
38+
>
39+
{title}
40+
</span>
41+
</p>
42+
</>
43+
);
44+
45+
if (href) {
46+
return (
47+
<Link href={href} className="flex-center gap-1">
48+
{metricContent}
49+
</Link>
50+
);
51+
}
52+
53+
return <div className="flex-center flex-wrap gap-1">{metricContent}</div>;
54+
};
55+
56+
export default Metric;

components/shared/NoResult.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import Image from "next/image";
2+
import Link from "next/link";
3+
import React from "react";
4+
import { Button } from "../ui/button";
5+
6+
interface Props {
7+
title: string;
8+
description: string;
9+
link: string;
10+
linkTitle: string;
11+
}
12+
13+
const NoResult = ({ title, description, link, linkTitle }: Props) => {
14+
return (
15+
<div className="mt-10 flex w-full flex-col items-center justify-center">
16+
<Image
17+
src={`/assets/images/light-illustration.png`}
18+
alt="No result illustration"
19+
width={270}
20+
height={200}
21+
className="block object-contein dark:hidden"
22+
/>
23+
<Image
24+
src={`/assets/images/dark-illustration.png`}
25+
alt="No result illustration"
26+
width={270}
27+
height={200}
28+
className="hidden object-contein dark:flex"
29+
/>
30+
31+
<h2 className="h2-bold text-dark200_light900 mt-8">{title}</h2>
32+
<p className="body-regular text-dark500_light700 my-3.5 max-w-md text-center">
33+
{description}
34+
</p>
35+
<Link href={link}>
36+
<Button className="paragraph-medium mt-5 min-h-[46px] rounded-lg bg-primary-500 px-4 py-3 text-light-900 hover:bg-primary-500 dark:bg-primary-500 dark:text-light-900">
37+
{linkTitle}
38+
</Button>
39+
</Link>
40+
</div>
41+
);
42+
};
43+
44+
export default NoResult;

components/shared/RenderTag.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React from "react";
33
import { Badge } from "@/components/ui/badge";
44

55
interface Props {
6-
_id: number;
6+
_id: string;
77
name: string;
88
totalQuestions?: number;
99
showCount?: boolean;

0 commit comments

Comments
 (0)