Skip to content

Commit 6a7c994

Browse files
committed
adding community page
1 parent 31852e3 commit 6a7c994

File tree

7 files changed

+443
-22
lines changed

7 files changed

+443
-22
lines changed

src/components/Buttons.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* eslint-disable */
2+
/* eslint-disable */
23
import React from 'react';
34

45
interface Props {
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
import React, { useState, useEffect } from 'react';
2+
import { useQuery, useMutation } from '@apollo/client';
3+
import { toast } from 'react-toastify';
4+
import {
5+
GET_ALL_QUESTIONS,
6+
CREATE_QUESTION,
7+
CREATE_ANSWER,
8+
DELETE_QUESTION,
9+
DELETE_ANSWER,
10+
} from '../queries/questions.query';
11+
12+
function CommunityQuestions() {
13+
const { loading, error, data, refetch } = useQuery(GET_ALL_QUESTIONS);
14+
const [createQuestion, { loading: isSubmitting }] =
15+
useMutation(CREATE_QUESTION);
16+
const [createAnswer] = useMutation(CREATE_ANSWER);
17+
const [deleteQuestion] = useMutation(DELETE_QUESTION);
18+
const [deleteAnswer] = useMutation(DELETE_ANSWER);
19+
const [loggedUser, setLoggedUser] = useState<string | null>(null);
20+
const [questionText, setQuestionText] = useState('');
21+
const [answerText, setAnswerText] = useState('');
22+
const [selectedQuestion, setSelectedQuestion] = useState<any>(null);
23+
const [questionTitleText, setQuestionTitleText] = useState<any>(null);
24+
25+
useEffect(() => {
26+
const authData = localStorage.getItem('auth');
27+
if (authData) {
28+
const parsedAuthData = JSON.parse(authData);
29+
setLoggedUser(parsedAuthData.userId);
30+
}
31+
}, []);
32+
33+
const questions = data?.getAllQuestions || [];
34+
if (loading) return <p>Loading...</p>;
35+
if (error) return <p>Error loading questions.</p>;
36+
37+
const handleCreateQuestion = async () => {
38+
if (questionText.trim()) {
39+
await createQuestion({
40+
variables: {
41+
title: questionTitleText,
42+
content: questionText,
43+
},
44+
update: (cache, { data: { createQuestion } }) => {
45+
const existingQuestions: any = cache.readQuery({
46+
query: GET_ALL_QUESTIONS,
47+
});
48+
cache.writeQuery({
49+
query: GET_ALL_QUESTIONS,
50+
data: {
51+
getAllQuestions: [
52+
...existingQuestions.getAllQuestions,
53+
createQuestion,
54+
],
55+
},
56+
});
57+
},
58+
onError: (error) => {
59+
toast.error('Unable to create question.');
60+
},
61+
onCompleted: () => {
62+
toast.success('Question created successfully');
63+
refetch();
64+
setQuestionText('');
65+
setQuestionTitleText('');
66+
},
67+
});
68+
} else {
69+
toast.error('Question text cannot be empty.');
70+
}
71+
};
72+
73+
const handleCreateAnswer = async (questionId: string) => {
74+
if (answerText.trim()) {
75+
await createAnswer({
76+
variables: {
77+
questionId,
78+
content: answerText,
79+
},
80+
update: (cache, { data: { createAnswer } }) => {
81+
const existingQuestions: any = cache.readQuery({
82+
query: GET_ALL_QUESTIONS,
83+
});
84+
const updatedQuestions = existingQuestions.getAllQuestions.map(
85+
(question: any) => {
86+
if (question?.id === questionId) {
87+
return {
88+
...question,
89+
answers: [...(question?.answers || []), createAnswer],
90+
};
91+
}
92+
return question;
93+
},
94+
);
95+
cache.writeQuery({
96+
query: GET_ALL_QUESTIONS,
97+
data: { getAllQuestions: updatedQuestions },
98+
});
99+
},
100+
onError: (error) => {
101+
toast.error('Unable to submit your answer.');
102+
},
103+
onCompleted: () => {
104+
toast.success('Answer submitted successfully');
105+
refetch({ query: GET_ALL_QUESTIONS });
106+
setSelectedQuestion('');
107+
108+
setAnswerText('');
109+
},
110+
});
111+
} else {
112+
toast.error('Answer text cannot be empty.');
113+
}
114+
};
115+
116+
const handleDeleteQuestion = async (questionId: string) => {
117+
await deleteQuestion({
118+
variables: { id: questionId },
119+
update: (cache) => {
120+
const existingQuestions: any = cache.readQuery({
121+
query: GET_ALL_QUESTIONS,
122+
});
123+
const updatedQuestions = existingQuestions.getAllQuestions.filter(
124+
(question: any) => question?.id !== questionId,
125+
);
126+
cache.writeQuery({
127+
query: GET_ALL_QUESTIONS,
128+
data: { getAllQuestions: updatedQuestions },
129+
});
130+
},
131+
onError: (error) => {
132+
toast.error('Unable to delete the question.');
133+
},
134+
onCompleted: () => {
135+
toast.success('Question deleted successfully');
136+
setSelectedQuestion('');
137+
138+
refetch();
139+
},
140+
});
141+
};
142+
143+
const handleDeleteAnswer = async (answerId: string) => {
144+
await deleteAnswer({
145+
variables: { id: answerId },
146+
update: (cache) => {
147+
const existingQuestions: any = cache.readQuery({
148+
query: GET_ALL_QUESTIONS,
149+
});
150+
const updatedQuestions = existingQuestions.getAllQuestions.map(
151+
(question: any) => ({
152+
...question,
153+
answers: question?.answers.filter(
154+
(answer: any) => answer.id !== answerId,
155+
),
156+
}),
157+
);
158+
cache.writeQuery({
159+
query: GET_ALL_QUESTIONS,
160+
data: { getAllQuestions: updatedQuestions },
161+
});
162+
},
163+
onError: (error) => {
164+
toast.error('Unable to delete the answer.');
165+
},
166+
onCompleted: () => {
167+
toast.success('Answer deleted successfully');
168+
setSelectedQuestion('');
169+
refetch({ query: GET_ALL_QUESTIONS });
170+
},
171+
});
172+
};
173+
174+
return (
175+
<div className="flex w-full p-4 bg-indigo-100 dark:bg-transparent rounded-md shadow-lg">
176+
<div className="w-1/3 pr-4 border-r border-divider-bg dark:border-border-dark">
177+
<h2 className="text-lg font-semibold text-center text-header-text dark:text-dark-text-fill">
178+
Questions
179+
</h2>
180+
{loggedUser && (
181+
<div className="mt-4 -4 bg-tertiary rounded-md flex flex-col gap-2">
182+
<input
183+
type="text"
184+
value={questionTitleText}
185+
onChange={(e) => setQuestionTitleText(e.target.value)}
186+
placeholder="Write question title..."
187+
className="p-2 border border-gray-300 dark:border-gray-600 dark:text-black rounded w-full"
188+
/>
189+
<textarea
190+
value={questionText}
191+
onChange={(e) => setQuestionText(e.target.value)}
192+
rows={3}
193+
className="w-full p-2 border rounded-md dark:text-black"
194+
placeholder="Ask a question..."
195+
/>
196+
<button
197+
onClick={handleCreateQuestion}
198+
disabled={isSubmitting}
199+
type="button"
200+
className="mt-2 w-full p-2 bg-primary text-white rounded-md disabled:opacity-50"
201+
>
202+
{isSubmitting ? 'Submitting...' : 'Submit Question'}
203+
</button>
204+
</div>
205+
)}
206+
<ul className="mt-4 space-y-4">
207+
{questions?.map((question: any) => (
208+
<li
209+
key={question?.id}
210+
className={`cursor-pointer p-3 rounded-md shadow-sm ${
211+
selectedQuestion?.id === question?.id
212+
? 'bg-primary text-dark-text-fill'
213+
: 'bg-tertiary text-light-text dark:bg-dark-tertiary dark:text-dark-text-fill'
214+
}`}
215+
onClick={() => setSelectedQuestion(question)}
216+
onKeyPress={(e) => {
217+
if (e.key === 'Enter' || e.key === ' ') {
218+
setSelectedQuestion(question);
219+
}
220+
}}
221+
role="presentation"
222+
>
223+
<p className="text-md font-semibold">{question?.title}</p>
224+
<p className="text-sm text-secondary dark:text-dark-text-fill">
225+
Asked by: {question?.author?.email}
226+
</p>
227+
</li>
228+
))}
229+
</ul>
230+
</div>
231+
232+
<div className="w-2/3 pl-4">
233+
{selectedQuestion ? (
234+
<div>
235+
<h2 className="text-lg font-bold text-primary">
236+
{selectedQuestion?.title}
237+
</h2>
238+
<span>{selectedQuestion?.content}</span>
239+
<p className="text-sm text-secondary">
240+
Asked by {selectedQuestion?.author?.email}
241+
</p>
242+
<div className="mt-4">
243+
<h3 className="font-semibold text-secondary dark:text-dark-text-fill">
244+
Answers:
245+
</h3>
246+
{selectedQuestion?.answers?.length > 0 ? (
247+
selectedQuestion?.answers.map((answer: any) => (
248+
<div
249+
key={answer.id}
250+
className="mt-2 p-2 bg-light-bg dark:bg-dark-tertiary rounded-md"
251+
>
252+
<p>{answer.content}</p>
253+
<p className="text-sm text-secondary dark:text-dark-text-fill">
254+
Answered by {answer.author.email}
255+
</p>
256+
{loggedUser && loggedUser === answer?.author?.id && (
257+
<button
258+
type="button"
259+
onClick={() => handleDeleteAnswer(answer.id)}
260+
className="mt-4 text-red-500 text-sm"
261+
>
262+
Delete Answer
263+
</button>
264+
)}
265+
</div>
266+
))
267+
) : (
268+
<p className="text-sm text-gray-500 dark:text-dark-45">
269+
No answers yet. Be the first to answer!
270+
</p>
271+
)}
272+
273+
{loggedUser && (
274+
<div className="mt-4">
275+
<input
276+
type="text"
277+
value={answerText}
278+
onChange={(e) => setAnswerText(e.target.value)}
279+
placeholder="Write an answer..."
280+
className="p-2 border border-gray-300 dark:border-gray-600 dark:text-black rounded w-full"
281+
/>
282+
<button
283+
type="button"
284+
onClick={() => handleCreateAnswer(selectedQuestion?.id)}
285+
className="mt-2 p-2 bg-primary text-white rounded w-full"
286+
>
287+
Submit Answer
288+
</button>
289+
</div>
290+
)}
291+
</div>
292+
{loggedUser && loggedUser === selectedQuestion?.author?.id && (
293+
<button
294+
type="button"
295+
onClick={() => handleDeleteQuestion(selectedQuestion?.id)}
296+
className="mt-4 text-red-500 text-sm"
297+
>
298+
Delete Question
299+
</button>
300+
)}
301+
</div>
302+
) : (
303+
<p className="text-lg text-gray-500 dark:text-dark-45">
304+
Select a question to view details.
305+
</p>
306+
)}
307+
</div>
308+
</div>
309+
);
310+
}
311+
312+
export default CommunityQuestions;

src/components/Footer.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
FaPlayCircle,
1010
} from 'react-icons/fa';
1111
import i18next from 'i18next';
12+
import { Link } from 'react-router-dom';
1213
import LogoFooter from '../assets/logo.svg';
1314
import getLanguage from '../utils/getLanguage';
1415
import LogoIcon from './logoIcon';
@@ -76,7 +77,9 @@ function Footer({ styles }: any) {
7677
<div className="w-full sm:w-1/2 md:w-1/4 p-4 text-center lg:text-left">
7778
<h3 className="font-bold mb-2">{t('Resources')}</h3>
7879
<ul>
79-
<li className="mb-1">{t('Community')}</li>
80+
<li className="mb-1">
81+
<Link to="/community">{t('Community')} </Link>
82+
</li>
8083
<li className="mb-1">{t('Help Center')}</li>
8184
</ul>
8285
</div>

0 commit comments

Comments
 (0)