Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Language Metadata Filtering #188

Merged
merged 3 commits into from
Jul 26, 2024
Merged
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
7 changes: 2 additions & 5 deletions components/Card/Course/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function CourseCard({ course }) {
<Button auto color={''} bordered rounded icon={<BiTimeFive />}>
<Text weight={'bold'} size={'$xs'}>
{' '}
{course.duration}{' '}
<RenderField object={course} field="duration" isHtml={true} maxSize={200} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this field HTML? Text only is OK here I believe

</Text>
</Button>
<Link href={`/courses/${course.id}`}>
Expand All @@ -50,7 +50,7 @@ export function CourseCard({ course }) {
</Button>
</Link>
<Button color={'success'} rounded flat size={'xs'}>
{course.difficulty}
<RenderField object={course} field="difficulty" isHtml={true} maxSize={200} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

</Button>
<Card.Divider />
<Collapse.Group>
Expand Down Expand Up @@ -89,6 +89,3 @@ export function CourseCard({ course }) {
</Card>
)
}



13 changes: 1 addition & 12 deletions components/Modal/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default function Modal({
onClose,
course,
lesson,
section,
submissionType,
submissionText,
submissionTitle,
Expand Down Expand Up @@ -50,21 +51,9 @@ export default function Modal({
}
}, [cohorts]);*/

const getSection = () => {
const section = Object.entries(course.sections)
.map((section) =>
section[1].map((item) => {
if (item.file.includes(lesson)) return section[0]
})
)
.flat()
.find(Boolean)
return section
}
const saveLessonSubmission = async (userSubmission, submissionId) => {
if (!userSubmission) return toast.error(t('messages.lesson_no_response'))
if (!submissionId) submissionId = uuid()
const section = getSection()
const content = {
type: submissionType,
value: userSubmission,
Expand Down
9 changes: 3 additions & 6 deletions components/RenderField/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@ import { useTranslation } from 'react-i18next'

export default function RenderField({ object, field, isHtml, maxSize }) {
const { i18n } = useTranslation()
let content
const defaultLanguage = 'en'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not have hardcoded default language to 'en'. This config should come from i18n definitions.

const language = i18n.resolvedLanguage || defaultLanguage

if (object?.metadata) {
content = object.metadata[i18n.resolvedLanguage || 'en']?.[field]
} else {
content = object?.[field]
}
let content = object?.metadata?.[language]?.[field] ?? object?.[field]

if (maxSize && content?.length > maxSize) {
content = `${content.substring(0, maxSize)}...`
Expand Down
50 changes: 26 additions & 24 deletions components/Tabs/index.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,47 @@
import React, { useState, useEffect } from 'react'
import useAuth from '../../hooks/useAuth'
import { checkSections, colorTab } from './tabFunctions'
import { useTranslation } from "react-i18next"
import { useTranslation } from 'react-i18next'

export default function Tabs({ course, lessonsSubmitted, cohort }) {
const { t } = useTranslation()
const { t, i18n } = useTranslation()
const language = i18n.resolvedLanguage

const isCourse = lessonsSubmitted || cohort

if (!isCourse) {
const sectionAnalytics = course?.analytics
return (
<div className="flex flex-col rounded-lg">
<div className="flex flex-row justify-center items-center">
{sectionAnalytics?.photoUrls?.slice(0, 3).map((source, index) => (
<img
key={source}
src={source}
alt="User avatar"
style={{
width: '50px',
height: '50px',
borderRadius: '50%',
marginLeft: index !== 0 ? '-15px' : '0',
position: 'relative',
zIndex: sectionAnalytics?.photoUrls.length - index,
}}
/>
))}
<div className="flex flex-row items-center justify-center">
{sectionAnalytics?.photoUrls?.slice(0, 3).map((source, index) => (
<img
key={source}
src={source}
alt="User avatar"
style={{
width: '50px',
height: '50px',
borderRadius: '50%',
marginLeft: index !== 0 ? '-15px' : '0',
position: 'relative',
zIndex: sectionAnalytics?.photoUrls.length - index,
}}
/>
))}
</div>
{sectionAnalytics?.students && (
<p style={{ textAlign: 'center', marginTop: '20px' }}>
{sectionAnalytics.students} entusiastas nesse grupo de estudos!
{sectionAnalytics.students} {t('peopleBuilding')}!
</p>
)}
</div>
)
)
}

if (!course?.sections) return null
const [activeTab, setActiveTab] = useState(Object.keys(course?.sections).sort())
const sections = course?.metadata?.[language]?.sections || course?.sections
if (!sections) return null
const [activeTab, setActiveTab] = useState(Object.keys(sections).sort())

const toggle = (tab) => {
if (activeTab !== tab) setActiveTab(tab)
Expand All @@ -48,7 +50,7 @@ export default function Tabs({ course, lessonsSubmitted, cohort }) {
return (
<div className="mt-6 mb-6 flex flex-col rounded-lg p-4 shadow-xl lg:mt-12">
<div className="flex flex-row justify-between maxsm:flex-col maxsm:text-center">
{Object.keys(course?.sections)
{Object.keys(sections)
.sort()
.map((section) => {
const sectionAnalytics = course.analytics?.find((item) => item.section === section)
Expand Down Expand Up @@ -94,4 +96,4 @@ export default function Tabs({ course, lessonsSubmitted, cohort }) {
</div>
</div>
)
}
}
29 changes: 6 additions & 23 deletions lib/course.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,6 @@ export async function getPage(course, section, lesson, language) {
return text
}

async function getCourseLessons(course) {
if (course == undefined || course.sections == undefined) return []
let lessons = Object.keys(course.sections)
.map((section) => {
return course.sections[section].map((lesson) => {
lesson = lesson.file
return {
section,
lesson,
}
})
})
.flat()

return lessons
}

export async function getStudyGroup(groupSlug) {
const colRef = collection(db, 'study_groups')
const q = query(colRef, where('slug', '==', groupSlug))
Expand All @@ -53,8 +36,7 @@ export async function getCourse(course_id) {
const docRef = doc(db, 'courses', course_id)
const courseDoc = await getDoc(docRef)
const course = { id: course_id, ...courseDoc.data() }
const lessons = await getCourseLessons(course)
return { ...course, lessons }
return { ...course }
}

export const defaultCourse = {
Expand All @@ -79,11 +61,12 @@ export async function getHomeCourse() {
}

export function getFieldContent(object, field, i18n) {
let content
const defaultLanguage = 'en'
const language = i18n.resolvedLanguage || defaultLanguage

let content = object?.metadata?.[language]?.[field]

if (object?.metadata) {
content = object.metadata[i18n.resolvedLanguage || 'en']?.[field]
} else {
if (content === undefined) {
content = object?.[field]
}

Expand Down
137 changes: 71 additions & 66 deletions pages/courses/[id].js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Loading from '../../components/Loading'
import { dateFormat } from '../../lib/dateFormat'
import { useTranslation } from 'react-i18next'
import RenderField from '../../components/RenderField'
import { toast } from 'react-toastify'

function Course({ course, currentDate }) {
if (!course.active) return <NotFound />
Expand All @@ -35,14 +36,20 @@ function Course({ course, currentDate }) {
const { t, i18n } = useTranslation()
const language = i18n.resolvedLanguage

useEffect(() => {
if (course?.metadata && !course.metadata.hasOwnProperty(language)) {
toast.error(t('messages.language_not_available'))
}
}, [language])

let counter = 0
useEffect(async () => {
setCohorts(await getAllCohorts())
}, [])
useEffect(async () => {
let lessonsSubmitted_ = await getLessonsSubmissions(user?.uid, cohort?.id)
setLessonsSubmitted(lessonsSubmitted_)
}, [user])
}, [user, cohort])
useEffect(async () => {
if (cohorts) {
setCohort(getCurrentCohort(user, cohorts, course, currentDate))
Expand Down Expand Up @@ -88,7 +95,6 @@ function Course({ course, currentDate }) {
(userCohort) => userCohort.course_id == course.id && userCohort.cohort_id == cohort?.id
)
}

const userSubmissions = (lesson) => {
return lessonsSubmitted.some((submittedLesson) => submittedLesson.lesson === lesson.file)
}
Expand Down Expand Up @@ -166,6 +172,68 @@ function Course({ course, currentDate }) {
setKickoffEndDate(cohortKickoffEndDateGTMPattern)
}, [cohort])

function renderSections(course, language) {
const sections = course?.metadata?.[language]?.sections || course?.sections
if (!sections) return null

return Object.keys(sections)
.sort()
.map((section) => {
const sectionNumber = section.replace('Section_', '')
return (
<div key={section}>
<span id={section} className="mb-4 font-bold">
{t('section') + ' ' + sectionNumber}
</span>
<ul className="mt-4 mb-4 flex list-none flex-col">
{sections[section]
.sort((a, b) => a.title.localeCompare(b.title))
.map((lesson) => (
<li key={lesson.title} className="mb-4 items-center rounded py-2 px-4">
<div className="flex items-center">
<div className="relative mr-2 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full">
<input
disabled
type="radio"
name="radio"
className="checkbox absolute mt-1 h-full w-full appearance-none rounded-full border"
/>
<div className="check-icon z-1 mb-1 h-full w-full rounded-full">
{userSubmissions(lesson) ? (
<Image
className="h-full w-full"
width={48}
height={48}
src={'/assets/img/checked-radio-button.svg'}
alt={lesson.title}
/>
) : (
<Image
className="h-full w-full"
width={48}
height={48}
src={'/assets/img/radio-button.svg'}
alt={lesson.title}
/>
)}
</div>
</div>
<Link
href={`/courses/${course.id}/${section}/${lesson.file}?lang=${language}`}
>
<a id="access-lesson">
<p className="m-0 p-0">{lesson.title}</p>
</a>
</Link>
</div>
</li>
))}
</ul>
</div>
)
})
}

return (
<>
<Head>
Expand Down Expand Up @@ -353,70 +421,7 @@ function Course({ course, currentDate }) {
<Tabs course={course} lessonsSubmitted={lessonsSubmitted} cohort={cohort} />

<div className="z-10 my-8 w-full rounded-lg p-7">
{course?.sections &&
Object.keys(course?.sections)
.sort()
.map((section) => {
const sectionNumber = section.replace('Section_', '')
return (
<div key={section}>
<span id={section} className="mb-4 font-bold">
{t('section') + ' ' + sectionNumber}
</span>
<ul className="mt-4 mb-4 flex list-none flex-col ">
{course?.sections[section]
.map((lesson) => {
return (
<li
key={lesson.title}
className="mb-4 items-center rounded py-2 px-4"
>
<div className="flex items-center ">
<div className="relative mr-2 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full">
<input
disabled
type="radio"
name="radio"
className="checkbox absolute mt-1 h-full w-full appearance-none rounded-full border"
/>
<div className="check-icon z-1 mb-1 h-full w-full rounded-full">
{userSubmissions(lesson) ? (
<Image
className="h-full w-full "
width={48}
height={48}
src={'/assets/img/checked-radio-button.svg'}
alt={lesson.title}
/>
) : (
<Image
className="h-full w-full"
width={48}
height={48}
src={'/assets/img/radio-button.svg'}
alt={lesson.title}
/>
)}
</div>
</div>
<div className={counter > 1 ? 'pointer-events-none' : ''}>
<Link
href={`/courses/${course.id}/${section}/${lesson.file}?lang=${language}`}
>
<a id="access-lesson">
<p className="m-0 p-0">{lesson.title}</p>
</a>
</Link>
</div>
</div>
</li>
)
})
.sort((a, b) => a - b)}
</ul>
</div>
)
})}
{renderSections(course, language)}
</div>
</div>
<div className="mb-3 flex pt-6">
Expand Down
Loading
Loading