Skip to content

Commit

Permalink
feat(view-course/lessons): implement page for bought course and lesso…
Browse files Browse the repository at this point in the history
…ns with video
  • Loading branch information
danilych committed Feb 13, 2024
1 parent eb4d971 commit 2ae471f
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 12 deletions.
2 changes: 1 addition & 1 deletion app/entities/course-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface Props {

export function CourseCard({ id, picturePath, name, price, rating, isBought, isForEdit }: Props) {
return (
<Link to={isBought ? `/` : isForEdit ? `/edit-course/${id}` : `/courses/${id}`}>
<Link to={isBought ? `/learn/${id}` : isForEdit ? `/edit-course/${id}` : `/courses/${id}`}>
<div className="w-[484px] relative h-[570px] bg-[#F6F6F6] pt-6 rounded-[20px] px-5">
<img
className="h-[250px] w-[444px] mx-auto rounded-[20px]"
Expand Down
30 changes: 23 additions & 7 deletions app/features/course-element.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import clsx from 'clsx'
import { useState } from 'react'
import { DeleteIcon, EditIcon } from 'assets/icons'
import { useDispatch } from 'react-redux'
import type { ThunkDispatch } from '@reduxjs/toolkit'
import toast from 'react-hot-toast'
import { removeBlock } from '~/redux/slices/course'
import { delay } from '~/widgets/helpers/delay'
import { ToasterWithOptions, TransparentButton } from '~/shared'
import { Link } from '@remix-run/react'
import { ToasterWithOptions } from '~/shared'
import { useNavigate } from '@remix-run/react'
import { fetchLesson } from '~/redux/slices/lesson'

interface Props {
header: string
Expand All @@ -16,6 +13,7 @@ interface Props {
className?: string
items: any
lecturesCount: string
isUpdateId?: boolean
}

export function CourseElement({
Expand All @@ -25,13 +23,31 @@ export function CourseElement({
id,
items,
lecturesCount,
isUpdateId
}: Props) {
const [isVisibly, setVisibly] = useState(false)

const dispatch = useDispatch<ThunkDispatch<any, any, any>>()

const navigate = useNavigate()

function changeVisibly() {
setVisibly(!isVisibly)
}

function setVideoPath(id_: string) {
console.log("bebra")
window.localStorage.setItem('lectureId', id_)

if(isUpdateId) {
let requestData = new FormData()
requestData.append('courseItemId', id_)
dispatch(fetchLesson(requestData))

navigate(`/learn/lesson/${id_}`)
}
}

return (
<div
className={clsx(
Expand Down Expand Up @@ -64,7 +80,7 @@ export function CourseElement({
)}
>
{items.map((item: any, index: number) => (
<div key={index} className="py-3 pl-7 mt-1">
<div onClick={() => setVideoPath(item.courseItemId)} key={index} className="py-3 pl-7 mt-1">
<p className="text-manrope font-normal text-base text-[#1a1a1b]">
{item.elementName}
</p>
Expand Down
1 change: 0 additions & 1 deletion app/redux/slices/author.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import moment from 'moment'
import instance from '~/axios'

export const fetchAuthor = createAsyncThunk(
Expand Down
45 changes: 45 additions & 0 deletions app/redux/slices/lesson.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import instance from '~/axios'

export const fetchLesson = createAsyncThunk(
'lesson/fetchLesson',
async (params: any) => {
const { data } = await instance.post('/Lesson/GetLessonFromCourseItem', params)

return data
}
)

const initialState = {
lesson: {
data: null,
isError: false,
status: 'loading',
},
}

const lessonSlice = createSlice({
name: 'lesson',
initialState,
reducers: {},
extraReducers: builder => {
builder.addCase(fetchLesson.pending, (state, action) => {
state.lesson.status = 'loading'

// @ts-ignore
state.lesson.data = null
})
builder.addCase(fetchLesson.fulfilled, (state, action) => {
state.lesson.status = 'loaded'
state.lesson.data = action.payload
})
builder.addCase(fetchLesson.rejected, (state, action) => {
state.lesson.status = 'error'

// @ts-ignore
state.lesson.data = null
})
},
})

export const lessonItemReducer = lessonSlice.reducer
2 changes: 2 additions & 0 deletions app/redux/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { courseReducer } from "./slices/courses";
import { userReducer } from "./slices/user";
import { courseItemReducer } from "./slices/course";
import { authorReducer } from "./slices/author";
import { lessonItemReducer } from "./slices/lesson";

const store = configureStore({
reducer: {
Expand All @@ -12,6 +13,7 @@ const store = configureStore({
auth: authReducer,
user: userReducer,
author: authorReducer,
lesson: lessonItemReducer,
},
});

Expand Down
3 changes: 2 additions & 1 deletion app/routes/courses.$course.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { ListElement } from '~/features'
import { CourseElement } from '~/features/course-element'
import { fetchAuthor } from '~/redux/slices/author'
import { fetchCourse } from '~/redux/slices/courses'
import { fetchUser } from '~/redux/slices/user'
import { FilledButton, Header2, Header3, Header4, Text } from '~/shared'

export default function Course() {
Expand All @@ -27,6 +26,8 @@ export default function Course() {
formData.append('CourseID', params.course as string)
formData.append('UserID', window.localStorage.getItem('userId') as string)
dispatch(fetchCourse(formData))

window.localStorage.getItem('lectureID')
}, [])

Check warning on line 31 in app/routes/courses.$course.tsx

View workflow job for this annotation

GitHub Actions / pipeline (21.x)

React Hook useEffect has missing dependencies: 'dispatch' and 'params.course'. Either include them or remove the dependency array

useEffect(() => {

Check warning on line 33 in app/routes/courses.$course.tsx

View workflow job for this annotation

GitHub Actions / pipeline (21.x)

React Hook useEffect contains a call to 'setIsPostLoading'. Without a list of dependencies, this can lead to an infinite chain of updates. To fix this, pass [course.status, course.data, dispatch] as a second argument to the useEffect Hook
Expand Down
120 changes: 120 additions & 0 deletions app/routes/learn.$course.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import type { ThunkDispatch } from '@reduxjs/toolkit'
import { useParams } from '@remix-run/react'
import { Spinner } from 'flowbite-react'
import { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { CourseElement } from '~/features/course-element'
import { fetchAuthor } from '~/redux/slices/author'
import { fetchFullCourse } from '~/redux/slices/course'
import { Header2, Header3, Header4, Text } from '~/shared'

export default function Course() {
const params = useParams()

const dispatch = useDispatch<ThunkDispatch<any, any, any>>()

// @ts-ignore
const { course } = useSelector(state => state.course)
// @ts-ignore
const { lesson } = useSelector(state => state.lesson)

const [isPostsLoading, setIsPostLoading] = useState(true)

useEffect(() => {
let formData = new FormData()
formData.append('ID', params.course as string)
// formData.append('ID', window.localStorage.getItem('userId') as string)
dispatch(fetchFullCourse(formData))

// setVideoPath(course.data.courseElementList[0].courseItems[0].videoPath)
console.log('dataview', course.data)
}, [])

Check warning on line 31 in app/routes/learn.$course.tsx

View workflow job for this annotation

GitHub Actions / pipeline (21.x)

React Hook useEffect has missing dependencies: 'course.data', 'dispatch', and 'params.course'. Either include them or remove the dependency array

if(lesson.data != null) console.log("lesson",lesson.data.videoPath!)
// console.log("lesson",lesson.data.videoPath!)

useEffect(() => {

Check warning on line 36 in app/routes/learn.$course.tsx

View workflow job for this annotation

GitHub Actions / pipeline (21.x)

React Hook useEffect contains a call to 'setIsPostLoading'. Without a list of dependencies, this can lead to an infinite chain of updates. To fix this, pass [course.status, course.data, dispatch] as a second argument to the useEffect Hook
if (course.status === 'loading') setIsPostLoading(true)

if (course.status === 'loaded') setIsPostLoading(false)

if (course.data != null) {
let authorFormData = new FormData()
authorFormData.append(
'encriptId',
course.data.authorId.toString() as string
)

dispatch(fetchAuthor(authorFormData))
}
})

return (
<div className="py-12 my-[56px] mx-auto">
{isPostsLoading ? (
<div className="w-full h-screen">
<Spinner
className="absolute top-1/2 left-1/2 mt-[-50px] ml-[-50px]"
aria-label="Extra large spinner example"
size="xl"
/>
</div>
) : (
<div className="w-full">
<Header2 className="text-center mt-4">{course.data.name}</Header2>

<img
className="w-full h-[800px] mt-[50px]"
src={
'https://mystudystorage.blob.core.windows.net/test/' +
course.data.picturePath
}
alt=""
/>

<div className="mt-[92px] pl-[400px]">
<div className="w-[739px]">
<Header3>Про курс</Header3>

<Header4 className="mt-6">Цей курс містить:</Header4>

<Text className="mt-4 max-w-[738px]">
{course.data.description}
</Text>
</div>

<div className="w-[992px] mt-[92px]">
<Header3>Програма курсу</Header3>

<div className="mt-6">
{/* ts-ignore */}
{course.data.courseElementsList.map(
(element: any, index: number) => (
<div key={index} className="w-[992px] mt-4">
<CourseElement
isUpdateId={true}
lecturesCount={element.lessonsCount}
items={element.courseItems}
id={element.id}
header={element.name}
index={index + 1}
/>
</div>
)
)}
</div>
</div>

<div className="mt-[92px] w-[1200px]">
<Header3>Ваш викладач</Header3>

<div className="flex flex-row gap-[42px]">
<img src="" alt="" />
</div>
</div>
</div>
</div>
)}
</div>
)
}
43 changes: 43 additions & 0 deletions app/routes/learn.lesson.$lesson.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Spinner } from 'flowbite-react'
import { useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import { Header2} from '~/shared'

export default function Course() {
// @ts-ignore
const { lesson } = useSelector(state => state.lesson)

const [isPostsLoading, setIsPostLoading] = useState(true)

useEffect(() => {
if (lesson.data != null) setIsPostLoading(false)
})

return (
<div className="py-12 my-[56px] mx-auto">
{isPostsLoading ? (
<div className="w-full h-screen">
<Spinner
className="absolute top-1/2 left-1/2 mt-[-50px] ml-[-50px]"
aria-label="Extra large spinner example"
size="xl"
/>
</div>
) : (
<div className="w-full">
<Header2 className="text-center mt-4">{lesson.data.theme}</Header2>

<video className="mt-[50px]" controls width="100%">
<source
src={
'https://mystudystorage.blob.core.windows.net/test/' + lesson.data.videoPath!
}
type="video/mp4"
/>
Sorry, your browser doesn't support videos.
</video>
</div>
)}
</div>
)
}
11 changes: 9 additions & 2 deletions app/routes/my-account.courses.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,17 @@ export default function Courses() {
</div>
) : (
<div className="mt-[42px] w-[1500px] mx-auto grid grid-cols-3 gap-2">

{/* @ts-ignore */}
{courses.items.map((course: any) => (
<CourseCard key={course.id} id={course.id} picturePath={course.picturePath} name={course.name} price={course.price} rating={course.rating} />
<CourseCard
isBought={true}
key={course.id}
id={course.id}
picturePath={course.picturePath}
name={course.name}
price={course.price}
rating={course.rating}
/>
))}
</div>
)}
Expand Down

0 comments on commit 2ae471f

Please sign in to comment.