Desenvolvedores
src_
components_
CourseCard.jsx

📌 Visão Geral

O CourseCard é um componente React que exibe um cartão com informações sobre um curso. Ele utiliza useState para controlar a expansão da descrição e exibe:

  • Imagem do curso 🖼️
  • Título e descrição 📖
  • Tags associadas 🏷️
  • Criador do curso 👤
  • Botão para acessar o curso 🎯

🏗️ Estrutura do Componente

O CourseCard recebe um objeto course como propriedade (prop) e utiliza vários subcomponentes da biblioteca shadcn/ui para estruturar o layout.

Vamos analisar o código em partes.


🖼️ Exibição da Imagem

A primeira seção do componente exibe a imagem do curso dentro de um container responsivo usando o AspectRatio.

<AspectRatio ratio={16 / 9}>
  <Image
    src={course.fotoBanner?.url}
    alt={course.fotoBanner?.alt}
    className="w-full h-full object-cover rounded-tl-lg rounded-tr-lg"
    width={500}
    height={300}
  />
</AspectRatio>

📌 Explicação:

  • AspectRatio ratio={16 / 9} garante que a imagem sempre mantenha a proporção de 16:9.
  • Image carrega a foto do curso (course.fotoBanner.url).
  • alt={course.fotoBanner?.alt} adiciona um texto alternativo para acessibilidade.
  • As classes Tailwind w-full h-full object-cover garantem que a imagem preencha o container corretamente.

📝 Título e Descrição

Depois da imagem, exibimos o título do curso e uma descrição que pode ser expandida.

<CardHeader className="px-4 pt-0 pb-0 space-y-4">
  <CardTitle className="mt-4 mb-2">{course.nomeCurso}</CardTitle>
  <CardDescription className="mt-4 mb-2">
    {course.descricao.length > 200
      ? expanded
        ? course.descricao
        : `${course.descricao.substring(0, 200)}...`
      : course.descricao}
    {course.descricao.length > 200 && (
      <span onClick={toggleDescription} className="block mt-2 text-blue-500 cursor-pointer">
        {expanded ? 'Veja menos' : 'Veja mais'}
      </span>
    )}
  </CardDescription>
</CardHeader>

📌 Explicação:

  • CardTitle exibe o nome do curso 📌
  • CardDescription exibe a descrição, que pode ser cortada em 200 caracteres e expandida ao clicar em "Veja mais" 📖
  • useState controla a expansão do texto 🛠️

🔀 Alternância entre Expandir e Recolher

Para controlar se a descrição está expandida ou recolhida, usamos useState e a função toggleDescription:

const [expanded, setExpanded] = useState(false);
 
const toggleDescription = () => setExpanded(!expanded);

🏷️ Exibição das Tags

Caso o curso tenha tags associadas, elas são exibidas logo abaixo da descrição.

<div className="mb-4 flex flex-wrap gap-2">
  {tags.map((tag) => (
    <TagBadge key={tag.id} tag={tag.nome} />
  ))}
</div>

📌 Explicação:

  • tags.map(...) percorre todas as tags associadas ao curso.
  • Cada tag é renderizada com o componente TagBadge.
  • As classes Tailwind flex-wrap gap-2 garantem um layout responsivo.

👤 Exibição do Criador do Curso

Os dados do criador do curso são mostrados dentro de um HoverCard, que exibe mais informações ao passar o mouse.

<HoverCard>
  <HoverCardTrigger asChild>
    <div className="flex items-center space-x-2 sm:whitespace-nowrap">
      <CollaboratorInfo
        collaborator={{
          photo: createdBy.foto?.url,
          full_name: createdBy.nomeCompleto,
        }}
      />
    </div>
  </HoverCardTrigger>
  <HoverCardContent className="w-80">
    <div className="flex justify-between space-x-4">
      <Avatar>
        <AvatarImage src={createdBy.foto?.url} />
        <AvatarFallback>{createdBy.nomeCompleto}</AvatarFallback>
      </Avatar>
      <div className="space-y-1">
        <h4 className="text-sm font-semibold">{createdBy.nomeCompleto}</h4>
        <p className="text-sm">{createdBy.descricao}</p>
        <div className="flex items-center pt-2">
          <CalendarDays className="mr-2 h-4 w-4 opacity-70" />{' '}
          <span className="text-xs text-muted-foreground">
            Inscreveu-se em {new Date(createdBy.createdAt).toLocaleDateString()}
          </span>
        </div>
      </div>
    </div>
  </HoverCardContent>
</HoverCard>

📌 Explicação:

  • O HoverCard exibe detalhes sobre o criador ao passar o mouse.
  • O componente CollaboratorInfo exibe a foto e nome do criador.
  • Avatar e AvatarImage mostram a foto do criador, com um fallback caso a imagem não carregue.

🎯 Botão "Começar"

Para permitir que o usuário acesse o curso, um botão com link é adicionado.

<Link href={`/cursos/${course.slug}`} passHref>
  <Button className="w-full sm:w-auto mt-4 sm:mt-0">Começar</Button>
</Link>

📌 Explicação:

  • href={/cursos/${course.slug}} cria um link dinâmico para a página do curso.
  • Button estiliza o botão.
  • w-full sm:w-auto garante que o botão seja responsivo.

🔗 Código Completo

import React, { useState } from 'react'
import Link from 'next/link'
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/hover-card'
import { Button } from '@/components/ui/button'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { AspectRatio } from '@/components/ui/aspect-ratio'
import TagBadge from '@/components/TagBadge'
import CollaboratorInfo from '@/components/CollaboratorInfo'
import { CalendarDays } from 'lucide-react'
import Image from 'next/image'
 
const CourseCard = ({ course }) => {
  const [expanded, setExpanded] = useState(false)
 
  const toggleDescription = () => setExpanded(!expanded)
 
  const { createdBy, tags = [] } = course
 
  return (
    <Card>
      <AspectRatio ratio={16 / 9}>
        <Image src={course.fotoBanner?.url} alt={course.fotoBanner?.alt} width={500} height={300} />
      </AspectRatio>
      <CardHeader>
        <CardTitle>{course.nomeCurso}</CardTitle>
        <CardDescription>
          {course.descricao.length > 200 ? expanded ? course.descricao : `${course.descricao.substring(0, 200)}...` : course.descricao}
          {course.descricao.length > 200 && (
            <span onClick={toggleDescription} className="text-blue-500 cursor-pointer">
              {expanded ? 'Veja menos' : 'Veja mais'}
            </span>
          )}
        </CardDescription>
      </CardHeader>
      <CardContent>
        <Link href={`/cursos/${course.slug}`} passHref>
          <Button>Começar</Button>
        </Link>
      </CardContent>
    </Card>
  )
}
 
export default CourseCard