Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions src/components/Courses/LectureQuestion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React, { useState, useContext } from 'react'
import { toast } from 'react-toastify'
import { useTranslation } from 'react-i18next'
import { GoTriangleLeft } from 'react-icons/go'
import { Question } from '../../types/api.types'
import { updateQuestion, getLectureQuestions } from '../../services/api'
import { AuthContext } from '../../contexts/AuthContext'

type Props = {
question: Question
isLoggedIn: boolean
lectureId: number
updateQuestionsList: (data: Question[]) => void
}

const LectureQuestions: React.FC<Props> = ({
question,
isLoggedIn,
lectureId,
updateQuestionsList,
}) => {
const { t } = useTranslation()
const {
id,
text,
replies,
user: { profile = {} },
} = question
const { currentUser } = useContext(AuthContext)
const replier = { ...currentUser }

const [replyInput, setReplyInput] = useState('')

const handleSubmit = async () => {
try {
await updateQuestion(id, {
replies: [
{ reply: replyInput, user: replier.profile || {} },
...replies,
],
})
const data = await getLectureQuestions(lectureId)
updateQuestionsList(data)
setReplyInput('')
toast.success('Reply submitted successfully')
} catch (err) {
const message = err.message.match(/(403|400)/)
? 'errors.invalid_auth'
: 'errors.generic'
toast.error(t(message))
}
}

return (
<div className="mx-5 my-5">
<div className="flex">
<div className="h-20 w-20 bg-brmg-primary mr-1" />

<GoTriangleLeft size={30} className="text-brmg-text mt-2" />

<div className="bg-white px-3 h-20 flex-grow">
<div className="text-brmg-primary font-bold border-b-4">
{profile.name}
</div>
<div>{text}</div>
</div>
</div>

<div className="ml-28 my-2">
{isLoggedIn && (
<div className="flex mb-3 ml-28">
<input
type="text"
className="mr-3 px-2 bg-gray-100 text-gray-700 border border-gray-300 rounded block appearance-none placeholder-gray-500 focus:outline-none focus:bg-white"
placeholder="Your Reply"
value={replyInput}
onChange={e => setReplyInput(e.target.value)}
/>
<button
type="submit"
onClick={handleSubmit}
className="bg-brmg-secondary hover:bg-brmg-primary text-white font-bold py-2 px-4 rounded"
>
{t('addReply')}
</button>
</div>
)}
{replies.map(reply => (
<div className="flex my-2">
<div className="h-20 w-20 bg-brmg-primary mr-1" />

<GoTriangleLeft size={30} className="text-brmg-text mt-2" />

<div className="bg-white px-3 h-20 flex-grow">
<div className="text-brmg-primary font-bold border-b-4">
{reply.user.name}
</div>
<div>{reply.reply}</div>
</div>
</div>
))}
</div>
</div>
)
}

export default LectureQuestions
87 changes: 87 additions & 0 deletions src/components/Courses/LectureQuestions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React, { useState, useEffect } from 'react'
import { toast } from 'react-toastify'
import { useTranslation } from 'react-i18next'
import { addQuestion, getLectureQuestions } from '../../services/api'
import { Question } from '../../types/api.types'
import LectureQuestion from './LectureQuestion'

type Props = {
isLoggedIn: boolean
lectureId: number
}

const LectureQuestions: React.FC<Props> = ({ isLoggedIn, lectureId }) => {
const { t } = useTranslation()

const [questions, setQuestions] = useState<Question[]>([])
const [questionInput, setQuestionInput] = useState('')

useEffect(() => {
const fetchData = async () => {
try {
const data = await getLectureQuestions(lectureId)
setQuestions(data)
} catch (e) {
console.log(e)
}
}
fetchData()
}, [lectureId])

const updateQuestionsList = data => {
setQuestions(data)
}

const handleSubmit = async () => {
try {
await addQuestion({
text: questionInput,
replies: [],
lecture: lectureId,
})
const data = await getLectureQuestions(lectureId)
updateQuestionsList(data)
setQuestionInput('')
toast.success('Question submitted successfully')
} catch (err) {
const message = err.message.match(/(403|400)/)
? 'errors.invalid_auth'
: 'errors.generic'
toast.error(t(message))
}
}

return (
<div className="bg-gray-300">
<div className="text-3xl mb-5 ml-2">{t('questions')}</div>
{isLoggedIn && (
<div className="flex mb-3">
<input
type="text"
className="p-2 mx-3 bg-gray-100 text-gray-700 border border-gray-300 rounded block appearance-none placeholder-gray-500 focus:outline-none focus:bg-white"
placeholder="Your Question"
value={questionInput}
onChange={e => setQuestionInput(e.target.value)}
/>
<button
type="submit"
onClick={handleSubmit}
className="bg-brmg-secondary hover:bg-brmg-primary text-white font-bold py-2 px-4 rounded"
>
{t('addQuestion')}
</button>
</div>
)}
{questions.map(question => (
<LectureQuestion
question={question}
isLoggedIn={isLoggedIn}
lectureId={lectureId}
updateQuestionsList={updateQuestionsList}
/>
))}
</div>
)
}

