From 6bebcf17abad02fbd65aad84c3676241190e1b5c Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Wed, 12 Jun 2024 21:52:35 -0700 Subject: [PATCH 01/28] Added new Components --- src/App.jsx | 110 +++++++++++++++++++++++++++++++++++++++++++--- src/Modal.css | 0 src/Modal.jsx | 0 src/MoviCard.css | 0 src/MovieCard.jsx | 0 src/MovieList.css | 0 src/MovieList.jsx | 0 src/Search.jsx | 0 8 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 src/Modal.css create mode 100644 src/Modal.jsx create mode 100644 src/MoviCard.css create mode 100644 src/MovieCard.jsx create mode 100644 src/MovieList.css create mode 100644 src/MovieList.jsx create mode 100644 src/Search.jsx diff --git a/src/App.jsx b/src/App.jsx index 48215b3f..46e0bd74 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,10 +1,106 @@ -import { useState } from 'react' -import './App.css' +import React from 'react'; +import MovieList from './MovieList'; +import Search from './Search'; +import { useEffect, useState } from 'react'; +import Modal from './Modal'; +import './App.css'; +import './Modal.css'; -const App = () => { -
- -
+function App() { + const [movies, setMovies] = useState([]); + const [page, setPage] = useState(1); + const [searchValue, setSearchValue] = useState(''); + const [modal, setModal] = useState(false); + const [movieID, setMovieID] = useState(''); + + const getMovies = async () => { + const url = `https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${page}&api_key=0d7613c1b95dbc61f3dd491c8f802475`; + const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIwZDc2MTNjMWI5NWRiYzYxZjNkZDQ5MWM4ZjgwMjQ3NSIsInN1YiI6IjY2Njc2NmJhODAyN2M0OWNmYjk5ZmJiYyIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.0iAwLYCfoeheb8A8_TWhHNrdDn2P1x3LL-d0_fGs6BA' + } + }; + + const response = await fetch(url, options); + const jsonResponse = await response.json(); + if (page === 1){ + setMovies(jsonResponse.results) + } else { + setMovies(prevMovies => [...prevMovies, ...jsonResponse.results]) + } + + } + + const searchMovies = async () => { + const result = searchValue.replace(/ +/g, '+'); + const url = `https://api.themoviedb.org/3/search/movie?query=${result}&api_key=0d7613c1b95dbc61f3dd491c8f802475`; + const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIwZDc2MTNjMWI5NWRiYzYxZjNkZDQ5MWM4ZjgwMjQ3NSIsInN1YiI6IjY2Njc2NmJhODAyN2M0OWNmYjk5ZmJiYyIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.0iAwLYCfoeheb8A8_TWhHNrdDn2P1x3LL-d0_fGs6BA' + } + }; + const response = await fetch(url, options); + const jsonResponse = await response.json(); + const results = jsonResponse.results.filter((movie) => { + return movie.title && movie.title.toLowerCase().includes(searchValue.toLowerCase()); + }) + if (results.length === 0){ + return "Try again, No results found :)" + }else { + setMovies(results); + } + + + } + + useEffect(() => { + getMovies(); + }, [page]) + + useEffect(() => { + searchMovies() + }, [searchValue]) + + const loadMore = () => { + setPage(page => page + 1); +}; +// setModal(!modal); + const movieClick = (movieId) => { + setMovieID(movieId); + } + + + return ( + <> +
+

Flixster

