From ffd3ca466aa650e58ef50f2d1cd9f7bfa545376d Mon Sep 17 00:00:00 2001 From: Gabriella Gabriel Toby Date: Wed, 12 Jun 2024 11:06:16 -0700 Subject: [PATCH 01/11] feat: sorting sorts the current stored movies --- .env | 1 + src/App.css | 1 + src/App.jsx | 26 +++++++++-- src/components/Header.css | 44 +++++++++++++++++++ src/components/Header.jsx | 24 +++++++++++ src/components/ModalContainer.jsx | 0 src/components/MovieCards.jsx | 13 ++++++ src/components/MovieList.jsx | 26 +++++++++++ src/components/NowPlayingScreen.css | 0 src/components/NowPlayingScreen.jsx | 67 +++++++++++++++++++++++++++++ src/components/SearchScreen.jsx | 46 ++++++++++++++++++++ src/components/movieCards.css | 36 ++++++++++++++++ 12 files changed, 280 insertions(+), 4 deletions(-) create mode 100644 .env create mode 100644 src/components/Header.css create mode 100644 src/components/Header.jsx create mode 100644 src/components/ModalContainer.jsx create mode 100644 src/components/MovieCards.jsx create mode 100644 src/components/MovieList.jsx create mode 100644 src/components/NowPlayingScreen.css create mode 100644 src/components/NowPlayingScreen.jsx create mode 100644 src/components/SearchScreen.jsx create mode 100644 src/components/movieCards.css diff --git a/.env b/.env new file mode 100644 index 00000000..bc925fa4 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +VITE_API_KEY= dae9a6b98e10cd92e717f89b3d61b7c5 \ No newline at end of file diff --git a/src/App.css b/src/App.css index 0bf65669..02f9967a 100644 --- a/src/App.css +++ b/src/App.css @@ -26,3 +26,4 @@ flex-direction: column; } } + diff --git a/src/App.jsx b/src/App.jsx index 48215b3f..17a65816 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,10 +1,28 @@ import { useState } from 'react' import './App.css' +import Header from "./components/Header"; +import.meta.env.VITE_API_KEY; +import NowPlayingScreen from './components/NowPlayingScreen'; +import SearchScreen from './components/SearchScreen.jsx'; const App = () => { -
- -
+ const [isSearching, setSearching] = useState(false); + const [criteria, setCriteria] = useState("") + + const handleNewCriteria = (criteriaFromHeader) => { + setCriteria(criteriaFromHeader) + } + + return ( +
+
+ {!isSearching ? + + : + + } +
+ ); } -export default App +export default App; diff --git a/src/components/Header.css b/src/components/Header.css new file mode 100644 index 00000000..0ee2c1bf --- /dev/null +++ b/src/components/Header.css @@ -0,0 +1,44 @@ +.header { + background-color: #ff0000; + padding: 10px 20px; + display: flex; + justify-content: space-between; + align-items: center; + color: white; +} + +.header h1 { + margin: 0; +} + +.toggle-button { + display: flex; + align-items: center; +} + +.header input { + padding: 5px; + font-size: 16px; + border: none; + border-radius: 5px; + margin-right: 10px; +} + +.header button { + background-color: white; + color: #ff0000; + border: none; + padding: 5px 10px; + border-radius: 5px; + cursor: pointer; + margin-right: 10px; +} + +.header .sort-by { + background-color: #ff0000; + color: white; + border: none; + padding: 5px 10px; + border-radius: 5px; + cursor: pointer; +} \ No newline at end of file diff --git a/src/components/Header.jsx b/src/components/Header.jsx new file mode 100644 index 00000000..7cb696b2 --- /dev/null +++ b/src/components/Header.jsx @@ -0,0 +1,24 @@ + +//import './Header.css'; // Import the CSS file for the header + +import React from 'react'; +import './Header.css'; + +function Header({ setSearching, sortMovies }) { + return ( +
+

Flixster

+
+ + + +
+
+ ); +} + +export default Header; \ No newline at end of file diff --git a/src/components/ModalContainer.jsx b/src/components/ModalContainer.jsx new file mode 100644 index 00000000..e69de29b diff --git a/src/components/MovieCards.jsx b/src/components/MovieCards.jsx new file mode 100644 index 00000000..5e8b3468 --- /dev/null +++ b/src/components/MovieCards.jsx @@ -0,0 +1,13 @@ +import "./movieCards.css" + +function MovieCard (props) { + return( +
+ +

{props.title}

+

Rating: {props.rating}

+
+ ); +} + +export default MovieCard; \ No newline at end of file diff --git a/src/components/MovieList.jsx b/src/components/MovieList.jsx new file mode 100644 index 00000000..2b1d8e2c --- /dev/null +++ b/src/components/MovieList.jsx @@ -0,0 +1,26 @@ +//import {useEffect, useState} from "react"; +import MovieCards from "./MovieCards" +import "./movieCards.css" + +function MovieList(props){ + + + function createCards(card, index){ + return( + + ) + } + + return( +
+ {props.data.map(createCards)} +
+ ) +} + +export default MovieList; diff --git a/src/components/NowPlayingScreen.css b/src/components/NowPlayingScreen.css new file mode 100644 index 00000000..e69de29b diff --git a/src/components/NowPlayingScreen.jsx b/src/components/NowPlayingScreen.jsx new file mode 100644 index 00000000..065e95b4 --- /dev/null +++ b/src/components/NowPlayingScreen.jsx @@ -0,0 +1,67 @@ +import {useEffect, useState} from "react"; +import MovieList from "./MovieList"; +import Header from "./Header" + + +function NowPlayingScreen({ criteriaFromHeader }){ + const [isSearching, setSearching] = useState(false); + const [movies, setMovies]=useState([]); + const [pageNumber, setPageNumber]=useState(1); + const[url, setUrl]=useState(`https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${pageNumber}`) + + + useEffect(()=>{ + const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJkYWU5YTZiOThlMTBjZDkyZTcxN2Y4OWIzZDYxYjdjNSIsInN1YiI6IjY2NjY1MTQ1Y2M3MDc0ZDliNjFjMWM2ZiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.IDf1-04fWbMoc-zzed3BAcZLflL14UG-mdjcZobVjxA' + } + }; + + fetch(url, options) + .then(response => response.json()) + .then(response => { + handleSortMovies(criteriaFromHeader, movies.concat(response.results)) + // setMovies(prevMovies =>prevMovies) + }) + .catch(err => console.error(err)); + }, [url, pageNumber]); + + function loadMore(){ + setPageNumber(prevPageNumber => prevPageNumber+1) + setUrl(`https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${pageNumber + 1}`); + //useEffect(); + } + + function handleSortMovies(criteria, parmaMovies = movies){ + + const sortedMovies = [...parmaMovies].sort((a, b) => { + + if (criteria === 'title') { + return a.title.localeCompare(b.title); + } else if (criteria === 'date') { + return new Date(a.release_date) - new Date(b.release_date); + } else if (criteria === 'rating') { + return b.vote_average - a.vote_average; + } + return 0; + }); + setMovies(sortedMovies); + } + + useEffect(function() { + handleSortMovies(criteriaFromHeader) + }, [criteriaFromHeader]) + + return( +
+ + +
+ + ) + +} + +export default NowPlayingScreen; \ No newline at end of file diff --git a/src/components/SearchScreen.jsx b/src/components/SearchScreen.jsx new file mode 100644 index 00000000..77e2525d --- /dev/null +++ b/src/components/SearchScreen.jsx @@ -0,0 +1,46 @@ +import {useState} from "react"; +import MovieList from "./MovieList"; + +function SearchScreen(){ + const[query, setSearchQuery]= useState('') + const[movies, setMovies]= useState([]); + const[pageNumber, setPageNumber]= useState(1) + + function handleSearch(searchQuery){ + const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJkYWU5YTZiOThlMTBjZDkyZTcxN2Y4OWIzZDYxYjdjNSIsInN1YiI6IjY2NjY1MTQ1Y2M3MDc0ZDliNjFjMWM2ZiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.IDf1-04fWbMoc-zzed3BAcZLflL14UG-mdjcZobVjxA' + } + }; + + fetch(`https://api.themoviedb.org/3/search/movie?query=${searchQuery}&include_adult=false&language=en-US&page=${pageNumber}`, options) + .then(response => response.json()) + .then(response => setMovies(response.results)) + .catch(err => console.error(err)); + } + + function loadMore(){ + setPageNumber(prevPageNumber => prevPageNumber+1) + + } + + return( +
+ setSearchQuery(e.target.value.toLowerCase())} + /> + + + + + +
+ ) +} + +export default SearchScreen; \ No newline at end of file diff --git a/src/components/movieCards.css b/src/components/movieCards.css new file mode 100644 index 00000000..57ddccc7 --- /dev/null +++ b/src/components/movieCards.css @@ -0,0 +1,36 @@ + + +.movie-card { + background-color: #ff0000; + color: white; + width: 200px; + margin: 10px; + padding: 10px; + border-radius: 10px; + text-align: center; +} +.movie-card:hover { + transform: scale(1.0); + box-shadow: 0 7px 14px black; + +} +.movie-card img { + width: 100%; + border-radius: 10px; +} + +.movie-card h2 { + font-size: 18px; + margin: 10px 0 5px; +} + +.movie-card p { + margin: 5px 0; +} + +.movieList { + display: flex; + flex-wrap: wrap; + justify-content: center; + padding: 20px; +} \ No newline at end of file From 285ac3ab06ece51c8dde6a0478ccb7827617ebe8 Mon Sep 17 00:00:00 2001 From: Gabriella Gabriel Toby Date: Wed, 12 Jun 2024 11:17:08 -0700 Subject: [PATCH 02/11] "" --- .gitignore | 1 + src/components/NowPlayingScreen.jsx | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index a547bf36..7ceb59f8 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ dist-ssr *.njsproj *.sln *.sw? +.env diff --git a/src/components/NowPlayingScreen.jsx b/src/components/NowPlayingScreen.jsx index 065e95b4..e9952872 100644 --- a/src/components/NowPlayingScreen.jsx +++ b/src/components/NowPlayingScreen.jsx @@ -4,7 +4,7 @@ import Header from "./Header" function NowPlayingScreen({ criteriaFromHeader }){ - const [isSearching, setSearching] = useState(false); + const const [movies, setMovies]=useState([]); const [pageNumber, setPageNumber]=useState(1); const[url, setUrl]=useState(`https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${pageNumber}`) @@ -23,7 +23,6 @@ function NowPlayingScreen({ criteriaFromHeader }){ .then(response => response.json()) .then(response => { handleSortMovies(criteriaFromHeader, movies.concat(response.results)) - // setMovies(prevMovies =>prevMovies) }) .catch(err => console.error(err)); }, [url, pageNumber]); From 373fc5ae3802d212d947ba126c607a61612c436f Mon Sep 17 00:00:00 2001 From: Gabriella Gabriel Toby Date: Wed, 12 Jun 2024 11:20:45 -0700 Subject: [PATCH 03/11] removed env file --- .env | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .env diff --git a/.env b/.env deleted file mode 100644 index bc925fa4..00000000 --- a/.env +++ /dev/null @@ -1 +0,0 @@ -VITE_API_KEY= dae9a6b98e10cd92e717f89b3d61b7c5 \ No newline at end of file From 245af4f54ad4a1137d98b9a4d9bef8794178985b Mon Sep 17 00:00:00 2001 From: Gabriella Gabriel Toby Date: Wed, 12 Jun 2024 15:19:49 -0700 Subject: [PATCH 04/11] Modal overlay --- src/components/ModalContainer.css | 33 +++++++++++++++++++++++++++++ src/components/ModalContainer.jsx | 23 ++++++++++++++++++++ src/components/MovieCards.jsx | 26 ++++++++++++++++++----- src/components/NowPlayingScreen.jsx | 6 ++++-- src/components/SearchScreen.jsx | 7 +++--- 5 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 src/components/ModalContainer.css diff --git a/src/components/ModalContainer.css b/src/components/ModalContainer.css new file mode 100644 index 00000000..127cc648 --- /dev/null +++ b/src/components/ModalContainer.css @@ -0,0 +1,33 @@ +.modal-overlay { + position: absolute; + top: 25%; + left: 25%; + background: rgba(0, 0, 0, 0.7); + width: 50%; + + + justify-content: center; + align-items: center; + z-index: 1000; + } + + + .modal-content { + background: white; + padding: 20px; + border-radius: 5px; + position: relative; + } + + .close-button { + color:black; + position: absolute; + top: 10px; + right: 10px; + background: none; + border: none; + font-size: 24px; + cursor: pointer; + } + + \ No newline at end of file diff --git a/src/components/ModalContainer.jsx b/src/components/ModalContainer.jsx index e69de29b..81a910a3 100644 --- a/src/components/ModalContainer.jsx +++ b/src/components/ModalContainer.jsx @@ -0,0 +1,23 @@ +import "./ModalContainer.css"; +import React from "react"; + +function ModalContainer(props){ + return( +
+
+ +
+
Title:{props.data.title}
+ +
Original Title:
+
Overview: {props.data.overview}
+
Release Date: {props.data.releaseDate}
+
+
+
+ ); +} + +export default ModalContainer; \ No newline at end of file diff --git a/src/components/MovieCards.jsx b/src/components/MovieCards.jsx index 5e8b3468..e921bfea 100644 --- a/src/components/MovieCards.jsx +++ b/src/components/MovieCards.jsx @@ -1,12 +1,28 @@ import "./movieCards.css" +import { useState } from "react"; +import ModalContainer from "./ModalContainer"; function MovieCard (props) { + const[isModalOpen, setModal]=useState(false) + + function openModal(){ + setModal(true) + } + function closeModal() + { + setModal(false) + } return( -
- -

{props.title}

-

Rating: {props.rating}

-
+ <> +
+ +

{props.title}

+

Rating: {props.rating}

+
+ {isModalOpen ? : null} + + + ); } diff --git a/src/components/NowPlayingScreen.jsx b/src/components/NowPlayingScreen.jsx index e9952872..c40b93af 100644 --- a/src/components/NowPlayingScreen.jsx +++ b/src/components/NowPlayingScreen.jsx @@ -4,18 +4,20 @@ import Header from "./Header" function NowPlayingScreen({ criteriaFromHeader }){ - const + const [movies, setMovies]=useState([]); const [pageNumber, setPageNumber]=useState(1); const[url, setUrl]=useState(`https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${pageNumber}`) + const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN; + useEffect(()=>{ const options = { method: 'GET', headers: { accept: 'application/json', - Authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJkYWU5YTZiOThlMTBjZDkyZTcxN2Y4OWIzZDYxYjdjNSIsInN1YiI6IjY2NjY1MTQ1Y2M3MDc0ZDliNjFjMWM2ZiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.IDf1-04fWbMoc-zzed3BAcZLflL14UG-mdjcZobVjxA' + Authorization: `Bearer ${ACCESS_TOKEN}` } }; diff --git a/src/components/SearchScreen.jsx b/src/components/SearchScreen.jsx index 77e2525d..b83b44c0 100644 --- a/src/components/SearchScreen.jsx +++ b/src/components/SearchScreen.jsx @@ -5,13 +5,14 @@ function SearchScreen(){ const[query, setSearchQuery]= useState('') const[movies, setMovies]= useState([]); const[pageNumber, setPageNumber]= useState(1) + const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN; function handleSearch(searchQuery){ const options = { method: 'GET', headers: { accept: 'application/json', - Authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJkYWU5YTZiOThlMTBjZDkyZTcxN2Y4OWIzZDYxYjdjNSIsInN1YiI6IjY2NjY1MTQ1Y2M3MDc0ZDliNjFjMWM2ZiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.IDf1-04fWbMoc-zzed3BAcZLflL14UG-mdjcZobVjxA' + Authorization: `Bearer ${ACCESS_TOKEN}` } }; @@ -23,9 +24,9 @@ function SearchScreen(){ function loadMore(){ setPageNumber(prevPageNumber => prevPageNumber+1) - + } - + return(
Date: Fri, 14 Jun 2024 15:17:24 -0700 Subject: [PATCH 05/11] stretch features done --- index.html | 1 + src/App.css | 4 +- src/App.jsx | 46 ++++++++++- src/components/FavMovies.jsx | 10 +++ src/components/Header.css | 6 +- src/components/Header.jsx | 11 +-- src/components/ModalContainer.css | 119 ++++++++++++++++++++-------- src/components/ModalContainer.jsx | 56 ++++++++++--- src/components/MovieCards.jsx | 30 +++++-- src/components/MovieList.jsx | 9 ++- src/components/NowPlayingScreen.jsx | 32 ++++++-- src/components/SearchScreen.jsx | 55 +++++++++---- src/components/SideBar.jsx | 65 +++++++++++++++ src/components/movieCards.css | 31 ++++++-- src/components/sideBar.css | 15 ++++ 15 files changed, 388 insertions(+), 102 deletions(-) create mode 100644 src/components/FavMovies.jsx create mode 100644 src/components/SideBar.jsx create mode 100644 src/components/sideBar.css diff --git a/index.html b/index.html index 0abcfe5f..de73b4dd 100644 --- a/index.html +++ b/index.html @@ -3,6 +3,7 @@ + Flixster diff --git a/src/App.css b/src/App.css index 02f9967a..376d2ad8 100644 --- a/src/App.css +++ b/src/App.css @@ -17,12 +17,12 @@ width: 100%; } - .search-bar { + .searchBar { flex-direction: column; gap: 10px; } - .search-bar form { + .searchBar form { flex-direction: column; } } diff --git a/src/App.jsx b/src/App.jsx index 17a65816..9c34dda0 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useState, useEffect } from 'react' import './App.css' import Header from "./components/Header"; import.meta.env.VITE_API_KEY; @@ -7,19 +7,57 @@ import SearchScreen from './components/SearchScreen.jsx'; const App = () => { const [isSearching, setSearching] = useState(false); - const [criteria, setCriteria] = useState("") + const [criteria, setCriteria] = useState(""); + + const [isFave, setFave] = useState([]); + const [isWatched, setWatched] = useState([]); + + function loadFromLocalStorage() { + let savedLists = localStorage.getItem("userLists"); + if (savedLists == undefined || savedLists == null ||savedLists == "") { + return; + } + let ListsObject = JSON.parse(savedLists); + setFave(ListsObject.favList); + setWatched(ListsObject.watchedList); + } + + /** + * If the watchlist are now empty, check for stored (usually after a page load) + * else: put the current state into localstorage + */ + useEffect(() => { + if (isWatched.length == 0 && isFave.length == 0) { + loadFromLocalStorage() + } else { + localStorage.setItem("userLists", JSON.stringify( + { + favList: isFave, + watchedList: isWatched + } + )) + } + }, [isFave, isWatched]) const handleNewCriteria = (criteriaFromHeader) => { setCriteria(criteriaFromHeader) } + const handleSetFav = (newVal) => { + setFave(newVal); + } + + const handleSetWatched = (newVal) => { + setWatched(newVal); + } + return (
{!isSearching ? - + : - + }
); diff --git a/src/components/FavMovies.jsx b/src/components/FavMovies.jsx new file mode 100644 index 00000000..2276bd91 --- /dev/null +++ b/src/components/FavMovies.jsx @@ -0,0 +1,10 @@ +function FavMovies(props){ + return( +
+ +

{props.title}

+
+ ) +} + +export default FavMovies \ No newline at end of file diff --git a/src/components/Header.css b/src/components/Header.css index 0ee2c1bf..f85f9aa9 100644 --- a/src/components/Header.css +++ b/src/components/Header.css @@ -1,5 +1,5 @@ .header { - background-color: #ff0000; + background-color:red; padding: 10px 20px; display: flex; justify-content: space-between; @@ -11,7 +11,7 @@ margin: 0; } -.toggle-button { +.toggleButton { display: flex; align-items: center; } @@ -34,7 +34,7 @@ margin-right: 10px; } -.header .sort-by { +.header .sortBy { background-color: #ff0000; color: white; border: none; diff --git a/src/components/Header.jsx b/src/components/Header.jsx index 7cb696b2..30dc11bb 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -1,17 +1,14 @@ - -//import './Header.css'; // Import the CSS file for the header - -import React from 'react'; import './Header.css'; -function Header({ setSearching, sortMovies }) { +function Header({setSearching,sortMovies }) { + return (

Flixster

-
+
- sortMovies(e.target.value)}> diff --git a/src/components/ModalContainer.css b/src/components/ModalContainer.css index 127cc648..d39e5820 100644 --- a/src/components/ModalContainer.css +++ b/src/components/ModalContainer.css @@ -1,33 +1,86 @@ -.modal-overlay { - position: absolute; - top: 25%; - left: 25%; - background: rgba(0, 0, 0, 0.7); - width: 50%; - - - justify-content: center; - align-items: center; - z-index: 1000; - } - - - .modal-content { - background: white; - padding: 20px; - border-radius: 5px; - position: relative; - } - - .close-button { - color:black; - position: absolute; - top: 10px; - right: 10px; - background: none; - border: none; - font-size: 24px; - cursor: pointer; - } - - \ No newline at end of file +.modalOverlay { + position: fixed; + top: 0; + left: 0; + height: 100%; + width: 100%; + backdrop-filter: blur(5px); + overflow: auto; + background: rgba(0, 0, 0, 0.75); +} + +.modalContent { + background: #141414; + margin: auto; + margin-top: 10%; + padding: 20px; + border-radius: 10px; + width: 50%; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); + position: relative; + color: white; + overflow: auto; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +.modalContent div { + margin-bottom: 10px; +} + +.closeButton { + position: absolute; + top: 10px; + right: 10px; + background: none; + border: none; + font-size: 24px; + cursor: pointer; + color: white; +} + +.modalBody { + margin-top: 20px; +} + +.modalBody div { + margin-bottom: 10px; + font-size: 16px; +} + +.modalBody img { + border-radius: 5px; +} + +.modalBody div:first-child { + font-size: 24px; + font-weight: bold; + color: #e50914; +} + +.modalBody div:nth-child(3) { + font-size: 18px; + color: #e50914; +} + +.modalContent div:nth-child(4){ + color:white; +} + +.modalContent div:nth-child(5){ + color:white; +} + +button { + background-color: #e50914; + color: white; + border: none; + padding: 10px 20px; + border-radius: 5px; + cursor: pointer; + transition: background-color 0.3s; + font-weight: bold; +} + +button:hover { + background-color: #ff1e1e; +} \ No newline at end of file diff --git a/src/components/ModalContainer.jsx b/src/components/ModalContainer.jsx index 81a910a3..35948180 100644 --- a/src/components/ModalContainer.jsx +++ b/src/components/ModalContainer.jsx @@ -1,20 +1,50 @@ import "./ModalContainer.css"; -import React from "react"; +import {useEffect, useState} from "react"; function ModalContainer(props){ + const[trailerUrl, setTrailerUrl]= useState("") + const apiKey = import.meta.env.VITE_API_KEY; + + useEffect(()=> { + const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJkYWU5YTZiOThlMTBjZDkyZTcxN2Y4OWIzZDYxYjdjNSIsInN1YiI6IjY2NjY1MTQ1Y2M3MDc0ZDliNjFjMWM2ZiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.IDf1-04fWbMoc-zzed3BAcZLflL14UG-mdjcZobVjxA' + } + }; + + fetch(`https://api.themoviedb.org/3/movie/${props.data.id}/videos?api_key=${apiKey}`, options) + .then(response => response.json()) + .then(response => response.results.find( + (video) => video.site === "YouTube" && video.type === "Trailer" + )) + .then((trailer) => setTrailerUrl(`https://www.youtube.com/embed/${trailer.key}`)) + .catch(err => console.error(err)); + }, [props.data.id, apiKey]) + + return( -
-
- -
-
Title:{props.data.title}
- -
Original Title:
-
Overview: {props.data.overview}
-
Release Date: {props.data.releaseDate}
-
+
+
+ +
+
Title:{props.data.title}
+ +
Original Title: {props.data.title}
+
Overview: {props.data.overview}
+
Release Date: {props.data.releaseDate}
+
+ +
); diff --git a/src/components/MovieCards.jsx b/src/components/MovieCards.jsx index e921bfea..38f276de 100644 --- a/src/components/MovieCards.jsx +++ b/src/components/MovieCards.jsx @@ -1,10 +1,13 @@ import "./movieCards.css" import { useState } from "react"; import ModalContainer from "./ModalContainer"; + function MovieCard (props) { const[isModalOpen, setModal]=useState(false) - + const[isFav, setIsFave] = useState(false); + const[watched, setWatched]= useState(false) + function openModal(){ setModal(true) } @@ -12,17 +15,34 @@ function MovieCard (props) { { setModal(false) } + return( <> -
- +
+

{props.title}

Rating: {props.rating}

+
+
+ +
+
+ +
+
{isModalOpen ? : null} - - ); } diff --git a/src/components/MovieList.jsx b/src/components/MovieList.jsx index 2b1d8e2c..d3075bf9 100644 --- a/src/components/MovieList.jsx +++ b/src/components/MovieList.jsx @@ -1,10 +1,7 @@ -//import {useEffect, useState} from "react"; import MovieCards from "./MovieCards" import "./movieCards.css" function MovieList(props){ - - function createCards(card, index){ return( props.favMovies(card.id)} + watchedMovies={() =>props.watchedMovies(card.id)} /> ) } diff --git a/src/components/NowPlayingScreen.jsx b/src/components/NowPlayingScreen.jsx index c40b93af..b9495c0a 100644 --- a/src/components/NowPlayingScreen.jsx +++ b/src/components/NowPlayingScreen.jsx @@ -1,17 +1,33 @@ +/* eslint-disable react/prop-types */ import {useEffect, useState} from "react"; import MovieList from "./MovieList"; -import Header from "./Header" +import SideBar from "./SideBar"; +const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN; -function NowPlayingScreen({ criteriaFromHeader }){ - +function NowPlayingScreen({criteriaFromHeader, setFave, isFave, setWatched, isWatched}){ const [movies, setMovies]=useState([]); const [pageNumber, setPageNumber]=useState(1); const[url, setUrl]=useState(`https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${pageNumber}`) + function addToFave(movieId){ + if(isFave.includes(movieId)){ + setFave(prevIds => prevIds.filter(prevId => prevId !== movieId)) + + } else{ + setFave(prevId => [...prevId, movieId]) + } + } - const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN; + function addToWatched(movieId){ + if(isWatched.includes(movieId)){ + setWatched(prevIds => prevIds.filter(prevId => prevId !== movieId)) + } else { + setWatched(prevId => [...prevId, movieId]) + } + } + useEffect(()=>{ const options = { method: 'GET', @@ -25,6 +41,7 @@ function NowPlayingScreen({ criteriaFromHeader }){ .then(response => response.json()) .then(response => { handleSortMovies(criteriaFromHeader, movies.concat(response.results)) + }) .catch(err => console.error(err)); }, [url, pageNumber]); @@ -32,7 +49,6 @@ function NowPlayingScreen({ criteriaFromHeader }){ function loadMore(){ setPageNumber(prevPageNumber => prevPageNumber+1) setUrl(`https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${pageNumber + 1}`); - //useEffect(); } function handleSortMovies(criteria, parmaMovies = movies){ @@ -56,13 +72,13 @@ function NowPlayingScreen({ criteriaFromHeader }){ }, [criteriaFromHeader]) return( -
- +
+ +
) - } export default NowPlayingScreen; \ No newline at end of file diff --git a/src/components/SearchScreen.jsx b/src/components/SearchScreen.jsx index b83b44c0..8d016065 100644 --- a/src/components/SearchScreen.jsx +++ b/src/components/SearchScreen.jsx @@ -1,10 +1,34 @@ -import {useState} from "react"; +/* eslint-disable react/prop-types */ +import { useState} from "react"; import MovieList from "./MovieList"; +import SideBar from "./SideBar"; -function SearchScreen(){ +function SearchScreen({setFave, isFave, setWatched, isWatched}){ const[query, setSearchQuery]= useState('') const[movies, setMovies]= useState([]); const[pageNumber, setPageNumber]= useState(1) + + + function addToFave(movieId){ + if(isFave.includes(movieId)){ + setFave(prevIds => prevIds.filter(prevId => prevId !== movieId)) + + } else{ + setFave(prevId => [...prevId, movieId]) + } + } + + function addToWatched(movieId){ + if(isWatched.includes(movieId)){ + setWatched(prevIds => prevIds.filter(prevId => prevId !== movieId)) + + } else { + setWatched(prevId => [...prevId, movieId]) + } + } + + + const ACCESS_TOKEN = import.meta.env.VITE_ACCESS_TOKEN; function handleSearch(searchQuery){ @@ -23,23 +47,22 @@ function SearchScreen(){ } function loadMore(){ - setPageNumber(prevPageNumber => prevPageNumber+1) - + setPageNumber(prevPageNumber => prevPageNumber+1) } return( -
- setSearchQuery(e.target.value.toLowerCase())} - /> - - - - - +
+ + setSearchQuery(e.target.value.toLowerCase())} + /> + + + +
) } diff --git a/src/components/SideBar.jsx b/src/components/SideBar.jsx new file mode 100644 index 00000000..97037543 --- /dev/null +++ b/src/components/SideBar.jsx @@ -0,0 +1,65 @@ +/* eslint-disable react/prop-types */ +import "./sideBar.css"; +import FavMovies from "./FavMovies"; +import { useEffect, useState } from "react"; + +function SideBar(props) { + const [favMovieElements, setFavMovieElements] = useState([]); + const [watchedMovieElements, setWatchedMovieElements] = useState([]); + + async function getMovie(movieId) { + const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJkYWU5YTZiOThlMTBjZDkyZTcxN2Y4OWIzZDYxYjdjNSIsInN1YiI6IjY2NjY1MTQ1Y2M3MDc0ZDliNjFjMWM2ZiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.IDf1-04fWbMoc-zzed3BAcZLflL14UG-mdjcZobVjxA' + } + }; + + let response = await fetch(`https://api.themoviedb.org/3/movie/${movieId}`, options) + response = response.json(); + return response; + } + + async function MovieCard(movieId) { + + + const movieIndex = props.movies.findIndex((item) => item.id === movieId) + let movie; + if (movieIndex == -1) { + movie = await getMovie(movieId); + } else { + movie = props.movies[movieIndex] + } + + return movie; + } + + + useEffect(() => { + Promise.all(props.fave.map((id) => (MovieCard(id)))).then((values) => { + setFavMovieElements(values) + }); + Promise.all(props.watched.map((id) => (MovieCard(id)))).then((values) => { + setWatchedMovieElements(values) + }); + + + }, [props.watched, props.fave]) + + return ( +
+

Favorites

+ {favMovieElements.map((movie, index) => { + return () + })} +

Watched

+ {watchedMovieElements.map((movie, index) => { + return () + })} + +
+ ); +}; + +export default SideBar; \ No newline at end of file diff --git a/src/components/movieCards.css b/src/components/movieCards.css index 57ddccc7..26cb8650 100644 --- a/src/components/movieCards.css +++ b/src/components/movieCards.css @@ -1,6 +1,4 @@ - - -.movie-card { +.movieCard { background-color: #ff0000; color: white; width: 200px; @@ -9,22 +7,32 @@ border-radius: 10px; text-align: center; } -.movie-card:hover { +.movieCard:hover { transform: scale(1.0); box-shadow: 0 7px 14px black; } -.movie-card img { +.movieCard img { width: 100%; border-radius: 10px; } -.movie-card h2 { +.favoriteWatchedIcon{ + display:flex; +} +.favoriteWatchedIcon button{ + background-color:red; + cursor: pointer; + font-size: 16px; + font-weight: bold; + transition: background-color 0.3s ease; +} +.movieCard h2 { font-size: 18px; margin: 10px 0 5px; } -.movie-card p { +.movieCard p { margin: 5px 0; } @@ -33,4 +41,11 @@ flex-wrap: wrap; justify-content: center; padding: 20px; -} \ No newline at end of file + margin-left: 13vw; +} + +.favoriteIcon { + cursor: pointer; + font-size: 15px; + color: #ffd700; /* Gold color for favorite star */ + } \ No newline at end of file diff --git a/src/components/sideBar.css b/src/components/sideBar.css new file mode 100644 index 00000000..f7f370fd --- /dev/null +++ b/src/components/sideBar.css @@ -0,0 +1,15 @@ +.sidebar { + height: 100vh; + width: 250px; + position: fixed; + top: 0; + left: 0; + background-color: red; + padding-top: 20px; + overflow: auto; + } + + .sidebar h2 { + color: white; + text-align: center; + } \ No newline at end of file From 05940457150206c66231b767fd6046467aef563b Mon Sep 17 00:00:00 2001 From: Toby Gabriella <136364857+tobygabriella@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:15:40 -0700 Subject: [PATCH 06/11] Update README.md --- README.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f768e33f..36ef0bf0 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,72 @@ -# React + Vite +## Unit Assignment: Flixster -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. +Submitted by: Gabriella -Currently, two official plugins are available: +Estimated time spent: 25 hours spent in total -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +Deployed Application (optional): [Flixster Deployed Site]((https://flixster-starter-jpsb.onrender.com)) + +### Application Features + +#### CORE FEATURES + + +- [X] **Display Movies** + - [X] Users can view a list of current movies from The Movie Database API. + - [X] For each movie displayed, users can see its title, poster image, and votes. + - [X] Users can load more current movies by clicking a button at the bottom of the list (page should not be reloaded). +- [X] **Search Functionality** + - [X] Users can search for movies and view the results in a grid. + - [X] Users can clear results and view previous current movies displayed. +- [X] **Accessibility Features** + - [X] Website implements accessibility features (semantic HTML, color contrast, font sizing, alt text for images). +- [X] **Responsive Design** + - [X] Website implements responsive web design. +- [X] **Movie Details** + - [X] Users can view more details about a movie in a popup, such as runtime in minutes, backdrop poster, release date, genres, and/or an overview. +- [X] **Sorting Options** + - [X] Users can click on a filter by drop down to sort product by type (alphabetic, release date, rating). +- [X] **Layout** + - [X] Website displays header, banner, search, movie grid, about, contact, and footer section. + +#### STRETCH FEATURES + +- [X] **Deployment** + - [X] Website is deployed via Render. +- [X] **Embedded Movie Trailers** + - [X] Within the popup displaying a movie's details, users can play the movie trailer. +- [X] **Watched Checkbox** + - [X] For each movie displayed, users can mark the movie as watched. +- [X] **Favorite Button** + - [X] For each movie displayed, users can favorite the movie. +- [X] **Sidebar** + - [X] Users can open a sidebar + - [X] The sidebar displays the user's favorited and watched movies + +### Walkthrough Video + +`TODO://` Add the embedded URL code to your animated app walkthrough below, `ADD_EMBEDDED_CODE_HERE`. Make sure the video or gif actually renders and animates when viewing this README. (🚫 Remove this paragraph after adding walkthrough video) + +`ADD_EMBEDDED_CODE_HERE` + +### Reflection + +* Did the topics discussed in your labs prepare you to complete the assignment? Be specific, which features in your weekly assignment did you feel unprepared to complete? + +Add your response here + +* If you had more time, what would you have done differently? Would you have added additional features? Changed the way your project responded to a particular event, etc. + +Add your response here + +* Reflect on your project demo, what went well? Were there things that maybe didn't go as planned? Did you notice something that your peer did that you would like to try next time? + +Add your response here + +### Open-source libraries used + +- Add any links to open-source libraries used in your project. + +### Shout out + +Give a shout out to somebody from your cohort that especially helped you during your project. This can be a fellow peer, instructor, TA, mentor, etc. From 67b3ab1c833dc7332b14502ca9ca72eaa8eb849c Mon Sep 17 00:00:00 2001 From: Toby Gabriella <136364857+tobygabriella@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:16:45 -0700 Subject: [PATCH 07/11] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 36ef0bf0..2d76b918 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Submitted by: Gabriella Estimated time spent: 25 hours spent in total -Deployed Application (optional): [Flixster Deployed Site]((https://flixster-starter-jpsb.onrender.com)) +Deployed Application (optional): [Flixster Deployed Site](https://flixster-starter-jpsb.onrender.com) ### Application Features From e895377ae4dcd26f09b4e449bd5224e1f2465e97 Mon Sep 17 00:00:00 2001 From: Toby Gabriella <136364857+tobygabriella@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:25:47 -0700 Subject: [PATCH 08/11] Update README.md --- README.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2d76b918..3e09ad3d 100644 --- a/README.md +++ b/README.md @@ -44,24 +44,21 @@ Deployed Application (optional): [Flixster Deployed Site](https://flixster-start - [X] The sidebar displays the user's favorited and watched movies ### Walkthrough Video - -`TODO://` Add the embedded URL code to your animated app walkthrough below, `ADD_EMBEDDED_CODE_HERE`. Make sure the video or gif actually renders and animates when viewing this README. (🚫 Remove this paragraph after adding walkthrough video) - -`ADD_EMBEDDED_CODE_HERE` +`[ADD_EMBEDDED_CODE_HERE](https://www.loom.com/share/fed1b3fdf9c64469ac9084acf1505b09?sid=b6ae632f-f6f4-4099-ac12-2f8b36bd2abf)` ### Reflection * Did the topics discussed in your labs prepare you to complete the assignment? Be specific, which features in your weekly assignment did you feel unprepared to complete? -Add your response here +Yes the labs did prepare me * If you had more time, what would you have done differently? Would you have added additional features? Changed the way your project responded to a particular event, etc. -Add your response here +I would have added users be able to click on the movies in the favorite tab and the modal for the movie dsplays. * Reflect on your project demo, what went well? Were there things that maybe didn't go as planned? Did you notice something that your peer did that you would like to try next time? -Add your response here +I think being able to do most of the stretch feautres was the best thing. One thing that did not go as planned was having to move code around because i didnt understand passing props properling and how states should be higher up in componenets that need it. ### Open-source libraries used @@ -69,4 +66,4 @@ Add your response here ### Shout out -Give a shout out to somebody from your cohort that especially helped you during your project. This can be a fellow peer, instructor, TA, mentor, etc. +Shout out to Theo From 78d612ee169db51015e7fdded6d02a3752bdc9d2 Mon Sep 17 00:00:00 2001 From: Toby Gabriella <136364857+tobygabriella@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:26:22 -0700 Subject: [PATCH 09/11] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3e09ad3d..dbe9bb29 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Deployed Application (optional): [Flixster Deployed Site](https://flixster-start - [X] The sidebar displays the user's favorited and watched movies ### Walkthrough Video -`[ADD_EMBEDDED_CODE_HERE](https://www.loom.com/share/fed1b3fdf9c64469ac9084acf1505b09?sid=b6ae632f-f6f4-4099-ac12-2f8b36bd2abf)` +https://www.loom.com/share/fed1b3fdf9c64469ac9084acf1505b09?sid=b6ae632f-f6f4-4099-ac12-2f8b36bd2abf ### Reflection From 5338fa4704e01784bfb8123a19d6a5ebfd0c192d Mon Sep 17 00:00:00 2001 From: Gabriella Gabriel Toby Date: Fri, 14 Jun 2024 19:32:11 -0700 Subject: [PATCH 10/11] added footer --- src/App.jsx | 2 ++ src/components/Footer.css | 59 +++++++++++++++++++++++++++++++++++++++ src/components/Footer.jsx | 29 +++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 src/components/Footer.css create mode 100644 src/components/Footer.jsx diff --git a/src/App.jsx b/src/App.jsx index 9c34dda0..ad60a4c5 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -4,6 +4,7 @@ import Header from "./components/Header"; import.meta.env.VITE_API_KEY; import NowPlayingScreen from './components/NowPlayingScreen'; import SearchScreen from './components/SearchScreen.jsx'; +import Footer from './components/Footer.jsx'; const App = () => { const [isSearching, setSearching] = useState(false); @@ -59,6 +60,7 @@ const App = () => { : } +
); } diff --git a/src/components/Footer.css b/src/components/Footer.css new file mode 100644 index 00000000..a95a4929 --- /dev/null +++ b/src/components/Footer.css @@ -0,0 +1,59 @@ +.footer { + background-color: #141414; + color: white; + padding: 20px 0; + text-align: center; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +.footer-content { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 20px; +} + +.footer-links { + display: flex; + flex-wrap: wrap; + justify-content: center; + margin-bottom: 20px; +} + +.footer-link { + color: #e50914; + margin: 0 10px; + text-decoration: none; + transition: color 0.3s ease; +} + +.footer-link:hover { + color: #ff1e1e; +} + +.footer-social { + margin-bottom: 20px; +} + +.social-link { + color: white; + margin: 0 10px; + text-decoration: none; + font-size: 18px; + transition: color 0.3s ease; +} + +.social-link:hover { + color: #e50914; +} + +.footer-bottom { + margin-top: 20px; +} + +.footer-bottom p { + margin: 0; + font-size: 14px; + color: #888; +} \ No newline at end of file diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx new file mode 100644 index 00000000..9e03c515 --- /dev/null +++ b/src/components/Footer.jsx @@ -0,0 +1,29 @@ +import './Footer.css'; + +const Footer = () => { + return ( + + ); +}; + +export default Footer; \ No newline at end of file From 3fc84c0f5189a2914e8d8b4a9242317d7d388541 Mon Sep 17 00:00:00 2001 From: Toby Gabriella <136364857+tobygabriella@users.noreply.github.com> Date: Fri, 14 Jun 2024 19:46:25 -0700 Subject: [PATCH 11/11] Update README.md --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dbe9bb29..c019ce6c 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,14 @@ Deployed Application (optional): [Flixster Deployed Site](https://flixster-start - [X] The sidebar displays the user's favorited and watched movies ### Walkthrough Video -https://www.loom.com/share/fed1b3fdf9c64469ac9084acf1505b09?sid=b6ae632f-f6f4-4099-ac12-2f8b36bd2abf + ### Reflection