Skip to content

Commit

Permalink
Merge pull request #41 from vishalkondle45/feature/forum
Browse files Browse the repository at this point in the history
feature: tags in forum
  • Loading branch information
vishalkondle-dev authored Jul 10, 2024
2 parents a9adb3f + 9271656 commit 2021f88
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 15 deletions.
11 changes: 6 additions & 5 deletions app/(private)/forum/[forum]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ForumType } from '@/components/Forum/Forum.types';
import { FORUM_ANSWERS_SORT_OPTIONS } from '@/lib/constants';

const ForumPage = ({ params }: { params: { forum: string } }) => {
const { data, refetch } = useFetchData(`/api/forums?_id=${params.forum}`);
const { data, refetch, loading } = useFetchData(`/api/forums?_id=${params.forum}`);
const [opened, { open, close }] = useDisclosure(false);
const [value, setValue] = useState<string | null>('highest');
const [answers, setAnswers] = useState(data?.[1]);
Expand All @@ -37,7 +37,7 @@ const ForumPage = ({ params }: { params: { forum: string } }) => {
setAnswers(sortIt);
}, [value, data]);

if (!data?.length) {
if (loading) {
return <></>;
}

Expand Down Expand Up @@ -80,9 +80,10 @@ const ForumPage = ({ params }: { params: { forum: string } }) => {
placeholder="Sort by"
/>
</Group>
{answers?.map((forum: ForumType) => (
<Forum key={String(forum._id)} forum={forum} refetch={refetch} />
))}
{answers &&
answers?.map((forum: ForumType) => (
<Forum key={String(forum._id)} forum={forum} refetch={refetch} />
))}
<ReplyToQuestion refetch={refetch} parent={data[0]?._id} />
</Stack>
<AskQuestion opened={opened} close={close} />
Expand Down
13 changes: 9 additions & 4 deletions app/(private)/forum/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { useDisclosure, useMediaQuery } from '@mantine/hooks';
import { ForumItem, AskQuestion } from '@/components/Forum';
import useFetchData from '@/hooks/useFetchData';
import { ForumType } from '@/components/Forum/Forum.types';
import Skelton from '@/components/Skelton/Skelton';

const ForumPage = () => {
const { data: forums } = useFetchData('/api/forums');
const { data: forums, loading } = useFetchData('/api/forums');
const [opened, { open, close }] = useDisclosure(false);
const isMobile = useMediaQuery('(max-width: 768px)');

Expand All @@ -17,9 +18,13 @@ const ForumPage = () => {
<Button onClick={open}>Ask Question</Button>
</Group>
<Stack>
{forums?.map((forum: ForumType) => (
<ForumItem key={String(forum._id)} forum={forum} isMobile={isMobile} />
))}
{loading ? (
<Skelton items={6} height={90} />
) : (
forums?.map((forum: ForumType) => (
<ForumItem key={String(forum._id)} forum={forum} isMobile={isMobile} />
))
)}
</Stack>
<AskQuestion opened={opened} close={close} />
</>
Expand Down
76 changes: 76 additions & 0 deletions app/(private)/forum/tags/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
'use client';

import React, { useState } from 'react';
import {
Group,
rem,
SegmentedControl,
SimpleGrid,
Stack,
Text,
TextInput,
Title,
} from '@mantine/core';
import { IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { Tag } from '@/components/Forum';
import useFetchData from '@/hooks/useFetchData';
import Skelton from '@/components/Skelton/Skelton';

const TagsPage = () => {
const [sort, setSort] = useState('popular');
const [search, setSearch] = useState('');
const { data, loading } = useFetchData(`/api/forums/tags?sort=${sort}`);
const router = useRouter();

return (
<>
<Stack>
<Title>Tags</Title>
<Text>
A tag is a keyword or label that categorizes your question with other, similar questions.
Using the right tags makes it easier for others to find and answer your question.
</Text>
<Group justify="space-between">
<TextInput
leftSection={<IconSearch style={{ width: rem(18), height: rem(18) }} />}
placeholder="Filter by tag name"
w="min-width"
onChange={(e) => setSearch(e.currentTarget.value)}
value={search}
/>
<SegmentedControl
value={sort}
onChange={setSort}
color="indigo"
bg="white"
data={[
{ label: 'Popular', value: 'popular' },
{ label: 'Name', value: 'name' },
{ label: 'New', value: 'new' },
]}
/>
</Group>
<SimpleGrid cols={{ base: 1, xs: 2, sm: 3, md: 4 }}>
{loading ? (
<Skelton items={8} height={70} />
) : (
data
?.filter((tag) => String(tag._id).includes(search))
?.map((tag) => (
<Tag
key={String(tag._id)}
tag={String(tag._id)}
count={tag.total}
todayCount={tag.today}
onClick={() => router.push(`/forum/tagged/${tag._id}`)}
/>
))
)}
</SimpleGrid>
</Stack>
</>
);
};

export default TagsPage;
36 changes: 36 additions & 0 deletions app/api/forums/tags/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import dayjs from 'dayjs';
import { getServerSession } from 'next-auth';
import { NextRequest, NextResponse } from 'next/server';
import startDb from '@/lib/db';
import Forum from '@/models/Forum';
import { authOptions } from '../../auth/[...nextauth]/authOptions';
import { UserDataTypes } from '../../auth/[...nextauth]/next-auth.interfaces';

export const maxDuration = 60;
export const dynamic = 'force-dynamic';

export async function GET(req: NextRequest) {
const today = dayjs().startOf('day');
try {
const session: UserDataTypes | null = await getServerSession(authOptions);
if (!session?.user) {
return NextResponse.json({ error: 'You are not authorized' }, { status: 401 });
}
const sort = req.nextUrl.searchParams.get('sort')?.toString() ?? 'name';
await startDb();
const tags = await Forum.aggregate([
{ $unwind: '$tags' },
{
$group: {
_id: '$tags',
total: { $sum: 1 },
today: { $sum: { $cond: [{ $gte: ['$createdAt', today.toDate()] }, 1, 0] } },
},
},
{ $sort: sort === 'name' ? { _id: 1 } : sort === 'new' ? { createdAt: -1 } : { total: -1 } },
]);
return NextResponse.json(tags, { status: 200 });
} catch (error: any) {
return NextResponse.json({ error: error?.message }, { status: 500 });
}
}
27 changes: 27 additions & 0 deletions components/Forum/Tag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Badge, Group, Paper, Stack, Text } from '@mantine/core';
import React from 'react';

interface Props {
tag: string;
onClick: () => void;
count: number;
todayCount?: number;
}

export const Tag = ({ tag, onClick, count, todayCount }: Props) => (
<Paper p="md" withBorder>
<Stack>
<Badge style={{ cursor: 'pointer' }} onClick={onClick} radius="xs" tt="lowercase">
{tag}
</Badge>
<Group wrap="nowrap" justify="space-between">
<Text size="xs" c="dimmed">
{count} questions
</Text>
<Text size="xs" c="dimmed">
{todayCount} asked today
</Text>
</Group>
</Stack>
</Paper>
);
1 change: 1 addition & 0 deletions components/Forum/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { AskQuestion } from './AskQuestion';
export { Forum } from './Forum';
export { ForumItem } from './ForumItem';
export { ReplyToQuestion } from './ReplyToQuestion';
export { Tag } from './Tag';
4 changes: 2 additions & 2 deletions components/Skelton/Skelton.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Skeleton } from '@mantine/core';
import React from 'react';

const Skelton = ({ items = 4 }: { items?: number }) =>
[...Array(items)].map((_, i) => <Skeleton key={String(i)} height={100} mt="md" animate />);
const Skelton = ({ items = 4, height = 100 }: { items?: number; height?: number }) =>
[...Array(items)].map((_, i) => <Skeleton key={String(i)} height={height} mt="md" animate />);

export default Skelton;
6 changes: 3 additions & 3 deletions hooks/useFetchData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ export default function useFetchData(url: string, cb?: () => void) {
const res: any[] | any = await apiCall(url, null, 'GET', cb);
dispatch(set(res?.data));
dispatch(stop());
} catch (error) {
failure(String(error) || 'Something went wrong');
router.back();
} catch (err: any) {
failure(String(err) || 'Something went wrong');
router.push('/');
}
};

Expand Down
12 changes: 11 additions & 1 deletion lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
IconPrompt,
IconRobot,
IconStar,
IconTagsFilled,
IconTrash,
IconWallet,
} from '@tabler/icons-react';
Expand Down Expand Up @@ -130,7 +131,16 @@ export const APPS: AppType[] = [
color: 'green',
sidebar: [{ label: 'Calendar', path: '/calendar', icon: <IconCalendar /> }],
},
{ label: 'Forum', path: '/forum', icon: <IconMessageQuestion />, color: 'indigo', sidebar: [] },
{
label: 'Forum',
path: '/forum',
icon: <IconMessageQuestion />,
color: 'indigo',
sidebar: [
{ label: 'Home', path: '/forum', icon: <IconMessageQuestion /> },
{ label: 'Tags', path: '/forum/tags', icon: <IconTagsFilled /> },
],
},
{ label: 'Passwords', path: '/passwords', icon: <IconLock />, color: 'red', sidebar: [] },
{ label: 'Chat', path: '/chat', icon: <IconMessages />, color: 'pink', sidebar: [] },
{ label: 'Wallet', path: '/wallet', icon: <IconWallet />, color: 'grape', sidebar: [] },
Expand Down

0 comments on commit 2021f88

Please sign in to comment.