+ + + + + +
+ + + + ) } -export default App +export default App; diff --git a/src/Modal.css b/src/Modal.css new file mode 100644 index 00000000..e69de29b diff --git a/src/Modal.jsx b/src/Modal.jsx new file mode 100644 index 00000000..e69de29b diff --git a/src/MoviCard.css b/src/MoviCard.css new file mode 100644 index 00000000..e69de29b diff --git a/src/MovieCard.jsx b/src/MovieCard.jsx new file mode 100644 index 00000000..e69de29b diff --git a/src/MovieList.css b/src/MovieList.css new file mode 100644 index 00000000..e69de29b diff --git a/src/MovieList.jsx b/src/MovieList.jsx new file mode 100644 index 00000000..e69de29b diff --git a/src/Search.jsx b/src/Search.jsx new file mode 100644 index 00000000..e69de29b From 5d03bde045645db226487e9d0209db81a7988299 Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Wed, 12 Jun 2024 21:54:05 -0700 Subject: [PATCH 02/28] Added App styling --- src/App.css | 82 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 16 deletions(-) diff --git a/src/App.css b/src/App.css index 0bf65669..4214f1e7 100644 --- a/src/App.css +++ b/src/App.css @@ -1,28 +1,78 @@ -.App { +#root { + max-width: 1900px; + margin: 0 auto; + /* padding: 2rem; */ text-align: center; + background-color: #f5f5f5; } -.App-header { - background-color: #282c34; +.header { + border: 2px solid indigo; + width: 100%; + background-color: indigo; + display: fixed; +} + +.title { + color: white; +} +form{ display: flex; - flex-direction: row; - align-items: center; - justify-content: space-evenly; + justify-content: flex-start; + margin-left: 25px; +} + +#search-bar { + padding: 10px; + width: 500px; + margin-right: 3px; + +} + +#search-button { + background-color: #B19CD9; + padding: 10px; + border: 2px solid darkmagenta; + border-radius: 5px; +} + +#search-button:hover, +#nowPlayingButton:hover, +#searchTabButton:hover { color: white; - padding: 20px; + background-color: darkmagenta; + border: 2px solid #B19CD9; + border-radius: 5px; } -@media (max-width: 600px) { - .movie-card { - width: 100%; - } +#nowPlayingButton { + background-color: #B19CD9; + padding: 10px; + border: 2px solid darkmagenta; + border-radius: 5px; + margin-right: 10px; +} + +#searchTabButton { + background-color: #B19CD9; + padding: 10px; + border: 2px solid darkmagenta; + border-radius: 5px; +} - .search-bar { - flex-direction: column; - gap: 10px; + +.searchForm { + display: none; +} + +@media all and (max-width: 630px) { + #search-bar { + width: 300px; } +} - .search-bar form { - flex-direction: column; +@media all and (max-width: 400px) { + #search-bar { + width: 100px; } } From 065f2a23eed77f850fc5fa41ebced069214e1192 Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Wed, 12 Jun 2024 21:54:47 -0700 Subject: [PATCH 03/28] Added Modal styling --- src/Modal.css | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/Modal.css b/src/Modal.css index e69de29b..39463b92 100644 --- a/src/Modal.css +++ b/src/Modal.css @@ -0,0 +1,31 @@ +.movieModal{ + display: none; + position: fixed; + z-index: 1; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: hidden; + background-color: rgba(225, 225, 225, 0.5); + /* margin-top: 15px; */ +} + +.modalContent { + display: grid; + position: relative; + margin: 10% auto; + padding: 20px; + width: 63%; + height: 75%; + border: 2px solid black; + border-radius: 10px; + background-color: black; + justify-content: center; + color: white; +} + +.body-no-scroll { + overflow: hidden; + height: 100%; +} From d63990f88ec89d6a79a7e175a0270eea00f2887c Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Wed, 12 Jun 2024 21:55:20 -0700 Subject: [PATCH 04/28] Added MovieCard styling --- src/MoviCard.css | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/MoviCard.css b/src/MoviCard.css index e69de29b..9d19081c 100644 --- a/src/MoviCard.css +++ b/src/MoviCard.css @@ -0,0 +1,55 @@ +.imageContainer { + border: 2px solid indigo; + border-radius: 10px; + display: inline-flex; + flex-direction: column; + width: 250px; + height: auto; + justify-content: center; + margin: 20px 10px 10px 20px; + background-color: #f5f5f5; +} + +#movie-poster { + width: 100%; + height: 90%; + align-self: center; +} + +#movie-title { + font-size: 18px; + font-weight: bold; + margin-bottom: 5px; + flex-shrink: 1; +} + +#movie-rating { + font-size: 16px; + margin-top: 5px; + font-weight: bold; +} + +.bad { + color: red; +} + +.okay { + color: orange; +} + +.good { + color: lightgreen; +} + + +@media all and (max-width: 630px) { + .imageContainer { + width: 150px; + } +} + +@media all and (max-width: 800px) { + .imageContainer { + width: 190px; + } +} From e5d78ad8f3ee3abf84a0e0c8613b709ee39b4d7c Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Wed, 12 Jun 2024 21:55:51 -0700 Subject: [PATCH 05/28] Added MovieCard Component --- src/MovieCard.jsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/MovieCard.jsx b/src/MovieCard.jsx index e69de29b..72e3785e 100644 --- a/src/MovieCard.jsx +++ b/src/MovieCard.jsx @@ -0,0 +1,14 @@ +import './MovieCard.css'; + +const MovieCard = ({ image, title, rating }) => { + const className = Number(rating) < 5 ? 'bad' : Number(rating) < 7.5 ? 'okay' : 'good' + return ( +
+ +

{title}

+

{rating}

+
+ ) +} + +export default MovieCard From 026f9f2075f67afae2b9e10aea85f67d8d675275 Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Wed, 12 Jun 2024 21:56:16 -0700 Subject: [PATCH 06/28] Added MovieList styling --- src/MovieList.css | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/MovieList.css b/src/MovieList.css index e69de29b..f1214c18 100644 --- a/src/MovieList.css +++ b/src/MovieList.css @@ -0,0 +1,17 @@ +#load-more { + background-color: indigo; + color: white; + margin-top: 5px; + padding: 12px; + height: 50px; + align-self: end; + justify-self: center; + margin-bottom: 5px; +} + +.movie-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; +} From 883bfe1b9a1a73cb54e7af05b8323cfb8c9453d1 Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Wed, 12 Jun 2024 21:56:41 -0700 Subject: [PATCH 07/28] Added MovieList Component --- src/MovieList.jsx | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/MovieList.jsx b/src/MovieList.jsx index e69de29b..dea3d2b3 100644 --- a/src/MovieList.jsx +++ b/src/MovieList.jsx @@ -0,0 +1,29 @@ +import { useEffect, useState } from 'react'; +import './MovieList.css'; +import MovieCard from './MovieCard'; + + + +const MovieList = (props) => { + + const uniqueMovies = Array.from(new Map(props.data.map(movie => [movie.id, movie])).values()); + // replace uniqueMovies with the props and handle search call in Search.jsx + return ( + <> +
+ {uniqueMovies.map(movie => { + const posterImage = `https://image.tmdb.org/t/p/w500${movie.poster_path}`; + return ( +
props.handleMovieClick(movie.id)}> + +
+ ); + })} +
+
+ + + ); +}; + +export default MovieList From 7916f268cc403bbd5476009a7b9d08015a9f5e35 Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Wed, 12 Jun 2024 21:57:03 -0700 Subject: [PATCH 08/28] Added Search Component --- src/Search.jsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Search.jsx b/src/Search.jsx index e69de29b..e3709243 100644 --- a/src/Search.jsx +++ b/src/Search.jsx @@ -0,0 +1,16 @@ +import { useEffect, useState } from 'react'; +import './App.css'; + + +const Search = (props) => { + return ( +
+
+ props.setSearchValue(event.target.value)} placeholder="Search" id="search-bar"> + +
+
+ ) +} + +export default Search From c3d439a69c4baf98c6183e95a7c6c715447a56f1 Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Wed, 12 Jun 2024 21:58:06 -0700 Subject: [PATCH 09/28] Added api key --- .env | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 00000000..f6df4307 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +VITE_API_KEY=0d7613c1b95dbc61f3dd491c8f802475 + +# import.meta.env.VITE_API_KEY From 091f1380aa53a53900a7c45e12b9f77cbe1ad4f8 Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Wed, 12 Jun 2024 22:01:42 -0700 Subject: [PATCH 10/28] hid api key --- src/App.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 46e0bd74..bfbbf456 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -12,6 +12,7 @@ function App() { const [searchValue, setSearchValue] = useState(''); const [modal, setModal] = useState(false); const [movieID, setMovieID] = useState(''); + const apiKey = import.meta.env.VITE_API_KEY; const getMovies = async () => { const url = `https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${page}&api_key=0d7613c1b95dbc61f3dd491c8f802475`; @@ -19,7 +20,7 @@ function App() { method: 'GET', headers: { accept: 'application/json', - Authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIwZDc2MTNjMWI5NWRiYzYxZjNkZDQ5MWM4ZjgwMjQ3NSIsInN1YiI6IjY2Njc2NmJhODAyN2M0OWNmYjk5ZmJiYyIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.0iAwLYCfoeheb8A8_TWhHNrdDn2P1x3LL-d0_fGs6BA' + Authorization: `Bearer ${apiKey}` } }; @@ -40,7 +41,7 @@ function App() { method: 'GET', headers: { accept: 'application/json', - Authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIwZDc2MTNjMWI5NWRiYzYxZjNkZDQ5MWM4ZjgwMjQ3NSIsInN1YiI6IjY2Njc2NmJhODAyN2M0OWNmYjk5ZmJiYyIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.0iAwLYCfoeheb8A8_TWhHNrdDn2P1x3LL-d0_fGs6BA' + Authorization: `Bearer ${apiKey}` } }; const response = await fetch(url, options); From 7472cab0dbb30761745d19fc69639ee9af7c75eb Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Wed, 12 Jun 2024 22:04:53 -0700 Subject: [PATCH 11/28] edited file name --- package-lock.json | 8 ++++---- package.json | 2 +- src/{MoviCard.css => MovieCard.css} | 0 3 files changed, 5 insertions(+), 5 deletions(-) rename src/{MoviCard.css => MovieCard.css} (100%) diff --git a/package-lock.json b/package-lock.json index 92a683d2..941b4915 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", - "vite": "^5.2.0" + "vite": "^5.2.13" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -4100,9 +4100,9 @@ } }, "node_modules/vite": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.7.tgz", - "integrity": "sha512-k14PWOKLI6pMaSzAuGtT+Cf0YmIx12z9YGon39onaJNy8DLBfBJrzg9FQEmkAM5lpHBZs9wksWAsyF/HkpEwJA==", + "version": "5.2.13", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.13.tgz", + "integrity": "sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A==", "dev": true, "dependencies": { "esbuild": "^0.20.1", diff --git a/package.json b/package.json index eded5715..374006bf 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,6 @@ "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", - "vite": "^5.2.0" + "vite": "^5.2.13" } } diff --git a/src/MoviCard.css b/src/MovieCard.css similarity index 100% rename from src/MoviCard.css rename to src/MovieCard.css From 147c02e1d8f507c4d8ded56e743697b355883c01 Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Wed, 12 Jun 2024 23:52:13 -0700 Subject: [PATCH 12/28] Added Modal Component --- package-lock.json | 10 +++++++++ package.json | 1 + src/App.css | 1 + src/App.jsx | 6 +---- src/Modal.css | 56 +++++++++++++++++++++++++++++++++++++---------- src/Modal.jsx | 32 +++++++++++++++++++++++++++ src/MovieCard.jsx | 25 ++++++++++++++++----- src/MovieList.jsx | 2 +- 8 files changed, 110 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 941b4915..b8c51720 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "flixster", "version": "0.0.0", "dependencies": { + "date-fns": "^3.6.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -1645,6 +1646,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", diff --git a/package.json b/package.json index 374006bf..261a67e6 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "date-fns": "^3.6.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/src/App.css b/src/App.css index 4214f1e7..31b3c35e 100644 --- a/src/App.css +++ b/src/App.css @@ -65,6 +65,7 @@ form{ display: none; } + @media all and (max-width: 630px) { #search-bar { width: 300px; diff --git a/src/App.jsx b/src/App.jsx index bfbbf456..ab995460 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,15 +2,13 @@ import React from 'react'; import MovieList from './MovieList'; import Search from './Search'; import { useEffect, useState } from 'react'; -import Modal from './Modal'; import './App.css'; -import './Modal.css'; function App() { const [movies, setMovies] = useState([]); const [page, setPage] = useState(1); const [searchValue, setSearchValue] = useState(''); - const [modal, setModal] = useState(false); + // const [modal, setModal] = useState(false); const [movieID, setMovieID] = useState(''); const apiKey = import.meta.env.VITE_API_KEY; @@ -55,7 +53,6 @@ function App() { setMovies(results); } - } useEffect(() => { @@ -98,7 +95,6 @@ function App() { searchMovies(); }}>Search - ) diff --git a/src/Modal.css b/src/Modal.css index 39463b92..639d36b4 100644 --- a/src/Modal.css +++ b/src/Modal.css @@ -1,5 +1,5 @@ -.movieModal{ - display: none; +.movie-modal{ + display: flex; position: fixed; z-index: 1; left: 0; @@ -8,24 +8,58 @@ height: 100%; overflow: hidden; background-color: rgba(225, 225, 225, 0.5); - /* margin-top: 15px; */ + justify-content: center; + align-items: center; } -.modalContent { - display: grid; + +.modal-content { + display: flex; position: relative; margin: 10% auto; padding: 20px; - width: 63%; - height: 75%; + width: 50%; + /* height: 50%; */ border: 2px solid black; border-radius: 10px; - background-color: black; + background-color: #f5f5f5; justify-content: center; - color: white; + /* color: white; */ +} +.modal-info { + align-content: center; } -.body-no-scroll { - overflow: hidden; +#close-modal { + justify-self: flex-end; + font-size: 35px; + cursor: pointer; +} + +#modal-title { + font-size: 25px; + font-weight: bold; + color: indigo; + margin-top: 10px; + justify-self: center; +} + +#modal-poster{ + width: 100%; height: 100%; + justify-self: center; + align-self: center; + border: 2px +} + +#release-date { + font-weight: bold; + font-size: 18px; + color: indigo; +} + +#movie-overview { + font-size: 18px; + color: indigo; + font-weight: bold; } diff --git a/src/Modal.jsx b/src/Modal.jsx index e69de29b..3b093609 100644 --- a/src/Modal.jsx +++ b/src/Modal.jsx @@ -0,0 +1,32 @@ +import './Modal.css'; +import { format } from 'date-fns' + +const Modal = (props) => { + + const posterImage = `https://image.tmdb.org/t/p/w500${props.image}`; + const date = new Date(props.releaseDate); + const formattedDate = format(date, 'MMMM d, yyyy'); + // movie genre returns undefined, might need to fetch genre information from API because it currently returns an array of numbers, that's supposed oto map to genres??? + // console.log(props.movieGenre); + return ( +
+
+
+ +
+
+ +

