Nós iremos agora fazer a manipulação da página de curso. Nós iremos começar retornando as informações do curso, depois iremos partir para episódios.
Iremos começar então criando uma seção exclusiva para a imagem do curso. Dentro dessa div nós iremos colocar o nosso header.
<div style={{}}>
<HeaderAuth />
</div>
Nós iremos agora colocar um background e também uma altura mínima nessa nossa div, onde teremos a imagem, parecido com o que temos na “homeAuth”
<main>
<div
style={{
backgroundImage: `linear-gradient(to bottom, #6666661a, #151515),
url(${process.env.NEXT_PUBLIC_BASEURL}/${course?.thumbnailUrl})`,
backgroundSize: "cover",
backgroundPosition: "center",
minHeight: "450px",
}}
>
<HeaderAuth />
</div>
</main>
Agora, nós faremos um container onde vai ficar todas as informações do curso.
<Container className={styles.courseInfo}>
<p className={styles.courseTitle}>{course?.name}</p>
<p className={styles.courseDescription}>{course?.synopsis}</p>
<Button outline className={styles.courseBtn}>
ASSISTIR AGORA!
<img
src="/buttonPlay.svg"
alt="buttonImg"
className={styles.buttonImg}
/>
</Button>
</Container>
Nós vamos também colocar os ícones de like e de favorito abaixo do botão.
<Container className={styles.courseInfo}>
<p className={styles.courseTitle}>{course?.name}</p>
<p className={styles.courseDescription}>{course?.synopsis}</p>
<Button outline className={styles.courseBtn}>
ASSISTIR AGORA!
<img
src="/buttonPlay.svg"
alt="buttonImg"
className={styles.buttonImg}
/>
</Button>
<div className={styles.interactions}>
<img
src="/course/iconLike.svg"
alt="likeImage"
className={styles.interactionImages}
onClick={handleLikeCourse}
/>
<img
onClick={handleFavCourse}
src="/course/iconAddFav.svg"
alt="addFav"
className={styles.interactionImages}
/>
</div>
</Container>
Podemos ver que temos retorno visual na tela, vamos então começar a manipular ele. Nós iremos colocar um pequeno margintop negativo para os nossos textos ficarem em cima da transição do degradê.
@import "../styles/colors.module.scss";
.courseInfo {
margin-top: -120px;
}
Agora, podemos manipular os textos e os botões de interação.
.courseTitle {
font-size: 40px;
font-weight: bold;
padding-bottom: 25px;
width: 700px;
}
.courseDescription {
color: $lightGray;
width: 650px;
padding-bottom: 25px;
}
.courseBtn {
display: flex;
align-items: center;
gap: 15px;
font-weight: bold;
font-size: 14px;
padding: 10px 35px;
color: white;
border-color: $lightRed;
&:hover {
border-color: $darkRed;
color: $lightGray;
background-color: transparent;
}
}
.buttonImg {
width: 10px;
margin: 0;
padding: 0;
}
.interactions {
display: flex;
gap: 20px;
margin: 26px 0px;
}
.interactionImages {
width: 36px;
cursor: pointer;
}
Vamos agora criar dois states que vão começar como falso. Vamos criar um para like e outro para favorito.
const [liked, setLiked] = useState(false);
const [favorited, setFavorited] = useState(false);
E nós vamos colocar dentro de “getCouse” os sets para esses states. Nós temos duas coisas dentro do objeto do curso, que é o “liked” e “favorited”, sendo boleanos. Ou seja, vamos saber se o usuário já deu like ou favoritou esse curso.
const getCourse = async function () {
if (typeof id !== "string") return;
const res = await courseService.getEpisodes(id);
if (res.status === 200) {
setCourse(res.data);
setLiked(res.data.liked);
setFavorited(res.data.favorited);
}
};
Agora, quando a página de curso for carregada, nós vmaos conseguir verificar se o curso está com like e favoritado. Com isso, nós vamos fazer uma renderização condicional para o nosso curso. Começando pelo like, vamos verificar se o like está como falso, se estiver, vamos mostrar a imagem do coração vazio, se não estiver, o coração cheio.
{liked === false ? (
<img
src="/course/iconLike.svg"
alt="likeImage"
className={styles.interactionImages}
onClick={handleLikeCourse}
/>
) : (
<img
src="/course/iconLiked.svg"
alt="likedImage"
className={styles.interactionImages}
onClick={handleLikeCourse}
/>
)}
Nós vamos então fazer a mesma coisa com favoritos. Se for falso, iremos renderizar a normal.
{favorited === false ? (
<img
onClick={handleFavCourse}
src="/course/iconAddFav.svg"
alt="addFav"
className={styles.interactionImages}
/>
) : (
<img
onClick={handleFavCourse}
src="/course/iconFavorited.svg"
alt="favorited"
className={styles.interactionImages}
/>
)}
Agora nós temos temos as imagens prontas. Precisamos apenas criar os handles para transformar o like e o favorito do curso. Nós vamos primeiro criar o handle de like. Vamos fazer uma função assíncrona, que vai ter uma verificação. Se o esado estiver como like, vamos remover o like e dar um set para ele de falso. Vamos então botar um else fazendo o inverso.
const handleLikeCourse = async () => {
if (liked === true) {
await courseService.removeLike(id);
setLiked(false);
} else {
await courseService.like(id);
setLiked(true);
}
};
Podemos até testar e ver que está funcionando. Vamos fazer a mesma coisa com favorito. Do mesmo estilo.
const handleFavCourse = async () => {
if (favorited === true) {
await courseService.removeFav(id);
setFavorited(false);
} else {
await courseService.addToFav(id);
setFavorited(true);
}
};
Nós vamos poder testar os dois agora. Até mesmo sair do curso, ir no local dos slides de favortos e vermos que estamos tendo retorno dos cursos.
Agora, nós iremos começar colocando a quantidade de episódios existentes nesse curso. Nós estaremos colocar quando episódios e o lenght desses episódios são opcionais, por que um curso pode não ter episódios, então ambos serão opcionais.
<Container className={styles.episodeInfo}>
<p className={styles.episodeDivision}>EPISÓDIOS</p>
<p className={styles.episodeLength}>
{course?.episodes?.length} episódios
</p>
</Container>
Conseguimos ver que ele trouxe um retorno da quantidade de episódios no curso. Agora, nós faremos um map para retornar o título do episódio por enquanto, faremos ser opcional também.
<Container className={styles.episodeInfo}>
<p className={styles.episodeDivision}>EPISÓDIOS</p>
<p className={styles.episodeLength}>
{course?.episodes?length} episódios
</p>
{course?.episodes?.map((episode) => (
<>
<p>{episode.name}</p>
</>
))}
</Container>
Nós vamos fazer então um component para conter a lista de episódios. Podemos então ir na pasta “components” criar um arquivo chamado “episodesList” e criar dentro dele index e scss.
import styles from "./styles.module.scss";
const EpisodeList = function () {
return <></>;
};
export default EpisodeList;
Nós vamos agora fazer algo muito parecido com o que fizemos so searchCard, nós iremos fazer aqui uma interface props para colocarmos o episódio que vai vir do map.
import styles from "./styles.module.scss";
import { EpisodeType } from "../../services/courseService";
interface props {
episode: EpisodeType;
}
const EpisodeList = function ({ episode }: props) {
return <></>;
};
export default EpisodeList;
Nós iremos agora colocar 2 divisões, uma ficará com o número do episódio e com o tempo dele e outro vai ficar com título e descrição
<>
<div className={styles.episodeCard}>
<div className={styles.episodeOrderTime}>
<p className={styles.episodeOrder}>Episódio Nº {episode.order}</p>
<p className={styles.episodeTime}>{episode.secondsLong}</p>
</div>
<div className={styles.episodeTitleDescription}>
<p className={styles.episodeTitle}>{episode.name}</p>
<p className={styles.episodeDescription}>{episode.synopsis}</p>
</div>
</div>
</>