export default LectureQuestions
3 changes: 3 additions & 0 deletions src/locales/am/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@
"deleteUserData": "Delete My User Data",
"confirmUserDelete": "Are you sure you want to delete all your user data ?",
"cancel": "Cancel",
"questions": "Questions",
"addQuestion": "Add Question",
"addReply": "Add Reply",
"validation": {
"required": "ይሄ ቦታ አስፈላጊ ነው",
"email": "Please enter a valid email"
Expand Down
3 changes: 3 additions & 0 deletions src/locales/ar/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@
"deleteUserData": "حذف كل بياناتي الشخصية",
"confirmUserDelete": "هل انت متأكد من حذف كل بياناتك الشخصية ؟",
"cancel": "الغاء",
"questions": "اﻷسئلة",
"addQuestion": "اضافة سؤال",
"addReply": "اضافة رد",
"validation": {
"required": "هذ الحقل مطلوب",
"email": "الرجاء ادخال بريد الكتروني صحيح"
Expand Down
3 changes: 3 additions & 0 deletions src/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@
"deleteUserData": "Delete My User Data",
"confirmUserDelete": "Are you sure you want to delete all your user data ?",
"cancel": "Cancel",
"questions": "Questions",
"addQuestion": "Ask Question",
"addReply": "Add Reply",
"validation": {
"required": "This field is required",
"email": "Please enter a valid email"
Expand Down
3 changes: 3 additions & 0 deletions src/locales/sw/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@
"deleteUserData": "Delete My User Data",
"confirmUserDelete": "Are you sure you want to delete all your user data ?",
"cancel": "Cancel",
"questions": "Questions",
"addQuestion": "Add Question",
"addReply": "Add Reply",
"validation": {
"required": "Taarifa hii inahitajika",
"email": "Please enter a valid email"
Expand Down
23 changes: 23 additions & 0 deletions src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
UserSubscribeToMailingList,
UserPrToReviewData,
CourseRating,
Question,
} from '../types/api.types'

const axiosInstance = () => {
Expand Down Expand Up @@ -100,6 +101,28 @@ export const subscribeToMailingList = async (
return data
}

export const getLectureQuestions = async (id: number): Promise<Question[]> => {
const { data } = await axiosInstance().get(
`/questions?_sort=created_at:DESC&lecture=${id}`
)
return data
}

export const addQuestion = async (
values: Partial<Question>
): Promise<Question> => {
const { data } = await axiosInstance().post(`/questions`, values)
return data
}

export const updateQuestion = async (
questionId: number,
values: Partial<Question>
): Promise<Question> => {
const { data } = await axiosInstance().put(`/questions/${questionId}`, values)
return data
}

/**
* Upload file to strapi media
* @param {*} file The file object
Expand Down
5 changes: 5 additions & 0 deletions src/templates/LectureView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import CourseCard from '../../components/Courses/CourseCard'
import { AuthContext } from '../../contexts/AuthContext'
import { ALLOWED_LECTURES_WHEN_NOT_LOGGED_IN } from '../../common/constants'
import { Course, Lecture } from '../../types/api.types'
import LectureQuestions from '../../components/Courses/LectureQuestions'

type LectureViewProps = {
data: {
Expand All @@ -28,6 +29,7 @@ type LectureViewProps = {
const LectureView: React.FC<LectureViewProps> = ({ data, location }) => {
const { t } = useTranslation()
const { strapiLecture, strapiCourse, relatedCourses } = data

const sortedLectures = orderBy(strapiCourse.lectures, 'position', 'asc')
const lecture = !strapiLecture ? sortedLectures[0] : strapiLecture
const { isLoggedIn, currentUser } = useContext(AuthContext)
Expand Down Expand Up @@ -144,6 +146,9 @@ const LectureView: React.FC<LectureViewProps> = ({ data, location }) => {
courseStrapiId={courseStrapiId}
/>
</div>
<div>
<LectureQuestions isLoggedIn={isLoggedIn} lectureId={strapiId} />
</div>
</div>
</div>
{relatedCourses.edges.length !== 0 && (
Expand Down
16 changes: 15 additions & 1 deletion src/types/api.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,21 @@ export type Resource = {
type: ResourceType
}

export type Lecture = {
export type Reply = {
reply: string
user: Partial<Profile>
}

export type Question = {
id: number
text: string
replies: Reply[]
lecture: number
user: User
}

export type Lecture = {
id: string
strapiId: number
title: string
description?: string
Expand All @@ -124,6 +137,7 @@ export type Lecture = {
position: number
slug: string
created_at: string
questions: Question[]
}

export type Course = {
Expand Down