📚 Documentação do Arquivo page.js
- [slugs]
🚀 Funcionalidades
- Carregamento Dinâmico: Carrega e exibe o curso com base no
slug
da URL. - Exibição Interativa: Apresenta informações como nome, descrição, níveis de habilidade, horas de duração, módulos e pré-requisitos.
- Interação com Módulos: Permite expandir ou contrair módulos para ver suas respectivas aulas.
🧑💻 Explicação do Código
Abaixo estão as principais partes do código que implementam a funcionalidade descrita acima.
'use client'
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { useCourses } from '@/components/context/CoursesContext'
import RichTextContent from '@/components/RichTextContent'
import Link from 'next/link'
import {
FaCode,
FaProjectDiagram,
FaInfoCircle,
FaAward,
FaFolder,
FaClock,
FaChartBar,
FaExclamationCircle,
FaChevronDown,
FaChevronUp,
FaBook,
} from 'react-icons/fa'
Aqui, importamos os hooks useState
e useEffect
, componentes e ícones necessários para o funcionamento da interface.
🔑 Estado e Efeitos
Definimos dois estados importantes para controlar os dados e o estado de visualização dos módulos.
export default function CoursePage({ params }) {
const { slug } = params // Obtém o slug do curso da URL
const { courses } = useCourses() // Acessa o contexto de cursos
const [course, setCourse] = useState(null) // Estado para armazenar o curso encontrado
const [openModuleIndex, setOpenModuleIndex] = useState(null) // Estado para controlar qual módulo está aberto
useEffect(() => {
if (slug) {
const foundCourse = courses.find((course) => course.slug === slug) // Busca o curso pelo slug
setCourse(foundCourse) // Atualiza o estado com o curso encontrado
}
}, [slug, courses]) // Re-executa quando o slug ou a lista de cursos muda
Usamos o useEffect
para buscar o curso baseado no slug
da URL. O estado course
armazena o curso encontrado, enquanto openModuleIndex
controla a expansão dos módulos.
📚 Exibição do Curso e Seus Detalhes
A seguir, temos o código que exibe as informações do curso.
if (!course) {
return <div>Curso não encontrado ou carregando...</div> // Mensagem de carregamento ou erro
}
const handleToggleModule = (index) => {
// Função para alternar a visibilidade de um módulo
setOpenModuleIndex(openModuleIndex === index ? null : index)
}
const totalModulos = Array.isArray(course.modulos) ? course.modulos.length : 0 // Total de módulos
const totalAulas = Array.isArray(course.modulos)
? course.modulos.reduce(
(acc, modulo) => acc + (Array.isArray(modulo.aulas) ? modulo.aulas.length : 0),
0,
)
: 0 // Total de aulas
Aqui, verificamos se o curso foi encontrado e calculamos o total de módulos e aulas, além de permitir a alternância entre expandir ou contrair módulos.
🖥️ Renderização e Interface de Usuário
Agora, vamos para a renderização do conteúdo do curso, como nome, descrição, módulos e aulas.
return (
<div className="max-w-4xl mx-auto p-4 space-y-8">
<section className="p-6 border rounded-md">
<h2 className="text-sm font-semibold text-muted-foreground">Curso</h2>
<h1 className="text-3xl font-bold">{course.nomeCurso}</h1>
<p className="mt-2 text-muted-foreground">{course.descricao}</p>
<Button className="mt-4 bg-primary text-primary-foreground">Iniciar</Button>
<div className="mt-6 p-4 border rounded-md">
<h3 className="text-lg font-semibold">Este curso inclui</h3>
<ul className="mt-2 space-y-2">
<li className="flex items-center">
<FaCode className="mr-2 h-5 w-5" />
Assistência de professores qualificados
</li>
<li className="flex items-center">
<FaProjectDiagram className="mr-2 h-5 w-5" />
Projetos para aplicar novas habilidades
</li>
<li className="flex items-center">
<FaInfoCircle className="mr-2 h-5 w-5" />
Questionários para testar seu conhecimento
</li>
<li className="flex items-center">
<FaAward className="mr-2 h-5 w-5" />
Um certificado de conclusão
</li>
</ul>
</div>
</section>
Esse trecho exibe as informações principais do curso, como nome, descrição, e botões de interação. Também destaca os recursos adicionais do curso, como assistência de professores e projetos.
🎓 Exibição de Módulos e Aulas
Agora, renderizamos a lista de módulos com a possibilidade de expandir suas aulas.
<section className="p-4 border rounded-md flex flex-wrap justify-between items-center">
<div className="flex items-center space-x-4 w-full sm:w-auto mb-4 sm:mb-0">
<div className="flex items-center space-x-2">
<FaChartBar className="w-6 h-6 text-muted-foreground mr-2" />
<div>
<p className="text-sm">Nível de habilidade</p>
<p className="text-lg font-semibold">{course.nivel}</p>
</div>
</div>
</div>
</section>
<section className="p-4 border rounded-md">
<h2 className="text-xl font-bold">Conteúdo Completo</h2>
<p>{`${totalModulos} módulos - ${totalAulas} aulas`}</p>
<div className="mt-4 space-y-4">
{course && course.modulos && course.modulos.length > 0 ? (
course.modulos.map((modulo, index) => (
<div key={index} className="p-4 border rounded-md">
<div className="flex justify-between items-center">
<div className="flex items-center space-x-2">
<span className="text-lg font-bold">{index + 1}</span>
<h3 className="text-lg font-semibold">{modulo.modulo.nomeModulo}</h3>
</div>
<button onClick={() => handleToggleModule(index)}>
{openModuleIndex === index ? (
<FaChevronUp className="h-5 w-5" />
) : (
<FaChevronDown className="h-5 w-5" />
)}
</button>
</div>
{openModuleIndex === index && (
<ul className="mt-4 space-y-2">
{modulo.modulo.aulas.map((aula, aulaIndex) => (
<li key={aulaIndex} className="flex items-center space-x-2 ml-3">
<FaBook className="h-5 w-5 text-muted-foreground" />
<Link
href={`/cursos/${course.slug}/${index}/${aula.aula.nomeAula}`}
className="text-primary hover:underline"
>
{aula.aula.nomeAula}
</Link>
</li>
))}
</ul>
)}
</div>
))
) : (
<p>Nenhum módulo encontrado</p>
)}
</div>
</section>
</div>
)
}
Aqui, o componente exibe todos os módulos e aulas do curso. A interação é feita com ícones de expandir e contrair, permitindo ao usuário visualizar as aulas conforme necessário.
📋 Código Completo
'use client'
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { useCourses } from '@/components/context/CoursesContext'
import RichTextContent from '@/components/RichTextContent'
import Link from 'next/link'
import {
FaCode,
FaProjectDiagram,
FaInfoCircle,
FaAward,
FaFolder,
FaClock,
FaChartBar,
FaExclamationCircle,
FaChevronDown,
FaChevronUp,
FaBook,
} from 'react-icons/fa'
export default function CoursePage({ params }) {
const { slug } = params // Obtém o slug do curso da URL
const { courses } = useCourses() // Acessa o contexto de cursos
const [course, setCourse] = useState(null) // Estado para armazenar o curso encontrado
const [openModuleIndex, setOpenModuleIndex] = useState(null) // Estado para controlar qual módulo está aberto
useEffect(() => {
if (slug) {
const foundCourse = courses.find((course) => course.slug === slug) // Busca o curso pelo slug
setCourse(foundCourse) // Atualiza o estado com o curso encontrado
}
}, [slug, courses]) // Re-executa quando o slug ou a lista de cursos muda
if (!course) {
return <div>Curso não encontrado ou carregando...</div> // Mensagem de carregamento ou erro
}
const handleToggleModule = (index) => {
// Função para alternar a visibilidade de um módulo
setOpenModuleIndex(openModuleIndex === index ? null : index)
}
const totalModulos = Array.isArray(course.modulos) ? course.modulos.length : 0 // Total de módulos
const totalAulas = Array.isArray(course.modulos)
? course.modulos.reduce(
(acc, modulo) => acc + (Array.isArray(modulo.aulas) ? modulo.aulas.length : 0),
0,
)
: 0 // Total de aulas
return (
<div className="max-w-4xl mx-auto p-4 space-y-8">
<section className="p-6 border rounded-md">
<h2 className="text-sm font-semibold text-muted-foreground">Curso</h2>
<h1 className="text-3xl font-bold">{course.nomeCurso}</h1>
<p className="mt-2 text-muted-foreground">{course.descricao}</p>
<Button className="mt-4 bg-primary text-primary-foreground">Iniciar</Button>
<div
className="mt-6 p-4 border rounded-md">
<h3 className="text-lg font-semibold">Este curso inclui</h3>
<ul className="mt-2 space-y-2">
<li className="flex items-center">
<FaCode className="mr-2 h-5 w-5" />
Assistência de professores qualificados
</li>
<li className="flex items-center">
<FaProjectDiagram className="mr-2 h-5 w-5" />
Projetos para aplicar novas habilidades
</li>
<li className="flex items-center">
<FaInfoCircle className="mr-2 h-5 w-5" />
Questionários para testar seu conhecimento
</li>
<li className="flex items-center">
<FaAward className="mr-2 h-5 w-5" />
Um certificado de conclusão
</li>
</ul>
</div>
</section>
<section className="p-4 border rounded-md flex flex-wrap justify-between items-center">
<div className="flex items-center space-x-4 w-full sm:w-auto mb-4 sm:mb-0">
<div className="flex items-center space-x-2">
<FaChartBar className="w-6 h-6 text-muted-foreground mr-2" />
<div>
<p className="text-sm">Nível de habilidade</p>
<p className="text-lg font-semibold">{course.nivel}</p>
</div>
</div>
</div>
</section>
<section className="p-4 border rounded-md">
<h2 className="text-xl font-bold">Conteúdo Completo</h2>
<p>{`${totalModulos} módulos - ${totalAulas} aulas`}</p>
<div className="mt-4 space-y-4">
{course && course.modulos && course.modulos.length > 0 ? (
course.modulos.map((modulo, index) => (
<div key={index} className="p-4 border rounded-md">
<div className="flex justify-between items-center">
<div className="flex items-center space-x-2">
<span className="text-lg font-bold">{index + 1}</span>
<h3 className="text-lg font-semibold">{modulo.modulo.nomeModulo}</h3>
</div>
<button onClick={() => handleToggleModule(index)}>
{openModuleIndex === index ? (
<FaChevronUp className="h-5 w-5" />
) : (
<FaChevronDown className="h-5 w-5" />
)}
</button>
</div>
{openModuleIndex === index && (
<ul className="mt-4 space-y-2">
{modulo.modulo.aulas.map((aula, aulaIndex) => (
<li key={aulaIndex} className="flex items-center space-x-2 ml-3">
<FaBook className="h-5 w-5 text-muted-foreground" />
<Link
href={`/cursos/${course.slug}/${index}/${aula.aula.nomeAula}`}
className="text-primary hover:underline"
>
{aula.aula.nomeAula}
</Link>
</li>
))}
</ul>
)}
</div>
))
) : (
<p>Nenhum módulo encontrado</p>
)}
</div>
</section>
</div>
)
}