Skip to content

Commit

Permalink
feat: answers in forum
Browse files Browse the repository at this point in the history
  • Loading branch information
vishalkondle-dev committed Jul 10, 2024
1 parent 9545be4 commit 387eec1
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 54 deletions.
65 changes: 62 additions & 3 deletions app/(private)/forum/[forum]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,86 @@
'use client';

import { Button, Group, Stack } from '@mantine/core';
import { useEffect, useState } from 'react';
import { Button, Divider, Group, rem, Select, Stack, Text } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import dayjs from 'dayjs';
import { Forum, AskQuestion, ReplyToQuestion } from '@/components/Forum';
import useFetchData from '@/hooks/useFetchData';
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 [opened, { open, close }] = useDisclosure(false);
const [value, setValue] = useState<string | null>('highest');
const [answers, setAnswers] = useState(data?.[1]);

useEffect(() => {
if (!data?.length || !data[1]) {
return;
}
const sortIt = [...data[1]];
switch (value) {
case 'newest':
sortIt.sort((a: any, b: any) => dayjs(b.updatedAt).diff(dayjs(a.updatedAt), 'seconds'));
break;
case 'oldest':
sortIt.sort((a: any, b: any) => dayjs(a.updatedAt).diff(dayjs(b.updatedAt), 'seconds'));
break;
default:
sortIt.sort(
(a: any, b: any) =>
b.upvotes.length - b.downvotes.length - (a.upvotes.length - a.downvotes.length)
);
break;
}
setAnswers(sortIt);
}, [value, data]);

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