Release Date: {formattedDate}

+

{props.movieOverview}

+

{props.movieGenre}

+
+ × +
+
+ ) +} + +export default Modal; + + +{/* */} diff --git a/src/MovieCard.jsx b/src/MovieCard.jsx index 72e3785e..852be224 100644 --- a/src/MovieCard.jsx +++ b/src/MovieCard.jsx @@ -1,12 +1,25 @@ +import { useState, useEffect } from 'react'; +import Modal from './Modal'; import './MovieCard.css'; +import './App.css'; -const MovieCard = ({ image, title, rating }) => { - const className = Number(rating) < 5 ? 'bad' : Number(rating) < 7.5 ? 'okay' : 'good' +const MovieCard = (props) => { + const [isClicked, setIsClicked] = useState(false); + + const toggleModal = () => { + setIsClicked(!isClicked); + } + // console.log(props); + + const className = Number(props.rating) < 5 ? 'bad' : Number(props.rating) < 7.5 ? 'okay' : 'good' return ( -
- -

{title}

-

{rating}

+
+
+ +

{props.title}

+

{props.rating}

+
+
) } diff --git a/src/MovieList.jsx b/src/MovieList.jsx index dea3d2b3..8e6f9295 100644 --- a/src/MovieList.jsx +++ b/src/MovieList.jsx @@ -15,7 +15,7 @@ const MovieList = (props) => { const posterImage = `https://image.tmdb.org/t/p/w500${movie.poster_path}`; return (
props.handleMovieClick(movie.id)}> - +
); })} From 2f3a39b63808ee8fd29d47e7c70aeddd2f777710 Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Thu, 13 Jun 2024 10:38:51 -0700 Subject: [PATCH 13/28] Working Modal --- src/App.jsx | 30 ++++++++++++++++-------------- src/Modal.css | 14 +++++++++++++- src/Modal.jsx | 41 +++++++++++++++++++++++++++++++++++++---- src/MovieCard.jsx | 3 +-- src/MovieList.jsx | 3 +-- 5 files changed, 68 insertions(+), 23 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index ab995460..29560e95 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -12,6 +12,22 @@ function App() { const [movieID, setMovieID] = useState(''); const apiKey = import.meta.env.VITE_API_KEY; + useEffect(() => { + getMovies(); + }, [page]) + + useEffect(() => { + searchMovies() + }, [searchValue]) + + const loadMore = () => { + setPage(page => page + 1); +}; + + const movieClick = (movieId) => { + setMovieID(movieId); + } + const getMovies = async () => { const url = `https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${page}&api_key=0d7613c1b95dbc61f3dd491c8f802475`; const options = { @@ -55,21 +71,7 @@ function App() { } - useEffect(() => { - getMovies(); - }, [page]) - - useEffect(() => { - searchMovies() - }, [searchValue]) - const loadMore = () => { - setPage(page => page + 1); -}; -// setModal(!modal); - const movieClick = (movieId) => { - setMovieID(movieId); - } return ( diff --git a/src/Modal.css b/src/Modal.css index 639d36b4..1102853c 100644 --- a/src/Modal.css +++ b/src/Modal.css @@ -20,7 +20,7 @@ padding: 20px; width: 50%; /* height: 50%; */ - border: 2px solid black; + border: 3px solid indigo; border-radius: 10px; background-color: #f5f5f5; justify-content: center; @@ -63,3 +63,15 @@ color: indigo; font-weight: bold; } + +#runtime { + font-size: 18px; + font-weight: bold; + color: indigo; +} + +#movie-genre { + font-size: 18px; + font-weight: bold; + color: indigo; +} diff --git a/src/Modal.jsx b/src/Modal.jsx index 3b093609..fc8c0619 100644 --- a/src/Modal.jsx +++ b/src/Modal.jsx @@ -1,7 +1,42 @@ import './Modal.css'; +import { useState, useEffect } from 'react'; import { format } from 'date-fns' const Modal = (props) => { + const apiKey = import.meta.env.VITE_API_KEY; + const [genres, setGenres] = useState(''); + const [runtime, setRuntime] = useState(''); + // const [genreNames, setGenreNames] = use + const movieId = props.id; + + // useEffect(() => { + // fetchMoreInfo(); + // const genreNames = genres.map(genre => genre.name); + // }, [props.isClicked]) + + + // const fetchMoreInfo = async () => { + // const url = `https://api.themoviedb.org/3/movie/${movieId}?language=en-US&api_key=0d7613c1b95dbc61f3dd491c8f802475` + // const options = { + // method: 'GET', + // headers: { + // accept: 'application/json', + // Authorization: `Bearer ${apiKey}`, + // } + // }; + + // const response = await fetch(url, options); + // const jsonResponse = await response.json(); + // console.log(jsonResponse.genres) + // setRuntime(jsonResponse.runtime); + // setGenres(jsonResponse.genres); + // } + + // fetchMoreInfo(); + // console.log(genres); + // const genreNames = genres.map(genre => genre.name); + // console.log(genreNames); + const posterImage = `https://image.tmdb.org/t/p/w500${props.image}`; const date = new Date(props.releaseDate); @@ -17,8 +52,9 @@ const Modal = (props) => {

Release Date: {formattedDate}

+ {/*

Runtime: {runtime} minutes

*/}

{props.movieOverview}

-

{props.movieGenre}

+ {/*

Genres: {genreNames}

*/}
×
@@ -27,6 +63,3 @@ const Modal = (props) => { } export default Modal; - - -{/* */} diff --git a/src/MovieCard.jsx b/src/MovieCard.jsx index 852be224..682fa4a3 100644 --- a/src/MovieCard.jsx +++ b/src/MovieCard.jsx @@ -9,7 +9,6 @@ const MovieCard = (props) => { const toggleModal = () => { setIsClicked(!isClicked); } - // console.log(props); const className = Number(props.rating) < 5 ? 'bad' : Number(props.rating) < 7.5 ? 'okay' : 'good' return ( @@ -19,7 +18,7 @@ const MovieCard = (props) => {

{props.title}

{props.rating}

- + ) } diff --git a/src/MovieList.jsx b/src/MovieList.jsx index 8e6f9295..62b745ed 100644 --- a/src/MovieList.jsx +++ b/src/MovieList.jsx @@ -5,7 +5,6 @@ import MovieCard from './MovieCard'; const MovieList = (props) => { - const uniqueMovies = Array.from(new Map(props.data.map(movie => [movie.id, movie])).values()); // replace uniqueMovies with the props and handle search call in Search.jsx return ( @@ -15,7 +14,7 @@ const MovieList = (props) => { const posterImage = `https://image.tmdb.org/t/p/w500${movie.poster_path}`; return (
props.handleMovieClick(movie.id)}> - +
); })} From c6f4bf5cebf7062fc45d6c6521a6a9c035bc4e1e Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Thu, 13 Jun 2024 10:44:26 -0700 Subject: [PATCH 14/28] Modal updated with runtime --- src/Modal.jsx | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Modal.jsx b/src/Modal.jsx index fc8c0619..b6850727 100644 --- a/src/Modal.jsx +++ b/src/Modal.jsx @@ -9,28 +9,28 @@ const Modal = (props) => { // const [genreNames, setGenreNames] = use const movieId = props.id; - // useEffect(() => { - // fetchMoreInfo(); - // const genreNames = genres.map(genre => genre.name); - // }, [props.isClicked]) + useEffect(() => { + fetchMoreInfo(); + // const genreNames = genres.map(genre => genre.name); + }, [props.isClicked]) - // const fetchMoreInfo = async () => { - // const url = `https://api.themoviedb.org/3/movie/${movieId}?language=en-US&api_key=0d7613c1b95dbc61f3dd491c8f802475` - // const options = { - // method: 'GET', - // headers: { - // accept: 'application/json', - // Authorization: `Bearer ${apiKey}`, - // } - // }; + const fetchMoreInfo = async () => { + const url = `https://api.themoviedb.org/3/movie/${movieId}?language=en-US&api_key=0d7613c1b95dbc61f3dd491c8f802475` + const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: `Bearer ${apiKey}`, + } + }; - // const response = await fetch(url, options); - // const jsonResponse = await response.json(); - // console.log(jsonResponse.genres) - // setRuntime(jsonResponse.runtime); - // setGenres(jsonResponse.genres); - // } + const response = await fetch(url, options); + const jsonResponse = await response.json(); + // console.log(jsonResponse.genres) + setRuntime(jsonResponse.runtime); + setGenres(jsonResponse.genres); + } // fetchMoreInfo(); // console.log(genres); @@ -52,7 +52,7 @@ const Modal = (props) => {

Release Date: {formattedDate}

- {/*

Runtime: {runtime} minutes

*/} +

Runtime: {runtime} minutes

{props.movieOverview}

{/*

Genres: {genreNames}

*/}
From 97585ba29236001b9090b1ac36a841ec0be52d99 Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Thu, 13 Jun 2024 13:33:50 -0700 Subject: [PATCH 15/28] =?UTF-8?q?Modal=20with=20runtime=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Modal.jsx | 23 ++++++----------------- src/MovieCard.jsx | 7 +++++-- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/Modal.jsx b/src/Modal.jsx index b6850727..4f624e98 100644 --- a/src/Modal.jsx +++ b/src/Modal.jsx @@ -4,19 +4,16 @@ import { format } from 'date-fns' const Modal = (props) => { const apiKey = import.meta.env.VITE_API_KEY; - const [genres, setGenres] = useState(''); + const [genres, setGenres] = useState([]); const [runtime, setRuntime] = useState(''); - // const [genreNames, setGenreNames] = use const movieId = props.id; useEffect(() => { fetchMoreInfo(); - // const genreNames = genres.map(genre => genre.name); - }, [props.isClicked]) - + }, [props.toggleModal]) const fetchMoreInfo = async () => { - const url = `https://api.themoviedb.org/3/movie/${movieId}?language=en-US&api_key=0d7613c1b95dbc61f3dd491c8f802475` + const url = `https://api.themoviedb.org/3/movie/${props.id}?language=en-US&api_key=0d7613c1b95dbc61f3dd491c8f802475` const options = { method: 'GET', headers: { @@ -27,22 +24,14 @@ const Modal = (props) => { const response = await fetch(url, options); const jsonResponse = await response.json(); - // console.log(jsonResponse.genres) - setRuntime(jsonResponse.runtime); setGenres(jsonResponse.genres); - } - - // fetchMoreInfo(); - // console.log(genres); - // const genreNames = genres.map(genre => genre.name); - // console.log(genreNames); + setRuntime(jsonResponse.runtime); + } const posterImage = `https://image.tmdb.org/t/p/w500${props.image}`; const date = new Date(props.releaseDate); const formattedDate = format(date, 'MMMM d, yyyy'); - // movie genre returns undefined, might need to fetch genre information from API because it currently returns an array of numbers, that's supposed oto map to genres??? - // console.log(props.movieGenre); return (
@@ -54,7 +43,7 @@ const Modal = (props) => {

Release Date: {formattedDate}

Runtime: {runtime} minutes

{props.movieOverview}

- {/*

Genres: {genreNames}

*/} + {/*

Genres: {genres}

*/}
×
diff --git a/src/MovieCard.jsx b/src/MovieCard.jsx index 682fa4a3..9e7dd5a9 100644 --- a/src/MovieCard.jsx +++ b/src/MovieCard.jsx @@ -5,6 +5,9 @@ import './App.css'; const MovieCard = (props) => { const [isClicked, setIsClicked] = useState(false); + const [genres, setGenres] = useState([]); + const [runtime, setRuntime] = useState(''); + const apiKey = import.meta.env.VITE_API_KEY; const toggleModal = () => { setIsClicked(!isClicked); @@ -13,12 +16,12 @@ const MovieCard = (props) => { const className = Number(props.rating) < 5 ? 'bad' : Number(props.rating) < 7.5 ? 'okay' : 'good' return (
-
+

{props.title}

{props.rating}

- +
) } From c9ad1e358c21a937e876b5149c00da960324413e Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Thu, 13 Jun 2024 14:18:59 -0700 Subject: [PATCH 16/28] =?UTF-8?q?Modal=20Component=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Modal.jsx | 29 +++++------------------------ src/MovieCard.jsx | 26 ++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/Modal.jsx b/src/Modal.jsx index 4f624e98..fb2479fb 100644 --- a/src/Modal.jsx +++ b/src/Modal.jsx @@ -5,33 +5,14 @@ import { format } from 'date-fns' const Modal = (props) => { const apiKey = import.meta.env.VITE_API_KEY; const [genres, setGenres] = useState([]); - const [runtime, setRuntime] = useState(''); const movieId = props.id; - useEffect(() => { - fetchMoreInfo(); - }, [props.toggleModal]) - - const fetchMoreInfo = async () => { - const url = `https://api.themoviedb.org/3/movie/${props.id}?language=en-US&api_key=0d7613c1b95dbc61f3dd491c8f802475` - const options = { - method: 'GET', - headers: { - accept: 'application/json', - Authorization: `Bearer ${apiKey}`, - } - }; - - const response = await fetch(url, options); - const jsonResponse = await response.json(); - setGenres(jsonResponse.genres); - setRuntime(jsonResponse.runtime); - - } - const posterImage = `https://image.tmdb.org/t/p/w500${props.image}`; const date = new Date(props.releaseDate); const formattedDate = format(date, 'MMMM d, yyyy'); + const genreNames = props.genres.map(genre => genre.name); + const genreNamesString = genreNames.join(', '); + console.log(genreNames); return (
@@ -41,9 +22,9 @@ const Modal = (props) => {

Release Date: {formattedDate}

-

Runtime: {runtime} minutes

+

Runtime: {props.runtime} minutes

{props.movieOverview}

- {/*

Genres: {genres}

*/} +

Genres: {genreNamesString}

×
diff --git a/src/MovieCard.jsx b/src/MovieCard.jsx index 9e7dd5a9..22811787 100644 --- a/src/MovieCard.jsx +++ b/src/MovieCard.jsx @@ -13,15 +13,37 @@ const MovieCard = (props) => { setIsClicked(!isClicked); } + useEffect(() => { + fetchMoreInfo(props.id); + }, [isClicked]) + + + + const fetchMoreInfo = async () => { + const url = `https://api.themoviedb.org/3/movie/${props.id}?language=en-US&api_key=0d7613c1b95dbc61f3dd491c8f802475` + const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: `Bearer ${apiKey}`, + } + }; + + const response = await fetch(url, options); + const jsonResponse = await response.json(); + setGenres(jsonResponse.genres); + setRuntime(jsonResponse.runtime); + } + const className = Number(props.rating) < 5 ? 'bad' : Number(props.rating) < 7.5 ? 'okay' : 'good' return (
-
+

{props.title}

{props.rating}

- +
) } From 116414d5f094f98f04ca973bd3508038fe5a4c57 Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Thu, 13 Jun 2024 15:42:19 -0700 Subject: [PATCH 17/28] Added like and watched features --- src/Modal.jsx | 7 +------ src/MovieCard.css | 14 +++++++++++++- src/MovieCard.jsx | 18 +++++++++++++++--- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/Modal.jsx b/src/Modal.jsx index fb2479fb..8167214a 100644 --- a/src/Modal.jsx +++ b/src/Modal.jsx @@ -1,18 +1,13 @@ import './Modal.css'; -import { useState, useEffect } from 'react'; import { format } from 'date-fns' const Modal = (props) => { - const apiKey = import.meta.env.VITE_API_KEY; - const [genres, setGenres] = useState([]); - const movieId = props.id; - const posterImage = `https://image.tmdb.org/t/p/w500${props.image}`; const date = new Date(props.releaseDate); const formattedDate = format(date, 'MMMM d, yyyy'); const genreNames = props.genres.map(genre => genre.name); const genreNamesString = genreNames.join(', '); - console.log(genreNames); + return (
diff --git a/src/MovieCard.css b/src/MovieCard.css index 9d19081c..7a762aa1 100644 --- a/src/MovieCard.css +++ b/src/MovieCard.css @@ -19,8 +19,9 @@ #movie-title { font-size: 18px; font-weight: bold; - margin-bottom: 5px; + margin-bottom: 10px; flex-shrink: 1; + color: indigo; } #movie-rating { @@ -41,6 +42,17 @@ color: lightgreen; } +#like-count { + font-weight: bold; + cursor: pointer; + color: indigo; + margin-left: 4px; +} + +#check-box { + font-weight: bold; + color: indigo; +} @media all and (max-width: 630px) { .imageContainer { diff --git a/src/MovieCard.jsx b/src/MovieCard.jsx index 22811787..599ee0e5 100644 --- a/src/MovieCard.jsx +++ b/src/MovieCard.jsx @@ -7,6 +7,8 @@ const MovieCard = (props) => { const [isClicked, setIsClicked] = useState(false); const [genres, setGenres] = useState([]); const [runtime, setRuntime] = useState(''); + const [likeCount, setLikeCount] = useState(0); + const [isChecked, setIsChecked] = useState(false); const apiKey = import.meta.env.VITE_API_KEY; const toggleModal = () => { @@ -17,6 +19,11 @@ const MovieCard = (props) => { fetchMoreInfo(props.id); }, [isClicked]) + const increaseLikes = () => { + const heart = document.getElementById(props.id).querySelector('#like-count'); + setLikeCount(likeCount + 1); + heart.innerText = `❤️ ${likeCount + 1}`; + } const fetchMoreInfo = async () => { @@ -35,13 +42,18 @@ const MovieCard = (props) => { setRuntime(jsonResponse.runtime); } + const handleCheckboxChange = () => { + setIsChecked(!isChecked); + } + const className = Number(props.rating) < 5 ? 'bad' : Number(props.rating) < 7.5 ? 'okay' : 'good' return (
-
- +
+

{props.title}

-

{props.rating}

+ +

{props.rating} ♡ {likeCount}

From 677bb79e045f3fb58154d16c3b9eb651743e6391 Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Thu, 13 Jun 2024 17:35:12 -0700 Subject: [PATCH 18/28] =?UTF-8?q?Filtering=20and=20Sorting=20feature?= =?UTF-8?q?=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 11 +++++- package.json | 3 +- src/App.css | 8 +++++ src/App.jsx | 85 +++++++++++++++++++++++++++++++++++++++++++++-- src/Modal.jsx | 5 +-- 5 files changed, 103 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index b8c51720..a7f42b75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,8 @@ "dependencies": { "date-fns": "^3.6.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-icons": "^5.2.1" }, "devDependencies": { "@types/react": "^18.2.66", @@ -3526,6 +3527,14 @@ "react": "^18.2.0" } }, + "node_modules/react-icons": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz", + "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/package.json b/package.json index 261a67e6..9d9deca6 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "dependencies": { "date-fns": "^3.6.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-icons": "^5.2.1" }, "devDependencies": { "@types/react": "^18.2.66", diff --git a/src/App.css b/src/App.css index 31b3c35e..69e3b261 100644 --- a/src/App.css +++ b/src/App.css @@ -65,6 +65,14 @@ form{ display: none; } +#dropdown-menu { + margin-left: 10px; + padding: 10px; + background-color: #B19CD9; + border: 2px solid darkmagenta; + border-radius: 5px; + text-align: center; +} @media all and (max-width: 630px) { #search-bar { diff --git a/src/App.jsx b/src/App.jsx index 29560e95..0a0ebfd7 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -8,7 +8,7 @@ function App() { const [movies, setMovies] = useState([]); const [page, setPage] = useState(1); const [searchValue, setSearchValue] = useState(''); - // const [modal, setModal] = useState(false); + const [selectedGenre, setSelectedGenre] = useState(''); const [movieID, setMovieID] = useState(''); const apiKey = import.meta.env.VITE_API_KEY; @@ -71,6 +71,75 @@ function App() { } + let genreId; + const handleGenreChange = (event) => { + const selectedValue = event.target.value; + if (selectedValue === "comedy") { + genreId = 35; + sortByGenre(genreId); + // const loadButton = document.getElementById('load-more'); + // loadButton.style.display = 'none'; + } else if (selectedValue === "action") { + genreId = 28; + sortByGenre(genreId); + // const loadButton = document.getElementById('load-more'); + // loadButton.style.display = 'none'; + } else if (selectedValue === "rating-desc"){ + sortByRatingDesc(); + // const loadButton = document.getElementById('load-more'); + // loadButton.style.display = 'none'; + } else if (selectedValue === "rating-asc"){ + sortByRatingAsc(); + } + } + + const sortByGenre = async (genreId) => { + const url =`https://api.themoviedb.org/3/discover/movie?include_adult=false&include_video=false&language=en-US&page=${page}&sort_by=popularity.desc&with_genres=${genreId}&api_key=0d7613c1b95dbc61f3dd491c8f802475`; + + const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: `Bearer ${apiKey}` + } + }; + + const response = await fetch(url, options); + const jsonResponse = await response.json(); + setMovies(jsonResponse.results); + } + + const sortByRatingDesc = async () => { + const url = `https://api.themoviedb.org/3/discover/movie?include_adult=false&include_video=false&language=en-US&page=${page}&sort_by=vote_average.desc&vote_average.gte=10&api_key=0d7613c1b95dbc61f3dd491c8f802475`; + + const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: `Bearer ${apiKey}` + } + }; + + const response = await fetch(url, options); + const jsonResponse = await response.json(); + setMovies(jsonResponse.results); + } + + const sortByRatingAsc = async () => { + const url = `https://api.themoviedb.org/3/discover/movie?include_adult=false&include_video=false&language=en-US&page=${page}&sort_by=vote_average.asc&vote_average.gte=5&api_key=0d7613c1b95dbc61f3dd491c8f802475`; + + const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: `Bearer ${apiKey}` + } + }; + + const response = await fetch(url, options); + const jsonResponse = await response.json(); + setMovies(jsonResponse.results); + } @@ -86,16 +155,26 @@ function App() { form.style.display = 'none'; loadButton.style.display = 'grid'; loadButton.style.marginLeft = '40%'; + setPage(1); getMovies(); }}>Now Playing + + diff --git a/src/Modal.jsx b/src/Modal.jsx index 8167214a..aec51875 100644 --- a/src/Modal.jsx +++ b/src/Modal.jsx @@ -1,10 +1,7 @@ import './Modal.css'; -import { format } from 'date-fns' const Modal = (props) => { const posterImage = `https://image.tmdb.org/t/p/w500${props.image}`; - const date = new Date(props.releaseDate); - const formattedDate = format(date, 'MMMM d, yyyy'); const genreNames = props.genres.map(genre => genre.name); const genreNamesString = genreNames.join(', '); @@ -16,7 +13,7 @@ const Modal = (props) => {
-

Release Date: {formattedDate}

+

Release Date: {props.releaseDate}

Runtime: {props.runtime} minutes

{props.movieOverview}

Genres: {genreNamesString}

From e7fe6ffb96cec8f0e719f688788aa0e4353850df Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Thu, 13 Jun 2024 22:11:54 -0700 Subject: [PATCH 19/28] Footer added --- src/App.css | 13 +++++++++++++ src/App.jsx | 11 +++++++---- src/Modal.jsx | 8 +++++++- src/MovieList.css | 9 +++++++-- src/MovieList.jsx | 4 +++- 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/App.css b/src/App.css index 69e3b261..942aea4f 100644 --- a/src/App.css +++ b/src/App.css @@ -11,6 +11,19 @@ width: 100%; background-color: indigo; display: fixed; + border-bottom-left-radius: 30px; + border-bottom-right-radius: 30px; +} + +.footer { + background-color: indigo; + height: 40px; + display: relative; + border: 2px solid indigo; + color: white; + margin-bottom: 0px; + border-top-left-radius: 30px; + border-top-right-radius: 30px; } .title { diff --git a/src/App.jsx b/src/App.jsx index 0a0ebfd7..98a71487 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -146,15 +146,15 @@ function App() { return ( <>
-

Flixster

+

🎥 Flixster 🍿

@@ -177,6 +177,9 @@ function App() {
+
+

Copyright © 2024 Meta U

+
) } diff --git a/src/Modal.jsx b/src/Modal.jsx index aec51875..4d191bd4 100644 --- a/src/Modal.jsx +++ b/src/Modal.jsx @@ -4,6 +4,12 @@ const Modal = (props) => { const posterImage = `https://image.tmdb.org/t/p/w500${props.image}`; const genreNames = props.genres.map(genre => genre.name); const genreNamesString = genreNames.join(', '); + let runtime; + if(props.runtime === 0) { + runtime = "Not Available" + }else { + runtime = `${props.runtime} minutes` + } return (
@@ -14,7 +20,7 @@ const Modal = (props) => {

Release Date: {props.releaseDate}

-

Runtime: {props.runtime} minutes

+

Runtime: {runtime}

{props.movieOverview}

Genres: {genreNamesString}

diff --git a/src/MovieList.css b/src/MovieList.css index f1214c18..1d2bebf4 100644 --- a/src/MovieList.css +++ b/src/MovieList.css @@ -4,8 +4,8 @@ margin-top: 5px; padding: 12px; height: 50px; - align-self: end; - justify-self: center; + /* align-self: end; + justify-self: center; */ margin-bottom: 5px; } @@ -15,3 +15,8 @@ flex-wrap: wrap; justify-content: center; } + + +.load-class { + justify-content: center; +} diff --git a/src/MovieList.jsx b/src/MovieList.jsx index 62b745ed..b35332b5 100644 --- a/src/MovieList.jsx +++ b/src/MovieList.jsx @@ -20,7 +20,9 @@ const MovieList = (props) => { })}

- +
+ +
); }; From 80ebbb44f87b7be48448bcef0c4680a341aab888 Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Fri, 14 Jun 2024 09:54:08 -0700 Subject: [PATCH 20/28] Working sidebar, no movies added. --- package-lock.json | 41 ++++++++++++++++++++++++++++- package.json | 3 ++- src/App.css | 24 ++++++++++++++--- src/App.jsx | 15 +++++++++-- src/Modal.css | 28 +++++++++++++++++--- src/MovieCard.css | 9 ++++++- src/MovieCard.jsx | 63 +++++++++++++++++++++++++++++++++++++++++---- src/MovieList.jsx | 9 +++++-- src/SideBar.css | 32 +++++++++++++++++++++++ src/SideBar.jsx | 28 ++++++++++++++++++++ src/SideBarCard.jsx | 10 +++++++ 11 files changed, 244 insertions(+), 18 deletions(-) create mode 100644 src/SideBar.css create mode 100644 src/SideBar.jsx create mode 100644 src/SideBarCard.jsx diff --git a/package-lock.json b/package-lock.json index a7f42b75..c569a521 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "date-fns": "^3.6.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-icons": "^5.2.1" + "react-icons": "^5.2.1", + "react-router-dom": "^6.23.1" }, "devDependencies": { "@types/react": "^18.2.66", @@ -929,6 +930,14 @@ "node": ">= 8" } }, + "node_modules/@remix-run/router": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz", + "integrity": "sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.13.2", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.2.tgz", @@ -3550,6 +3559,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.23.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.1.tgz", + "integrity": "sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==", + "dependencies": { + "@remix-run/router": "1.16.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.23.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.1.tgz", + "integrity": "sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==", + "dependencies": { + "@remix-run/router": "1.16.1", + "react-router": "6.23.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", diff --git a/package.json b/package.json index 9d9deca6..f3968bb1 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "date-fns": "^3.6.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-icons": "^5.2.1" + "react-icons": "^5.2.1", + "react-router-dom": "^6.23.1" }, "devDependencies": { "@types/react": "^18.2.66", diff --git a/src/App.css b/src/App.css index 942aea4f..f513b217 100644 --- a/src/App.css +++ b/src/App.css @@ -1,17 +1,21 @@ #root { max-width: 1900px; margin: 0 auto; - /* padding: 2rem; */ text-align: center; background-color: #f5f5f5; } +main { + max-width: 1700px; + display: grid; + justify-content: end; + margin-left: 70px; +} + .header { border: 2px solid indigo; width: 100%; background-color: indigo; - display: fixed; - border-bottom-left-radius: 30px; border-bottom-right-radius: 30px; } @@ -28,7 +32,9 @@ .title { color: white; + margin-top: 0px; } + form{ display: flex; justify-content: flex-start; @@ -87,6 +93,18 @@ form{ text-align: center; } +.side-bar-logo { + font-size: 30px; + display: grid; + margin-left: 30px; + margin-top: 20px; + color: #f5f5f5; +} + +.side-bar-logo:hover { + transform: scale(1.2); +} + @media all and (max-width: 630px) { #search-bar { width: 300px; diff --git a/src/App.jsx b/src/App.jsx index 98a71487..b189da6f 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,6 +2,8 @@ import React from 'react'; import MovieList from './MovieList'; import Search from './Search'; import { useEffect, useState } from 'react'; +import SideBar from './SideBar'; +import { FaBarsStaggered } from "react-icons/fa6"; import './App.css'; function App() { @@ -10,6 +12,7 @@ function App() { const [searchValue, setSearchValue] = useState(''); const [selectedGenre, setSelectedGenre] = useState(''); const [movieID, setMovieID] = useState(''); + const [showSideBar, setShowSideBar] = useState(false); const apiKey = import.meta.env.VITE_API_KEY; useEffect(() => { @@ -141,11 +144,16 @@ function App() { setMovies(jsonResponse.results); } - + const handleClickSideBar = () => { + setShowSideBar(!showSideBar); + } return ( <>
+
+ +

🎥 Flixster 🍿

@@ -176,7 +184,10 @@ function App() {
- + +
+ +

Copyright © 2024 Meta U

diff --git a/src/Modal.css b/src/Modal.css index 1102853c..42e3f9e1 100644 --- a/src/Modal.css +++ b/src/Modal.css @@ -10,22 +10,23 @@ background-color: rgba(225, 225, 225, 0.5); justify-content: center; align-items: center; + animation: fadeIn 0.3s ease-out; } - .modal-content { display: flex; position: relative; margin: 10% auto; padding: 20px; width: 50%; - /* height: 50%; */ border: 3px solid indigo; border-radius: 10px; background-color: #f5f5f5; justify-content: center; - /* color: white; */ + box-shadow: 0 4px 6px rgba(225, 225, 225, 0.5); + animation: zoomIn 0.3s ease-out; } + .modal-info { align-content: center; } @@ -75,3 +76,24 @@ font-weight: bold; color: indigo; } + + +@keyframes fadeIn { + from { + background-color: transparent; + } + to { + background-color: rgba(225, 225, 225, 0.5); + } +} + +@keyframes zoomIn { + from { + transform: scale(0); + opacity: 0; + } + to { + transform: scale(1); + opacity: 1; + } +} diff --git a/src/MovieCard.css b/src/MovieCard.css index 7a762aa1..18928e70 100644 --- a/src/MovieCard.css +++ b/src/MovieCard.css @@ -6,8 +6,15 @@ width: 250px; height: auto; justify-content: center; - margin: 20px 10px 10px 20px; + margin: 30px 10px 10px 30px; background-color: #f5f5f5; + transition: transform 0.3s ease-in-out; + will-change: transform; +} + +.imageContainer:hover { + transform: scale(0.9) rotate(3deg); + cursor: pointer; } #movie-poster { diff --git a/src/MovieCard.jsx b/src/MovieCard.jsx index 599ee0e5..0632e947 100644 --- a/src/MovieCard.jsx +++ b/src/MovieCard.jsx @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'; import Modal from './Modal'; import './MovieCard.css'; import './App.css'; +import SideBar from './SideBar'; const MovieCard = (props) => { const [isClicked, setIsClicked] = useState(false); @@ -9,15 +10,60 @@ const MovieCard = (props) => { const [runtime, setRuntime] = useState(''); const [likeCount, setLikeCount] = useState(0); const [isChecked, setIsChecked] = useState(false); + const [isLiked, setIsLiked] = useState(false); + const [likedMovies, setLikedMovies] = useState([]); + const [isWatched, setIsWatched] = useState(false); + const [watchedMovies, setWatchedMovies] = useState([]); const apiKey = import.meta.env.VITE_API_KEY; + useEffect(() => { + fetchMoreInfo(props.id); + }, [isClicked]) + + + // useEffect(() => { + // addLikedMovie(props.title, props.image); + // }, [isLiked]) + + useEffect(() => { + addWatchedMovie(props.title, props.image); + console.log(props.watchedMovies); + }, [isChecked]) + + // useEffect(() => { + // onWatchedToggle(title, image, isChecked); + // }, [isChecked, title, image, onWatchedToggle]); + const toggleModal = () => { setIsClicked(!isClicked); } - useEffect(() => { - fetchMoreInfo(props.id); - }, [isClicked]) + const toggleLike = () => { + setIsLiked(!isLiked); + } + + const toggleWatched = () => { + setIsWatched(!isWatched); + } + + // const addLikedMovie = (movieTitle, movieImage) => { + // const newLikedList = [...likedMovies, [movieTitle, movieImage]]; + // setLikedMovies(newLikedList); + // // console.log(likedMovies); + // } + + const addWatchedMovie = (movieTitle, movieImage) => { + if (!isChecked) { + const newWatchedList = [...watchedMovies, {title: movieTitle, image: movieImage}]; + setWatchedMovies(newWatchedList); + }else { + const newWatchedList = watchedMovies.filter(movie => movie.title !== movieTitle); + setWatchedMovies(newWatchedList); + } + // console.log(isChecked); + // console.log(watchedMovies); + } + const increaseLikes = () => { const heart = document.getElementById(props.id).querySelector('#like-count'); @@ -46,16 +92,23 @@ const MovieCard = (props) => { setIsChecked(!isChecked); } + function handleLike() { + increaseLikes(); + toggleLike(); + } + const className = Number(props.rating) < 5 ? 'bad' : Number(props.rating) < 7.5 ? 'okay' : 'good' return (

{props.title}

- -

{props.rating} ♡ {likeCount}

+ +

{props.rating} ♡ {likeCount}

+ {/* */} +
) } diff --git a/src/MovieList.jsx b/src/MovieList.jsx index b35332b5..d0f0ed71 100644 --- a/src/MovieList.jsx +++ b/src/MovieList.jsx @@ -6,7 +6,7 @@ import MovieCard from './MovieCard'; const MovieList = (props) => { const uniqueMovies = Array.from(new Map(props.data.map(movie => [movie.id, movie])).values()); - // replace uniqueMovies with the props and handle search call in Search.jsx + return ( <>
@@ -14,7 +14,12 @@ const MovieList = (props) => { const posterImage = `https://image.tmdb.org/t/p/w500${movie.poster_path}`; return (
props.handleMovieClick(movie.id)}> - +
); })} diff --git a/src/SideBar.css b/src/SideBar.css new file mode 100644 index 00000000..5eb786d7 --- /dev/null +++ b/src/SideBar.css @@ -0,0 +1,32 @@ +.side-bar { + width: 200px; + background-color: indigo; + position: absolute; + height: 100vh; + padding: 0 30px; + left: -100%; + transition: all 0.5s; + color: white; + display: fixed; + z-index: 100; +} + +.side-bar.active { + left: 0; +} + +ul { + padding: 0; +} + +li { + list-style-type: none; +} + +.side-bar-content{ + font-size: 18px; +} + +.side-bar-title { + font-size: 20px; +} diff --git a/src/SideBar.jsx b/src/SideBar.jsx new file mode 100644 index 00000000..dc5ee003 --- /dev/null +++ b/src/SideBar.jsx @@ -0,0 +1,28 @@ +import React from 'react'; +import './SideBar.css'; + +const SideBar = (props) => { + // console.log(props) + // const posterImage = `https://image.tmdb.org/t/p/w500${props.likedMovies.movieImage}` + return ( +
+
+

SideBar

+
+
+
    +
    +
  • Liked Movies
  • + {/* poster */} +

    +
    +
    +
  • Watched Movies
  • +
    +
+
+
+ ) +} + +export default SideBar; diff --git a/src/SideBarCard.jsx b/src/SideBarCard.jsx new file mode 100644 index 00000000..4da75c12 --- /dev/null +++ b/src/SideBarCard.jsx @@ -0,0 +1,10 @@ +const SideBarCard = () => { + return ( +
+ {/* +

*/} +
+ ) +} + +export default SideBarCard; From 04a93692bd837b7fbb15ad9ed54d15369d64b4d2 Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Fri, 14 Jun 2024 13:32:39 -0700 Subject: [PATCH 21/28] =?UTF-8?q?Adding=20liked=20songs=20to=20side=20bar?= =?UTF-8?q?=20feature=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.jsx | 14 ++++----- src/MovieCard.css | 2 +- src/MovieCard.jsx | 69 +++++++++++++++------------------------------ src/MovieList.css | 2 -- src/MovieList.jsx | 4 +-- src/SideBar.css | 4 +-- src/SideBar.jsx | 16 ++++++++--- src/SideBarCard.css | 26 +++++++++++++++++ src/SideBarCard.jsx | 9 ++++-- 9 files changed, 75 insertions(+), 71 deletions(-) create mode 100644 src/SideBarCard.css diff --git a/src/App.jsx b/src/App.jsx index b189da6f..12ae4b0b 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -13,8 +13,11 @@ function App() { const [selectedGenre, setSelectedGenre] = useState(''); const [movieID, setMovieID] = useState(''); const [showSideBar, setShowSideBar] = useState(false); + const [likedList, setLikedList] = useState([]); + const [watchedList, setWatchedList] = useState([]); const apiKey = import.meta.env.VITE_API_KEY; + console.log(likedList); useEffect(() => { getMovies(); }, [page]) @@ -80,17 +83,11 @@ function App() { if (selectedValue === "comedy") { genreId = 35; sortByGenre(genreId); - // const loadButton = document.getElementById('load-more'); - // loadButton.style.display = 'none'; } else if (selectedValue === "action") { genreId = 28; sortByGenre(genreId); - // const loadButton = document.getElementById('load-more'); - // loadButton.style.display = 'none'; } else if (selectedValue === "rating-desc"){ sortByRatingDesc(); - // const loadButton = document.getElementById('load-more'); - // loadButton.style.display = 'none'; } else if (selectedValue === "rating-asc"){ sortByRatingAsc(); } @@ -160,7 +157,6 @@ function App() {
) } diff --git a/src/MovieList.css b/src/MovieList.css index 1d2bebf4..43c1227b 100644 --- a/src/MovieList.css +++ b/src/MovieList.css @@ -4,8 +4,6 @@ margin-top: 5px; padding: 12px; height: 50px; - /* align-self: end; - justify-self: center; */ margin-bottom: 5px; } diff --git a/src/MovieList.jsx b/src/MovieList.jsx index d0f0ed71..3eebb8c6 100644 --- a/src/MovieList.jsx +++ b/src/MovieList.jsx @@ -1,4 +1,3 @@ -import { useEffect, useState } from 'react'; import './MovieList.css'; import MovieCard from './MovieCard'; @@ -17,8 +16,7 @@ const MovieList = (props) => {
); diff --git a/src/SideBar.css b/src/SideBar.css index 5eb786d7..aa27c91c 100644 --- a/src/SideBar.css +++ b/src/SideBar.css @@ -2,12 +2,12 @@ width: 200px; background-color: indigo; position: absolute; - height: 100vh; + min-height: 100vh; padding: 0 30px; left: -100%; transition: all 0.5s; color: white; - display: fixed; + display: block; z-index: 100; } diff --git a/src/SideBar.jsx b/src/SideBar.jsx index dc5ee003..e3186483 100644 --- a/src/SideBar.jsx +++ b/src/SideBar.jsx @@ -1,9 +1,10 @@ import React from 'react'; +import { useState } from 'react'; +import SideBarCard from './SideBarCard'; import './SideBar.css'; const SideBar = (props) => { - // console.log(props) - // const posterImage = `https://image.tmdb.org/t/p/w500${props.likedMovies.movieImage}` + const likedMovies = props.likedList; return (
@@ -13,8 +14,15 @@ const SideBar = (props) => {
  • Liked Movies
  • - {/* poster */} -

    + {likedMovies.map(movie => ( + + ))}
  • Watched Movies
  • diff --git a/src/SideBarCard.css b/src/SideBarCard.css new file mode 100644 index 00000000..daa1cfb6 --- /dev/null +++ b/src/SideBarCard.css @@ -0,0 +1,26 @@ +.side-bar-card { + border: 2px solid indigo; + border-radius: 10px; + display: flex; + flex-direction: column; + width: 70px; + height: auto; + justify-content: center; +} + + +.sidebar-poster { + width: 70px; + height: 80%; + align-self: center; + margin-bottom: 0px; +} + +.sidebar-movie-title { + font-size: 13px; + font-weight: bold; + margin-bottom: 5px; + flex-shrink: 1; + color: white; + margin-top: 0px; +} diff --git a/src/SideBarCard.jsx b/src/SideBarCard.jsx index 4da75c12..4ad66e7b 100644 --- a/src/SideBarCard.jsx +++ b/src/SideBarCard.jsx @@ -1,8 +1,11 @@ -const SideBarCard = () => { +import React from 'react'; +import './SideBarCard.css'; + +const SideBarCard = (props) => { return (
    - {/* -

    */} + +

    {props.title}

    ) } From d7d123c6cf5e685c7ca3814ef08ff3c8a075f2e1 Mon Sep 17 00:00:00 2001 From: Maya Ody-Ajike Date: Fri, 14 Jun 2024 14:07:31 -0700 Subject: [PATCH 22/28] =?UTF-8?q?Adding=20watched=20movies=20to=20sidebar?= =?UTF-8?q?=20feature=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.jsx | 5 ++--- src/MovieCard.jsx | 27 +++++++++++++++++++-------- src/MovieList.jsx | 1 + src/SideBar.css | 2 ++ src/SideBar.jsx | 14 ++++++++++++-- 5 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 12ae4b0b..fa737f89 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -17,7 +17,6 @@ function App() { const [watchedList, setWatchedList] = useState([]); const apiKey = import.meta.env.VITE_API_KEY; - console.log(likedList); useEffect(() => { getMovies(); }, [page]) @@ -180,9 +179,9 @@ function App() { - +
    - +