Latest changes
This commit is contained in:
parent
c66526241c
commit
18c6684fb2
10 changed files with 265 additions and 29 deletions
|
@ -2,12 +2,14 @@ import { Routes, Route } from "react-router-dom"
|
|||
import "./App.css"
|
||||
import Home from "pages/Home"
|
||||
import Movie from "pages/Movie"
|
||||
import Actor from "pages/Actor"
|
||||
|
||||
const App = () => (
|
||||
<div className="App">
|
||||
<Routes>
|
||||
<Route index element={<Home />} />
|
||||
<Route path="/movie/:movieId" element={<Movie />} />
|
||||
<Route path="/actor/:actorId" element={<Actor />} />
|
||||
<Route path="*" element={<h1>404 - Not Found</h1>} />
|
||||
</Routes>
|
||||
</div>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import Card from "components/Card"
|
||||
import styles from "styles/CardList.module.css"
|
||||
import Card from "components/Card";
|
||||
import styles from "styles/CardList.module.css";
|
||||
|
||||
const CardList = ({ movies }) => (
|
||||
<div className={styles.CardList}>
|
||||
{movies.map((movie) => (
|
||||
<Card key={movie.id} movie={movie} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
<div className={styles.CardList}>
|
||||
{movies?.map((movie) => (
|
||||
<Card key={movie.id} movie={movie} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default CardList
|
||||
export default CardList;
|
||||
|
|
125
src/pages/Actor.jsx
Normal file
125
src/pages/Actor.jsx
Normal file
|
@ -0,0 +1,125 @@
|
|||
import { useEffect, useState } from "react"
|
||||
import config from "config"
|
||||
import styles from "styles/Actor.module.css"
|
||||
import { Link, useParams } from "react-router-dom"
|
||||
import { GENRES } from "../constants"
|
||||
import Card from "components/Card"
|
||||
|
||||
const Actor = () => {
|
||||
const { actorId } = useParams()
|
||||
const [actor, setActor] = useState()
|
||||
const [images, setImages] = useState()
|
||||
const [collapsed, setCollapsed] = useState(true)
|
||||
const [credits, setCredits] = useState()
|
||||
|
||||
useEffect(() => {
|
||||
const run = async () => {
|
||||
const response = await fetch(
|
||||
`https://api.themoviedb.org/3/person/${actorId}?api_key=${config.apiKey}`
|
||||
)
|
||||
const data = await response.json()
|
||||
setActor(data)
|
||||
}
|
||||
run()
|
||||
}, [actorId])
|
||||
|
||||
useEffect(() => {
|
||||
const run = async () => {
|
||||
const response = await fetch(
|
||||
`https://api.themoviedb.org/3/person/${actorId}/images?api_key=${config.apiKey}`
|
||||
)
|
||||
const data = await response.json()
|
||||
setImages(data)
|
||||
}
|
||||
run()
|
||||
}, [actorId])
|
||||
|
||||
useEffect(() => {
|
||||
const run = async () => {
|
||||
const response = await fetch(
|
||||
`https://api.themoviedb.org/3/person/${actorId}/movie_credits?api_key=${config.apiKey}`
|
||||
)
|
||||
const data = await response.json()
|
||||
setCredits(
|
||||
data.cast.map((movie) => ({
|
||||
id: movie.id,
|
||||
overview: movie.overview,
|
||||
adult: movie.adult,
|
||||
posterUrl: `https://image.tmdb.org/t/p/w342${movie.poster_path}`,
|
||||
backdropUrl: `https://image.tmdb.org/t/p/original${movie.backdrop_path}`,
|
||||
genres: movie.genre_ids.map((genreId) =>
|
||||
GENRES.find((genre) => genre.id == genreId)
|
||||
),
|
||||
title: movie.title,
|
||||
releaseDate: movie.release_date,
|
||||
year: movie.release_date.split("-")[0],
|
||||
averageVote: movie.vote_average,
|
||||
voteCount: movie.vote_count,
|
||||
popularity: movie.popularity,
|
||||
}))
|
||||
)
|
||||
}
|
||||
run()
|
||||
}, [actorId])
|
||||
|
||||
return (
|
||||
<div className={styles.Container}>
|
||||
<h1>{actor?.name}</h1>
|
||||
<div className={styles.Section}>
|
||||
<h2>Biography</h2>
|
||||
<div className={styles.Description}>
|
||||
{actor?.biography ? (
|
||||
<a
|
||||
href="#"
|
||||
className={styles.Collapse}
|
||||
onClick={() =>
|
||||
setCollapsed((collapsed) => !collapsed)
|
||||
}
|
||||
>
|
||||
<p className={collapsed ? styles.Collapsed : ""}>
|
||||
{actor.biography}
|
||||
</p>
|
||||
|
||||
<p className={styles.ReadMore}>
|
||||
{collapsed ? "Read More" : "Read Less"}
|
||||
</p>
|
||||
|
||||
<p>{collapsed}</p>
|
||||
</a>
|
||||
) : (
|
||||
<p>No biography available.</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.Section}>
|
||||
<h2>Images</h2>
|
||||
<div className={styles.Images}>
|
||||
{images?.profiles.map((image) => (
|
||||
<img
|
||||
key={image.file_path}
|
||||
className={styles.Image}
|
||||
src={`https://image.tmdb.org/t/p/w300${image.file_path}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.Section}>
|
||||
<h2>Movies</h2>
|
||||
<div className={styles.Credits}>
|
||||
{credits?.map((movie) => (
|
||||
<Link
|
||||
to={`/movie/${movie.id}`}
|
||||
key={movie.id}
|
||||
className={styles.Movie}
|
||||
>
|
||||
<img src={movie.posterUrl} />
|
||||
<span>{movie.title}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Actor
|
|
@ -1,10 +1,10 @@
|
|||
import CardList from "components/CardList"
|
||||
import TopBar from "components/TopBar"
|
||||
import useDebounce from "hooks/useDebounce"
|
||||
import { useEffect, useState } from "react"
|
||||
import styles from "styles/Home.module.css"
|
||||
import config from "config"
|
||||
import { GENRES } from "../constants"
|
||||
import Card from "components/Card"
|
||||
|
||||
const Home = () => {
|
||||
const [movies, setMovies] = useState([])
|
||||
|
@ -48,7 +48,11 @@ const Home = () => {
|
|||
return (
|
||||
<div className={styles.Container}>
|
||||
<TopBar search={search} setSearch={setSearch} />
|
||||
<CardList movies={movies} />
|
||||
<div className={styles.CardList}>
|
||||
{movies?.map((movie) => (
|
||||
<Card key={movie.id} movie={movie} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect, useState } from "react"
|
||||
import { useParams } from "react-router-dom"
|
||||
import { Link, useParams } from "react-router-dom"
|
||||
import styles from "styles/Movie.module.css"
|
||||
import config from "config"
|
||||
|
||||
|
@ -66,7 +66,9 @@ const Movie = () => {
|
|||
<p>{genre}</p>
|
||||
))}
|
||||
<p className={styles.Average}>
|
||||
{movie.averageVote}
|
||||
<span>
|
||||
Average Rating: {movie.averageVote.toFixed(1)}
|
||||
</span>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
|
@ -77,13 +79,34 @@ const Movie = () => {
|
|||
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z" />
|
||||
</svg>
|
||||
</p>
|
||||
{false ? (
|
||||
<p className={styles.Average}>
|
||||
<span>Average Rating: {movie.averageVote}</span>
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z" />
|
||||
</svg>
|
||||
</p>
|
||||
) : (
|
||||
<p>Rate this movie</p>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.Section}></div>
|
||||
<div className={styles.Section}>
|
||||
<h1 className={styles.Header}>Actors</h1>
|
||||
<div className={styles.Actors}>
|
||||
{cast?.map((actor) => (
|
||||
<div key={actor.id}>
|
||||
<Link
|
||||
key={actor.id}
|
||||
to={`/actor/${actor.id}`}
|
||||
className={styles.Actor}
|
||||
>
|
||||
{actor.profile_path ? (
|
||||
<img
|
||||
src={`https://image.tmdb.org/t/p/w200${actor.profile_path}`}
|
||||
|
@ -94,7 +117,7 @@ const Movie = () => {
|
|||
)}
|
||||
|
||||
<h3>{actor.name}</h3>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
|
64
src/styles/Actor.module.css
Normal file
64
src/styles/Actor.module.css
Normal file
|
@ -0,0 +1,64 @@
|
|||
.Container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 15px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.Images {
|
||||
display: flex;
|
||||
overflow: scroll;
|
||||
align-items: start;
|
||||
gap: 15px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.Image {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.Section div {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.Collapsed {
|
||||
-webkit-line-clamp: 5;
|
||||
-webkit-box-orient: vertical;
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.Collapse {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.Credits {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
gap: 20px;
|
||||
height: 500px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.ReadMore {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.Movie {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.Movie:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.Movie img {
|
||||
height: 300px;
|
||||
}
|
|
@ -18,6 +18,8 @@
|
|||
border-radius: 7px;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,3 +4,13 @@
|
|||
flex-direction: column;
|
||||
gap: 1.5em;
|
||||
}
|
||||
|
||||
.CardList {
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
width: 85%;
|
||||
margin: 0 100px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
|
|
@ -35,10 +35,16 @@
|
|||
display: flex;
|
||||
overflow: scroll;
|
||||
align-items: start;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.Actors > * {
|
||||
.Actor {
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.Actor:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.Tags {
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { defineConfig } from "vite"
|
||||
import react from "@vitejs/plugin-react"
|
||||
import path from "path"
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import path from "path";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
config: path.resolve(__dirname, "/src/config.json"),
|
||||
styles: path.resolve(__dirname, "/src/styles"),
|
||||
components: path.resolve(__dirname, "/src/components"),
|
||||
hooks: path.resolve(__dirname, "/src/hooks"),
|
||||
pages: path.resolve(__dirname, "/src/pages"),
|
||||
},
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
config: path.resolve(__dirname, "/src/config.json"),
|
||||
styles: path.resolve(__dirname, "/src/styles"),
|
||||
components: path.resolve(__dirname, "/src/components"),
|
||||
hooks: path.resolve(__dirname, "/src/hooks"),
|
||||
pages: path.resolve(__dirname, "/src/pages"),
|
||||
},
|
||||
})
|
||||
},
|
||||
});
|
||||
|
|
Reference in a new issue