return (
<>
<Group mb="md" justify="right">
<Group mb="md" justify="space-between">
<Stack>
<Text
fw={700}
fz="lg"
dangerouslySetInnerHTML={{ __html: String(data?.[0]?.question) }}
/>
<Group>
<Text c="dimmed" size={rem(14)}>
Asked {dayjs(data?.[0]?.createdAt).fromNow()}
</Text>
<Text c="dimmed" size={rem(14)}>
Modified {dayjs(data?.[0]?.updatedAt).fromNow()}
</Text>
<Text c="dimmed" size={rem(14)}>
Viewed {data?.[0]?.views?.length} times
</Text>
</Group>
</Stack>
<Button onClick={open}>Ask Question</Button>
</Group>
<Divider mb="md" />
<Stack gap="xl">
<Forum forum={data?.[0]} refetch={refetch} />
{data?.[1]?.map((forum: ForumType) => (
<Divider variant="dashed" />
<Group gap={0} wrap="nowrap" justify="space-between">
<Text fw={700} size="lg">
{data?.[1]?.length} Answers
</Text>
<Select
data={FORUM_ANSWERS_SORT_OPTIONS}
allowDeselect={false}
value={value}
onChange={setValue}
placeholder="Sort by"
/>
</Group>
{answers?.map((forum: ForumType) => (
<Forum key={String(forum._id)} forum={forum} refetch={refetch} />
))}
<ReplyToQuestion refetch={refetch} parent={data[0]?._id} />
Expand Down
4 changes: 3 additions & 1 deletion app/api/forums/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ export async function GET(req: NextRequest) {

if (_id) {
const forum = await Forum.findById(_id).populate({ path: 'user', select: 'name' });
const answers = await Forum.find({ parent: _id }).populate({ path: 'user', select: 'name' });
const answers = await Forum.find({ parent: _id })
.populate({ path: 'user', select: 'name' })
.select('-question -__v -tags -parent -views -saved -answers');
if (!forum?.views.includes(new mongoose.Types.ObjectId(session?.user._id))) {
await forum?.updateOne({ $push: { views: session?.user._id } }, { new: true });
}
Expand Down
10 changes: 10 additions & 0 deletions components/Document/Document.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';

import { useEffect } from 'react';
import {
ActionIcon,
Button,
Expand Down Expand Up @@ -121,6 +122,15 @@ const Document = ({ content, onUpdate, isDocumentPage = true, placeholder }: Pro
printWindow?.close();
};

useEffect(
() => () => {
if (editor) {
editor.destroy();
}
},
[]
);

return (
<>
<RichTextEditor bg="white" editor={editor}>
Expand Down
64 changes: 19 additions & 45 deletions components/Forum/Forum.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,13 @@
import {
ActionIcon,
Avatar,
Badge,
Button,
Divider,
Group,
Paper,
rem,
Stack,
Text,
} from '@mantine/core';
import { ActionIcon, Avatar, Badge, Button, Group, Paper, rem, Stack, Text } from '@mantine/core';
import { useSession } from 'next-auth/react';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import { useRouter } from 'next/navigation';
import {
IconBookmark,
IconBookmarkFilled,
IconChevronDown,
IconChevronUp,
IconCaretDownFilled,
IconCaretUpFilled,
IconMessageReply,
IconQuestionMark,
} from '@tabler/icons-react';
Expand Down Expand Up @@ -49,7 +38,11 @@ export const Forum = ({ forum, refetch }: Props) => {
const onDelete = () =>
openModal('This forum will be deleted permanently', () => {
apiCall(`/api/forums?_id=${forum?._id}`, {}, 'DELETE').then(() => {
router.push('/forum');
if (forum?.question) {
router.push('/forum');
} else {
refetch();
}
});
});

Expand Down Expand Up @@ -99,44 +92,25 @@ export const Forum = ({ forum, refetch }: Props) => {
variant="filled"
color="teal"
>
<IconChevronUp />
<IconCaretUpFilled />
</ActionIcon>
<Text fw={700}>{(forum?.upvotes?.length || 0) - (forum?.downvotes?.length || 0)}</Text>
<ActionIcon
disabled={isDownVoted}
onClick={onDownVote}
radius="xl"
variant="outline"
variant="filled"
color="red"
>
<IconChevronDown />
</ActionIcon>
<ActionIcon onClick={onBookmark} radius="xl" variant="transparent">
{isSaved ? <IconBookmarkFilled /> : <IconBookmark />}
<IconCaretDownFilled />
</ActionIcon>
</Stack>
<Stack w="100%">
{forum?.question && (
<>
<Text
fw={700}
fz="lg"
dangerouslySetInnerHTML={{ __html: String(forum?.question) }}
/>
<Group>
<Text c="dimmed" size={rem(14)}>
Asked {dayjs(forum?.createdAt).fromNow()}
</Text>
<Text c="dimmed" size={rem(14)}>
Modified {dayjs(forum?.updatedAt).fromNow()}
</Text>
<Text c="dimmed" size={rem(14)}>
Viewed {forum?.views?.length} times
</Text>
</Group>
<Divider />
</>
<ActionIcon onClick={onBookmark} radius="xl" variant="transparent">
{isSaved ? <IconBookmarkFilled /> : <IconBookmark />}
</ActionIcon>
)}
</Stack>
<Stack w="100%">
<Text dangerouslySetInnerHTML={{ __html: String(forum?.description) }} />
<Group>
{forum?.tags?.map((tag) => (
Expand All @@ -154,15 +128,15 @@ export const Forum = ({ forum, refetch }: Props) => {
</Group>
<Group align="baseline" justify="space-between">
<Group gap={rem(2)}>
<Button variant="transparent" color="gray" size="compact-xs">
<Button variant="transparent" color="teal" size="compact-xs">
Share
</Button>
{session.data.user._id === forum?.user?._id && (
<>
<Button variant="transparent" color="gray" size="compact-xs">
<Button variant="transparent" color="blue" size="compact-xs">
Edit
</Button>
<Button variant="transparent" color="gray" size="compact-xs" onClick={onDelete}>
<Button variant="transparent" color="red" size="compact-xs" onClick={onDelete}>
Delete
</Button>
</>
Expand Down
6 changes: 6 additions & 0 deletions components/Forum/ReplyToQuestion.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useState } from 'react';
import { Button, Paper, Text } from '@mantine/core';
import { useForm } from '@mantine/form';
import { nprogress } from '@mantine/nprogress';
Expand All @@ -10,6 +11,7 @@ interface Props {
}

export const ReplyToQuestion = ({ parent, refetch }: Props) => {
const [loader, setLoader] = useState(false);
const form = useForm({
initialValues: {
description: '',
Expand All @@ -20,6 +22,7 @@ export const ReplyToQuestion = ({ parent, refetch }: Props) => {
});

const onSubmit = async () => {
setLoader(true);
try {
nprogress.start();
await apiCall('/api/forums/reply', { description: form.values.description, parent }, 'POST');
Expand All @@ -29,9 +32,12 @@ export const ReplyToQuestion = ({ parent, refetch }: Props) => {
failure(error);
} finally {
nprogress.complete();
setLoader(false);
}
};

if (loader) return <></>;

return (
<Paper p="lg" withBorder>
<Text fw={700} mb="lg">
Expand Down
17 changes: 12 additions & 5 deletions hooks/useFetchData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import { useDispatch, useSelector } from 'react-redux';
import { useEffect } from 'react';
import { apiCall } from '@/lib/client_functions';
import { useRouter } from 'next/navigation';
import { apiCall, failure } from '@/lib/client_functions';
import { start, stop } from '@/store/features/loaderSlice';
import { RootState } from '@/store/store';
import { set } from '@/store/features/dataSlice';
Expand All @@ -11,12 +12,18 @@ export default function useFetchData(url: string, cb?: () => void) {
const dispatch = useDispatch();
const loading = useSelector((state: RootState) => state.loader.loading);
const data = useSelector((state: RootState) => state.data.data);
const router = useRouter();

const refetch = async () => {
dispatch(start());
const res: any[] | any = await apiCall(url, null, 'GET', cb);
dispatch(set(res?.data));
dispatch(stop());
try {
dispatch(start());
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();
}
};

useEffect(() => {
Expand Down
15 changes: 15 additions & 0 deletions lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,18 @@ export const hoursIcons = [
<IconNumber23Small />,
<IconNumber24Small />,
];

export const FORUM_ANSWERS_SORT_OPTIONS = [
{
label: 'Highest score',
value: 'highest',
},
{
label: 'Newest',
value: 'newest',
},
{
label: 'Oldest',
value: 'oldest',
},
];

0 comments on commit 387eec1

Please sign in to comment.