From 978eac8370cca6d1f9c04f3ef820fae972ec341b Mon Sep 17 00:00:00 2001 From: Paloma Levy Date: Mon, 9 Jun 2025 17:18:45 -0700 Subject: [PATCH 01/16] Connected API and formatted the header with search and sort by functions --- .env | 3 +++ src/App.jsx | 51 ++++++++++++++++++++++++++++++++++++++++-- src/utils/fetchData.js | 13 +++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 .env create mode 100644 src/utils/fetchData.js diff --git a/.env b/.env new file mode 100644 index 00000000..1372a489 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +VITE_API_KEY=eab5a29d82df8bc98787928fe208add7 + +VITE_AUTH_KEY=eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJlYWI1YTI5ZDgyZGY4YmM5ODc4NzkyOGZlMjA4YWRkNyIsIm5iZiI6MTc0OTUwNjIxNS45NDQsInN1YiI6IjY4NDc1OGE3ZDk5NGYxOTE4ZjlmNTFkMSIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.MwrlIdKbfWshfm4H2CkfSUe3zNPCHCSxyF1-m3bqEmc \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index dfa91584..5d77237e 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,12 +1,59 @@ -import { useState } from 'react' +import { useEffect, useState } from 'react' import './App.css' +const ViteAPI = import.meta.env.VITE_API_KEY; +const ViteAUTH = import.meta.env.VITE_AUTH_KEY; +import './utils/fetchData.js' +console.log(ViteAPI); +console.log(ViteAUTH); + +// get movies from fetch data file +// looping through movies using movies.map to get the props const App = () => { return (
- + {/* */} +
) } +// header component +const Header = () => { + return ( + <> +

Flixster

+ + + + ) +} + +function SearchForm() { + return ( +
+ + +
+ ) +} +function SortBy() { + return ( +
+ +
+ ) +} +// movie card component +const MovieCard = (props) => { + return ( +
+

Movie Title: {props.original_title}

+
+ ) +} export default App diff --git a/src/utils/fetchData.js b/src/utils/fetchData.js new file mode 100644 index 00000000..0ac9b7b4 --- /dev/null +++ b/src/utils/fetchData.js @@ -0,0 +1,13 @@ +const url = 'https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=1'; +const options = { + method: 'GET', + headers: { + accept: 'application/json', + Authorization: `Bearer ${import.meta.env.VITE_AUTH_KEY}` + } +}; + +const data = fetch(url, options) + .then(res => res.json()) + .then(json => console.log(json)) + .catch(err => console.error(err)); \ No newline at end of file From 4fa11084b9e87033467a7c43cfa187d26a26893e Mon Sep 17 00:00:00 2001 From: Paloma Levy Date: Mon, 9 Jun 2025 22:51:41 -0700 Subject: [PATCH 02/16] fetched data from the API --- src/App.css | 10 ++++++++++ src/App.jsx | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/App.css b/src/App.css index 0bf65669..4951ad11 100644 --- a/src/App.css +++ b/src/App.css @@ -26,3 +26,13 @@ flex-direction: column; } } + +.App-MovieCard { + display: flex; + flex-direction: row; +} + +img { + display: flex; + width: 200px; +} \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 5d77237e..56fe42a3 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -12,8 +12,12 @@ console.log(ViteAUTH); const App = () => { return (
- {/* */} -
+
+
+
+
+ +
) } @@ -48,12 +52,31 @@ function SortBy() { ) } + // movie card component -const MovieCard = (props) => { +const MovieCard = () => { + const [movies, setMovies] = useState([]); + + useEffect(() => { + fetch(`https://api.themoviedb.org/3/movie/popular?api_key=${ViteAPI}`) + .then(response => response.json()) + .then(data => setMovies(data.results)); + }, []); + return ( -
-

Movie Title: {props.original_title}

+
+ {movies.map((movie) => ( +
+

{movie.title}

+ {movie.title} + {/*

{movie.overview}

*/} +

Release Date: {movie.release_date}

+

Vote Average: {movie.vote_average}

+
+ ))}
- ) -} + ); +}; + + export default App From 692004bf039e017de38351ce7416bbb8ee22a153 Mon Sep 17 00:00:00 2001 From: Paloma Levy Date: Tue, 10 Jun 2025 07:42:26 -0700 Subject: [PATCH 03/16] formatted movie cards --- src/App.css | 8 +++++++- src/App.jsx | 18 +++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/App.css b/src/App.css index 4951ad11..87192a9d 100644 --- a/src/App.css +++ b/src/App.css @@ -30,9 +30,15 @@ .App-MovieCard { display: flex; flex-direction: row; + flex-wrap: wrap; + + .App-MovieContainer { + display: flex; + flex-direction: row; + border: solid black 1px; + } } img { - display: flex; width: 200px; } \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 56fe42a3..8381f93b 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -15,9 +15,7 @@ const App = () => {
-
-
) } @@ -64,14 +62,16 @@ const MovieCard = () => { }, []); return ( -
+
{movies.map((movie) => ( -
-

{movie.title}

- {movie.title} - {/*

{movie.overview}

*/} -

Release Date: {movie.release_date}

-

Vote Average: {movie.vote_average}

+
+
+

{movie.title}

+ {movie.title} + {/*

{movie.overview}

*/} +

Release Date: {movie.release_date}

+

Vote Average: {movie.vote_average}

+
))}
From 43fd396e36f4f051e0546c27f402694fbe6d31c5 Mon Sep 17 00:00:00 2001 From: Paloma Levy Date: Tue, 10 Jun 2025 12:51:58 -0700 Subject: [PATCH 04/16] updated format of movie cards and header --- README.md | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/App.css | 25 +++++++++- src/App.jsx | 49 +++++++++++++------- 3 files changed, 181 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index f768e33f..4dde4984 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,127 @@ -# 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: **Paloma Levy** -Currently, two official plugins are available: +Estimated time spent: **#** 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 (**required**): [Flixster Deployed Site](ADD_LINK_HERE) + +### Application Features + +#### REQUIRED FEATURES + +- [ ] **Display Movies** + - [x] Users can view a list of current movies from The Movie Database API in a grid view. + - [x] Movie tiles should be reasonably sized (at least 6 playlists on your laptop when full screen; large enough that the playlist components detailed in the next feature are legible). + - [x] For each movie displayed, users can see the movie's: + - [x] Title + - [x] Poster image + - [x] Vote average + - [ ] Users can load more current movies by clicking a button which adds more movies to the grid without reloading the entire page. +- [ ] **Search Functionality** + - [ ] Users can use a search bar to search for movies by title. + - [x] The search bar should include: + - [x] Text input field + - [x] Submit/Search button + - [x] Clear button + - [ ] Movies with a title containing the search query in the text input field are displayed in a grid view when the user either: + - [ ] Presses the Enter key + - [ ] Clicks the Submit/Search button + - [ ] Users can click the Clear button. When clicked: + - [ ] Most recent search results are cleared from the text input field and the grid view and all current movies are displayed in a grid view +- [ ] **Design Features** + - [ ] Website implements all of the following accessibility features: + - [ ] Semantic HTML + - [ ] [Color contrast](https://webaim.org/resources/contrastchecker/) + - [ ] Alt text for images + - [ ] Website implements responsive web design. + - [ ] Uses CSS Flexbox or CSS Grid + - [ ] Movie tiles and images shrink/grow in response to window size + - [ ] Users can click on a movie tile to view more details about a movie in a pop-up modal. + - [ ] The pop-up window is centered in the screen and does not occupy the entire screen. + - [ ] The pop-up window has a shadow to show that it is a pop-up and appears floating on the screen. + - [ ] The backdrop of the pop-up appears darker or in a different shade than before. including: + - [ ] The pop-up displays additional details about the moving including: + - [ ] Runtime in minutes + - [ ] Backdrop poster + - [ ] Release date + - [ ] Genres + - [ ] An overview + - [ ] Users can use a drop-down menu to sort movies. + - [ ] Drop-down allows movies to be sorted by: + - [ ] Title (alphabetic, A-Z) + - [ ] Release date (chronologically, most recent to oldest) + - [ ] Vote average (descending, highest to lowest) + - [ ] When a sort option is clicked, movies display in a grid according to selected criterion. + - [ ] Website displays: + - [ ] Header section + - [ ] Banner section + - [ ] Search bar + - [ ] Movie grid + - [ ] Footer section + - [ ] **VIDEO WALKTHROUGH SPECIAL INSTRUCTIONS**: To ease the grading process, please use the [color contrast checker](https://webaim.org/resources/contrastchecker/) to demonstrate to the grading team that text and background colors on your website have appropriate contrast. The Contrast Ratio should be above 4.5:1 and should have a green box surrounding it. + - [ ] **Deployment** + - [ ] Website is deployed via Render. + - [ ] **VIDEO WALKTHROUGH SPECIAL INSTRUCTIONS**: For ease of grading, please use the deployed version of your website when creating your walkthrough. + +#### STRETCH FEATURES + + +- [ ] **Embedded Movie Trailers** + - [ ] Within the pop-up modal displaying a movie's details, the movie trailer is viewable. + - [ ] When the trailer is clicked, users can play the movie trailer. +- [ ] **Favorite Button** + - [ ] For each movie displayed, users can favorite the movie. + - [ ] There should be visual element (such as a heart icon) on each movie's tile to show whether or not the movie has been favorited. + - [ ] If the movie is not favorited: + - [ ] Clicking on the visual element should mark the movie as favorited + - [ ] There should be visual feedback (such as the heart turning a different color) to show that the movie has been favorited by the user. + - [ ] If the movie is already favorited: + - [ ] Clicking on the visual element should mark the movie as *not* favorited. + - [ ] There should be visual feedback (such as the heart turning a different color) to show that the movie has been unfavorited. +- [ ] **Watched Checkbox** + - [ ] For each movie displayed, users can mark the movie as watched. + - [ ] There should be visual element (such as an eye icon) on each movie's tile to show whether or not the movie has been watched. + - [ ] If the movie has not been watched: + - [ ] Clicking on the visual element should mark the movie as watched + - [ ] There should be visual feedback (such as the eye turning a different color) to show that the movie has been watched by the user. + - [ ] If the movie is already watched: + - [ ] Clicking on the visual element should mark the movie as *not* watched. + - [ ] There should be visual feedback (such as the eye turning a different color) to show that the movie has not been watched. +- [ ] **Sidebar** + - [ ] The website includes a side navigation bar. + - [ ] The sidebar has three pages: + - [ ] Home + - [ ] Favorites + - [ ] Watched + - [ ] The Home page displays all current movies in a grid view, the search bar, and the sort movies drop-down. + - [ ] The Favorites page displays all favorited movies in a grid view. + - [ ] The Watched page displays all watched movies in a grid view. + +### 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. Ensure your walkthrough showcases the presence and/or functionality of all features you implemented above (check them off as you film!). Pay attention to any **VIDEO WALKTHROUGH SPECIAL INSTRUCTIONS** checkboxes listed above to ensure graders see the full functionality of your website! (🚫 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. \ No newline at end of file diff --git a/src/App.css b/src/App.css index 87192a9d..1fdd1889 100644 --- a/src/App.css +++ b/src/App.css @@ -10,6 +10,14 @@ justify-content: space-evenly; color: white; padding: 20px; + + .sortByButton { + background-color:rgb(255, 255, 255); + border-radius: 5px; + width: 70px; + padding: 1px; + border: none; + } } @media (max-width: 600px) { @@ -31,14 +39,29 @@ display: flex; flex-direction: row; flex-wrap: wrap; + gap: 20px; + justify-content: center; + padding-top: 20px; .App-MovieContainer { display: flex; flex-direction: row; - border: solid black 1px; + border-radius: 5px; + font-size: x-small; + width: 200px; + background-color:rgb(229, 229, 229); + box-shadow: 0px 5px 5px rgba(36, 36, 36, 0.5); + } + .App-MovieContainer:hover { + box-shadow: 0px 0px 20px rgba(36, 36, 36, 0.5); + cursor: pointer; + transform: scale(1.03); } } + img { width: 200px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; } \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 8381f93b..99e418ee 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -7,8 +7,6 @@ import './utils/fetchData.js' console.log(ViteAPI); console.log(ViteAUTH); -// get movies from fetch data file -// looping through movies using movies.map to get the props const App = () => { return (
@@ -19,22 +17,23 @@ const App = () => {
) } -// header component +// HEADER COMPONENT START const Header = () => { return ( - <> +

Flixster

- +
) } function SearchForm() { return (
- + +
) } @@ -42,16 +41,17 @@ function SearchForm() { function SortBy() { return (
- + - +
) } - -// movie card component +// HEADER COMPONENT END +// MOVIE COMPONENT START const MovieCard = () => { const [movies, setMovies] = useState([]); @@ -61,22 +61,39 @@ const MovieCard = () => { .then(data => setMovies(data.results)); }, []); + // const openModal = () => { + // console.log('hello') + // } + + // const [movieModal, setMovieModal] = useState(false) + // const visible = () => setMovieModal(true) + // const notVisible = () => setMovieModal(false) + return ( + // get movies from fetch data file + // looping through movies using movies.map to get the props
{movies.map((movie) => ( -
-
+ // <> +
+
+ {movie.title} +

{movie.title}

+

Release Date: {movie.release_date}

+

Vote Average: {movie.vote_average}

+
+
+ /*

{movie.title}

{movie.title} - {/*

{movie.overview}

*/}

Release Date: {movie.release_date}

-

Vote Average: {movie.vote_average}

+

Overview: {movie.overview}

-
+ */ ))}
); }; - +// MOVIE COMPONENT END export default App From 7a5dd5069496f05c5cc90e035e622f8562cf513e Mon Sep 17 00:00:00 2001 From: Paloma Levy Date: Tue, 10 Jun 2025 18:34:04 -0700 Subject: [PATCH 05/16] successfully added the modal with more information about the movie --- README.md | 4 +++- src/App.css | 29 +++++++++++++++++++++++ src/App.jsx | 52 ++++++++++++++++++++++++------------------ src/utils/fetchData.js | 5 +++- 4 files changed, 66 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 4dde4984..94f562f2 100644 --- a/README.md +++ b/README.md @@ -124,4 +124,6 @@ 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. \ No newline at end of file +- Pedro +- Danny (TA) +- Greg (TA) \ No newline at end of file diff --git a/src/App.css b/src/App.css index 1fdd1889..58e0009c 100644 --- a/src/App.css +++ b/src/App.css @@ -59,6 +59,35 @@ } } +.popup { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: #fff; + padding: 20px; + border: 1px solid #ddd; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); + width: 80%; + max-width: 600px; + z-index: 10; +} +.overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 9; +} +.visible { + display: block; +} +.notVisible { + display: none; +} img { width: 200px; diff --git a/src/App.jsx b/src/App.jsx index 99e418ee..33495023 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,10 +2,7 @@ import { useEffect, useState } from 'react' import './App.css' const ViteAPI = import.meta.env.VITE_API_KEY; const ViteAUTH = import.meta.env.VITE_AUTH_KEY; -import './utils/fetchData.js' - -console.log(ViteAPI); -console.log(ViteAUTH); +import './utils/fetchData'; const App = () => { return ( @@ -14,6 +11,8 @@ const App = () => {
+ {/* */} +
) } @@ -51,9 +50,11 @@ function SortBy() { ) } // HEADER COMPONENT END + // MOVIE COMPONENT START const MovieCard = () => { const [movies, setMovies] = useState([]); + const [pageNumber, setPageNumber] = useState(1); useEffect(() => { fetch(`https://api.themoviedb.org/3/movie/popular?api_key=${ViteAPI}`) @@ -61,39 +62,46 @@ const MovieCard = () => { .then(data => setMovies(data.results)); }, []); - // const openModal = () => { - // console.log('hello') - // } - - // const [movieModal, setMovieModal] = useState(false) - // const visible = () => setMovieModal(true) - // const notVisible = () => setMovieModal(false) + const [showModal, setShowModal] = useState(false) + const [selectedMovie, setSelectedMovie] = useState([]) return ( // get movies from fetch data file // looping through movies using movies.map to get the props
{movies.map((movie) => ( - // <> -
-
+
{ + console.log('click worked') + setShowModal(true) + setSelectedMovie(movie) + }} className="App-MovieContainer"> +
{movie.title}

{movie.title}

Release Date: {movie.release_date}

Vote Average: {movie.vote_average}

- /*
-

{movie.title}

- {movie.title} -

Release Date: {movie.release_date}

-

Overview: {movie.overview}

-
- */ ))} +
+

{selectedMovie.title}

+ {selectedMovie.title} +

Release Date: {selectedMovie.release_date}

+

Overview: {selectedMovie.overview}

+ +
+
); }; // MOVIE COMPONENT END - + +//LOAD MORE COMPONENT START + +// const [pageNumber, setPageNumber] = useState(0); + +// fetch (``) + + +//LOAD MORE COMPONENT END export default App diff --git a/src/utils/fetchData.js b/src/utils/fetchData.js index 0ac9b7b4..d13c07f4 100644 --- a/src/utils/fetchData.js +++ b/src/utils/fetchData.js @@ -10,4 +10,7 @@ const options = { const data = fetch(url, options) .then(res => res.json()) .then(json => console.log(json)) - .catch(err => console.error(err)); \ No newline at end of file + .catch(err => console.error(err)); + + +// const url = 'https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=1'; \ No newline at end of file From ff0ac65d967ec8a55811498c658f30b4c91da77a Mon Sep 17 00:00:00 2001 From: Paloma Levy Date: Wed, 11 Jun 2025 12:19:49 -0700 Subject: [PATCH 06/16] reorganized App.jsx into separate components for visibility --- README.md | 36 ++++++------ src/App.css | 11 ++++ src/App.jsx | 91 +++---------------------------- src/components/Header.jsx | 14 +++++ src/components/MovieCard.jsx | 51 +++++++++++++++++ src/components/PopupModal.jsx | 18 ++++++ src/components/SearchFunction.jsx | 31 +++++++++++ src/components/SortBy.jsx | 15 +++++ 8 files changed, 165 insertions(+), 102 deletions(-) create mode 100644 src/components/Header.jsx create mode 100644 src/components/MovieCard.jsx create mode 100644 src/components/PopupModal.jsx create mode 100644 src/components/SearchFunction.jsx create mode 100644 src/components/SortBy.jsx diff --git a/README.md b/README.md index 94f562f2..c6a5e996 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,14 @@ Deployed Application (**required**): [Flixster Deployed Site](ADD_LINK_HERE) #### REQUIRED FEATURES -- [ ] **Display Movies** +- [x] **Display Movies** - [x] Users can view a list of current movies from The Movie Database API in a grid view. - [x] Movie tiles should be reasonably sized (at least 6 playlists on your laptop when full screen; large enough that the playlist components detailed in the next feature are legible). - [x] For each movie displayed, users can see the movie's: - [x] Title - [x] Poster image - [x] Vote average - - [ ] Users can load more current movies by clicking a button which adds more movies to the grid without reloading the entire page. + - [x] Users can load more current movies by clicking a button which adds more movies to the grid without reloading the entire page. - [ ] **Search Functionality** - [ ] Users can use a search bar to search for movies by title. - [x] The search bar should include: @@ -35,18 +35,18 @@ Deployed Application (**required**): [Flixster Deployed Site](ADD_LINK_HERE) - [ ] [Color contrast](https://webaim.org/resources/contrastchecker/) - [ ] Alt text for images - [ ] Website implements responsive web design. - - [ ] Uses CSS Flexbox or CSS Grid + - [x] Uses CSS Flexbox or CSS Grid - [ ] Movie tiles and images shrink/grow in response to window size - - [ ] Users can click on a movie tile to view more details about a movie in a pop-up modal. - - [ ] The pop-up window is centered in the screen and does not occupy the entire screen. - - [ ] The pop-up window has a shadow to show that it is a pop-up and appears floating on the screen. - - [ ] The backdrop of the pop-up appears darker or in a different shade than before. including: - - [ ] The pop-up displays additional details about the moving including: - - [ ] Runtime in minutes - - [ ] Backdrop poster - - [ ] Release date + - [x] Users can click on a movie tile to view more details about a movie in a pop-up modal. + - [x] The pop-up window is centered in the screen and does not occupy the entire screen. + - [x] The pop-up window has a shadow to show that it is a pop-up and appears floating on the screen. + - [x] The backdrop of the pop-up appears darker or in a different shade than before. including: + - [x] The pop-up displays additional details about the moving including: + - [x] Runtime in minutes + - [x] Backdrop poster + - [x] Release date - [ ] Genres - - [ ] An overview + - [x] An overview - [ ] Users can use a drop-down menu to sort movies. - [ ] Drop-down allows movies to be sorted by: - [ ] Title (alphabetic, A-Z) @@ -54,10 +54,10 @@ Deployed Application (**required**): [Flixster Deployed Site](ADD_LINK_HERE) - [ ] Vote average (descending, highest to lowest) - [ ] When a sort option is clicked, movies display in a grid according to selected criterion. - [ ] Website displays: - - [ ] Header section - - [ ] Banner section - - [ ] Search bar - - [ ] Movie grid + - [x] Header section + - [x] Banner section + - [x] Search bar + - [x] Movie grid - [ ] Footer section - [ ] **VIDEO WALKTHROUGH SPECIAL INSTRUCTIONS**: To ease the grading process, please use the [color contrast checker](https://webaim.org/resources/contrastchecker/) to demonstrate to the grading team that text and background colors on your website have appropriate contrast. The Contrast Ratio should be above 4.5:1 and should have a green box surrounding it. - [ ] **Deployment** @@ -124,6 +124,6 @@ Add your response here ### Shout out -- Pedro - Danny (TA) -- Greg (TA) \ No newline at end of file +- Greg (TA) +- Oscar Portillo diff --git a/src/App.css b/src/App.css index 58e0009c..3a4dedfd 100644 --- a/src/App.css +++ b/src/App.css @@ -89,6 +89,17 @@ display: none; } +.loadButton { + display: flex; + justify-content: center; + align-items: center; +} + +.favoriteButton { + background: transparent; + border: none; +} + img { width: 200px; border-top-left-radius: 5px; diff --git a/src/App.jsx b/src/App.jsx index 33495023..fa623a5a 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,8 +1,9 @@ import { useEffect, useState } from 'react' import './App.css' -const ViteAPI = import.meta.env.VITE_API_KEY; const ViteAUTH = import.meta.env.VITE_AUTH_KEY; import './utils/fetchData'; +import Header from './components/Header'; +import MovieCard from './components/MovieCard'; const App = () => { return ( @@ -11,97 +12,19 @@ const App = () => {
- {/* */} - -
- ) -} -// HEADER COMPONENT START -const Header = () => { - return ( -
-

Flixster

- -
) } -function SearchForm() { - return ( -
- - - -
- ) -} -function SortBy() { - return ( -
- -
- ) +// SEARCH FORM COMPONENT START +function SearchForm( {onQueryChange} ) { + const [searchQuery, setSearchQuery] = useState(''); + const [filteredMovies, setFilteredMovies] = useState([]); } -// HEADER COMPONENT END - -// MOVIE COMPONENT START -const MovieCard = () => { - const [movies, setMovies] = useState([]); - const [pageNumber, setPageNumber] = useState(1); - - useEffect(() => { - fetch(`https://api.themoviedb.org/3/movie/popular?api_key=${ViteAPI}`) - .then(response => response.json()) - .then(data => setMovies(data.results)); - }, []); +// SEARCH FORM COMPONENT END - const [showModal, setShowModal] = useState(false) - const [selectedMovie, setSelectedMovie] = useState([]) - return ( - // get movies from fetch data file - // looping through movies using movies.map to get the props -
- {movies.map((movie) => ( -
{ - console.log('click worked') - setShowModal(true) - setSelectedMovie(movie) - }} className="App-MovieContainer"> -
- {movie.title} -

{movie.title}

-

Release Date: {movie.release_date}

-

Vote Average: {movie.vote_average}

-
-
- ))} -
-

{selectedMovie.title}

- {selectedMovie.title} -

Release Date: {selectedMovie.release_date}

-

Overview: {selectedMovie.overview}

- -
-
-
- ); -}; -// MOVIE COMPONENT END -//LOAD MORE COMPONENT START - -// const [pageNumber, setPageNumber] = useState(0); - -// fetch (``) - -//LOAD MORE COMPONENT END export default App diff --git a/src/components/Header.jsx b/src/components/Header.jsx new file mode 100644 index 00000000..b6cb16b8 --- /dev/null +++ b/src/components/Header.jsx @@ -0,0 +1,14 @@ +import SearchFunction from "./SearchFunction" +import SortBy from "./SortBy" + +const Header = () => { + return ( +
+

Flixster

+ + +
+ ) +} + +export default Header \ No newline at end of file diff --git a/src/components/MovieCard.jsx b/src/components/MovieCard.jsx new file mode 100644 index 00000000..c8a0db13 --- /dev/null +++ b/src/components/MovieCard.jsx @@ -0,0 +1,51 @@ +import { useEffect, useState } from "react"; +import PopupModal from "./PopupModal"; +const ViteAPI = import.meta.env.VITE_API_KEY; + +const MovieCard = () => { + const [movies, setMovies] = useState([]); + const [pageNum, setPageNum] = useState(1) + + useEffect(() => { + fetch(`https://api.themoviedb.org/3/movie/now_playing?api_key=${ViteAPI}&page=${pageNum}`) + .then(response => response.json()) + .then(data => setMovies([...movies, ...data.results])) + }, [pageNum]); + + const handleClick = () => { + setPageNum((pageNum) => {return pageNum + 1}) + } + + const [showModal, setShowModal] = useState(false) + const [selectedMovie, setSelectedMovie] = useState({}) + + return ( + // get movies from fetch data file + // looping through movies using movies.map to get the props +
+ {movies.map((movie) => ( +
{ + setShowModal(true) + setSelectedMovie(movie) + }} className="App-MovieContainer"> +
+ {movie.title} +

{movie.title}

+

Vote Average: {movie.vote_average}

+

Add to Favorites: + +

+
+
+ ))} + + +
+ ); +}; + +export default MovieCard; \ No newline at end of file diff --git a/src/components/PopupModal.jsx b/src/components/PopupModal.jsx new file mode 100644 index 00000000..c54855e9 --- /dev/null +++ b/src/components/PopupModal.jsx @@ -0,0 +1,18 @@ +import React from 'react' + +const PopupModal = ( {showModal, selectedMovie, setShowModal} ) => { + return ( + <> +
+

{selectedMovie.title}

+ {selectedMovie.title} +

Release Date: {selectedMovie.release_date}

+

Overview: {selectedMovie.overview}

+ +
+
+ + ) +} + +export default PopupModal \ No newline at end of file diff --git a/src/components/SearchFunction.jsx b/src/components/SearchFunction.jsx new file mode 100644 index 00000000..8407b5c0 --- /dev/null +++ b/src/components/SearchFunction.jsx @@ -0,0 +1,31 @@ +import React, { useState } from 'react' + + +const SearchFunction = () => { +// const [selectedMovie, setSelectedMovie] = useState(); +// const [mode, setMode] = useState('now_playing'); +// const [selectedMovieId, setSelectedMovieID] = useState(); + const [userInput, setUserInput] = useState('') + +// const fetchMovies = async (page, search = '') => { +// const apiKey = meta.env.VITE_API_KEY; +// const url = `https://api.themoviedb.org/3/movie/popular?api_key=${ViteAPI}&page=${pageNum}`; +// const searchURL = 'https://api.themoviedb.org/3/search/movie?query=${search}&api_key=${apiKey}&page=${currentPage}' +// const response = await fetch(url); +// const data = await response.json(); +// const movies = data.results; +// // setMovies(movies); +// }; + +return ( +
+ + +
+ +) + +}; + +export default SearchFunction + diff --git a/src/components/SortBy.jsx b/src/components/SortBy.jsx new file mode 100644 index 00000000..29d93f80 --- /dev/null +++ b/src/components/SortBy.jsx @@ -0,0 +1,15 @@ + +function SortBy() { + return ( +
+ +
+ ) +}; + +export default SortBy; \ No newline at end of file From 59b41a20e59919e63373096f814eeb148ded833d Mon Sep 17 00:00:00 2001 From: Paloma Levy Date: Wed, 11 Jun 2025 16:03:05 -0700 Subject: [PATCH 07/16] Implemented search by and updated load button to be responsive to the search --- src/App.jsx | 40 ++++++++++++++++++------- src/components/Header.jsx | 2 +- src/components/MovieCard.jsx | 26 ++++++++-------- src/components/SearchFunction.jsx | 49 ++++++++++++++++++++----------- src/utils/fetchData.js | 24 +++++++++++---- 5 files changed, 93 insertions(+), 48 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index fa623a5a..80fae7f9 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,30 +1,48 @@ import { useEffect, useState } from 'react' import './App.css' -const ViteAUTH = import.meta.env.VITE_AUTH_KEY; import './utils/fetchData'; import Header from './components/Header'; import MovieCard from './components/MovieCard'; +import SearchFunction from './components/SearchFunction'; + +const App = () => { + const [movies, setMovies] = useState([]); + const [pageNum, setPageNum] = useState(1) + + const handleClick = () => { + console.log('i was here') + setPageNum((pageNum) => {return pageNum + 1}) + }; + + const ViteAPI = import.meta.env.VITE_API_KEY; + useEffect(() => { + fetch(`https://api.themoviedb.org/3/movie/now_playing?api_key=${ViteAPI}&page=${pageNum}`) + .then(response => response.json()) + .then(data => setMovies([...movies, ...data.results])) + }, [pageNum]); + + const fetchMovies = async (currentPage, search = '') => { + console.log("search term:", search) + const searchURL = `https://api.themoviedb.org/3/search/movie?api_key=${ViteAPI}&query=${search}&page=${currentPage}`; + const response = await fetch(searchURL); + const data = await response.json(); + const movies = data.results; + console.log("movies:", movies) + setMovies(movies); + }; -const App = () => { return (
+
- +
) } -// SEARCH FORM COMPONENT START -function SearchForm( {onQueryChange} ) { - const [searchQuery, setSearchQuery] = useState(''); - const [filteredMovies, setFilteredMovies] = useState([]); -} -// SEARCH FORM COMPONENT END - - export default App diff --git a/src/components/Header.jsx b/src/components/Header.jsx index b6cb16b8..3b26d996 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -5,7 +5,7 @@ const Header = () => { return (

Flixster

- + {/* */}
) diff --git a/src/components/MovieCard.jsx b/src/components/MovieCard.jsx index c8a0db13..c19c3193 100644 --- a/src/components/MovieCard.jsx +++ b/src/components/MovieCard.jsx @@ -1,24 +1,24 @@ import { useEffect, useState } from "react"; import PopupModal from "./PopupModal"; -const ViteAPI = import.meta.env.VITE_API_KEY; -const MovieCard = () => { - const [movies, setMovies] = useState([]); - const [pageNum, setPageNum] = useState(1) +const MovieCard = ({movies, handleClick}) => { +// const [movies, setMovies] = useState([]); +// const [pageNum, setPageNum] = useState(1) - useEffect(() => { - fetch(`https://api.themoviedb.org/3/movie/now_playing?api_key=${ViteAPI}&page=${pageNum}`) - .then(response => response.json()) - .then(data => setMovies([...movies, ...data.results])) - }, [pageNum]); +// useEffect(() => { +// fetch(`https://api.themoviedb.org/3/movie/now_playing?api_key=${ViteAPI}&page=${pageNum}`) +// .then(response => response.json()) +// .then(data => setMovies([...movies, ...data.results])) +// }, [pageNum]); - const handleClick = () => { - setPageNum((pageNum) => {return pageNum + 1}) - } const [showModal, setShowModal] = useState(false) const [selectedMovie, setSelectedMovie] = useState({}) + const toggleClick = () => { + handleClick() + }; + return ( // get movies from fetch data file // looping through movies using movies.map to get the props @@ -43,7 +43,7 @@ const MovieCard = () => { selectedMovie={selectedMovie} showModal={showModal} /> - +
); }; diff --git a/src/components/SearchFunction.jsx b/src/components/SearchFunction.jsx index 8407b5c0..b4fdd389 100644 --- a/src/components/SearchFunction.jsx +++ b/src/components/SearchFunction.jsx @@ -1,28 +1,43 @@ -import React, { useState } from 'react' +import React, { useEffect, useState } from 'react' -const SearchFunction = () => { -// const [selectedMovie, setSelectedMovie] = useState(); -// const [mode, setMode] = useState('now_playing'); -// const [selectedMovieId, setSelectedMovieID] = useState(); +// SEARCH FORM COMPONENT START +// function SearchForm( {onQueryChange} ) { +// const [searchQuery, setSearchQuery] = useState(''); +// const [filteredMovies, setFilteredMovies] = useState([]); +// } +// SEARCH FORM COMPONENT END +const SearchFunction = ({fetchMovies}) => { + // const [selectedMovie, setSelectedMovie] = useState(); + // const [mode, setMode] = useState('now_playing'); + // const [selectedMovieId, setSelectedMovieID] = useState(); const [userInput, setUserInput] = useState('') -// const fetchMovies = async (page, search = '') => { -// const apiKey = meta.env.VITE_API_KEY; -// const url = `https://api.themoviedb.org/3/movie/popular?api_key=${ViteAPI}&page=${pageNum}`; -// const searchURL = 'https://api.themoviedb.org/3/search/movie?query=${search}&api_key=${apiKey}&page=${currentPage}' -// const response = await fetch(url); -// const data = await response.json(); -// const movies = data.results; -// // setMovies(movies); -// }; + const handleTextChange = (event) => { + console.log('trying to change') + const {value} = event.target + setUserInput(value) + } + // useEffect(() => { + // fetchMovies(1, 'lilo'); + // }, []); + + // const fetchMovies = async (currentPage, search = '') => { + // const ViteAPI = import.meta.env.VITE_API_KEY; + // // const url = `https://api.themoviedb.org/3/movie/popular?api_key=${ViteAPI}&page=${pageNum}`; + // const searchURL = `https://api.themoviedb.org/3/search/movie?api_key=${ViteAPI}&query=${search}&page=${currentPage}`; + // const response = await fetch(searchURL); + // const data = await response.json(); + // const movies = data.results; + // setSelectedMovie(movies); + // }; return ( -
- +
+ +
- ) }; diff --git a/src/utils/fetchData.js b/src/utils/fetchData.js index d13c07f4..5f73ac15 100644 --- a/src/utils/fetchData.js +++ b/src/utils/fetchData.js @@ -1,4 +1,5 @@ -const url = 'https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=1'; +const now_playing_url = 'https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=1'; + const options = { method: 'GET', headers: { @@ -7,10 +8,21 @@ const options = { } }; -const data = fetch(url, options) - .then(res => res.json()) - .then(json => console.log(json)) - .catch(err => console.error(err)); +// const data = fetch(url, options) +// .then(res => res.json()) +// .then(json => console.log(json)) +// .catch(err => console.error(err)); +function fetchMovies(url, options) { + fetch(url, options) + .then(res => res.json()) + .then((json) => { + console.log(json) + return json}) + .catch(err => console.error(err)); +}; + +const data = fetchMovies(now_playing_url, options); +console.log("this is the data:", data); -// const url = 'https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=1'; \ No newline at end of file +// \ No newline at end of file From 0659983caad5caafa3321ebcd36d28ae2437f4c7 Mon Sep 17 00:00:00 2001 From: Paloma Levy Date: Wed, 11 Jun 2025 19:44:09 -0700 Subject: [PATCH 08/16] sort by button is working now --- src/App.jsx | 16 +++++++++- src/components/Header.jsx | 9 +++--- src/components/MovieCard.jsx | 18 ++--------- src/components/PopupModal.jsx | 1 + src/components/SearchFunction.jsx | 51 ++++++++++++------------------- src/components/SortBy.jsx | 20 +++++++++--- 6 files changed, 59 insertions(+), 56 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 80fae7f9..164855dd 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -4,13 +4,13 @@ import './utils/fetchData'; import Header from './components/Header'; import MovieCard from './components/MovieCard'; import SearchFunction from './components/SearchFunction'; +import SortBy from "./components/SortBy" const App = () => { const [movies, setMovies] = useState([]); const [pageNum, setPageNum] = useState(1) const handleClick = () => { - console.log('i was here') setPageNum((pageNum) => {return pageNum + 1}) }; @@ -31,11 +31,25 @@ const App = () => { setMovies(movies); }; + const sortMovies = (type) => { + if (type === 'Rating') { + const currentMovies = [...movies].sort((a, b) => b.vote_average - a.vote_average) + setMovies(currentMovies); + } else if (type === "A-Z") { + const currentMovies = [...movies].sort((a, b) => a.title.localeCompare(b.title)) + setMovies(currentMovies); + } else if (type === "Date") { + const currentMovies = [...movies].sort((a, b) => new Date(b.release_date) - new Date(a.release_date)) + setMovies(currentMovies); + } + } + return (
+
diff --git a/src/components/Header.jsx b/src/components/Header.jsx index 3b26d996..a00c3dc9 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -1,14 +1,15 @@ import SearchFunction from "./SearchFunction" -import SortBy from "./SortBy" + +// HEADER COMPONENT START const Header = () => { return (

Flixster

- {/* */} -
) } -export default Header \ No newline at end of file +export default Header + +// HEADER COMPONENT END \ No newline at end of file diff --git a/src/components/MovieCard.jsx b/src/components/MovieCard.jsx index c19c3193..058fc13c 100644 --- a/src/components/MovieCard.jsx +++ b/src/components/MovieCard.jsx @@ -2,15 +2,6 @@ import { useEffect, useState } from "react"; import PopupModal from "./PopupModal"; const MovieCard = ({movies, handleClick}) => { -// const [movies, setMovies] = useState([]); -// const [pageNum, setPageNum] = useState(1) - -// useEffect(() => { -// fetch(`https://api.themoviedb.org/3/movie/now_playing?api_key=${ViteAPI}&page=${pageNum}`) -// .then(response => response.json()) -// .then(data => setMovies([...movies, ...data.results])) -// }, [pageNum]); - const [showModal, setShowModal] = useState(false) const [selectedMovie, setSelectedMovie] = useState({}) @@ -18,10 +9,9 @@ const MovieCard = ({movies, handleClick}) => { const toggleClick = () => { handleClick() }; - + +// const movieId = data.results[0].id; return ( - // get movies from fetch data file - // looping through movies using movies.map to get the props
{movies.map((movie) => (
{ @@ -32,9 +22,7 @@ const MovieCard = ({movies, handleClick}) => { {movie.title}

{movie.title}

Vote Average: {movie.vote_average}

-

Add to Favorites: - -

+
))} diff --git a/src/components/PopupModal.jsx b/src/components/PopupModal.jsx index c54855e9..1bef7108 100644 --- a/src/components/PopupModal.jsx +++ b/src/components/PopupModal.jsx @@ -8,6 +8,7 @@ const PopupModal = ( {showModal, selectedMovie, setShowModal} ) => { {selectedMovie.title}

Release Date: {selectedMovie.release_date}

Overview: {selectedMovie.overview}

+

Run Time: {}

diff --git a/src/components/SearchFunction.jsx b/src/components/SearchFunction.jsx index b4fdd389..b9c8528b 100644 --- a/src/components/SearchFunction.jsx +++ b/src/components/SearchFunction.jsx @@ -2,45 +2,32 @@ import React, { useEffect, useState } from 'react' // SEARCH FORM COMPONENT START -// function SearchForm( {onQueryChange} ) { -// const [searchQuery, setSearchQuery] = useState(''); -// const [filteredMovies, setFilteredMovies] = useState([]); -// } -// SEARCH FORM COMPONENT END + const SearchFunction = ({fetchMovies}) => { - // const [selectedMovie, setSelectedMovie] = useState(); - // const [mode, setMode] = useState('now_playing'); - // const [selectedMovieId, setSelectedMovieID] = useState(); const [userInput, setUserInput] = useState('') const handleTextChange = (event) => { - console.log('trying to change') const {value} = event.target setUserInput(value) } - // useEffect(() => { - // fetchMovies(1, 'lilo'); - // }, []); - - // const fetchMovies = async (currentPage, search = '') => { - // const ViteAPI = import.meta.env.VITE_API_KEY; - // // const url = `https://api.themoviedb.org/3/movie/popular?api_key=${ViteAPI}&page=${pageNum}`; - // const searchURL = `https://api.themoviedb.org/3/search/movie?api_key=${ViteAPI}&query=${search}&page=${currentPage}`; - // const response = await fetch(searchURL); - // const data = await response.json(); - // const movies = data.results; - // setSelectedMovie(movies); - // }; - -return ( -
- - - -
-) - + + const handleKeyDown = (event) => { + if (event.key === 'Enter') { + fetchMovies(1, userInput) + }; + }; + + return ( +
+ + + +
+ ) }; -export default SearchFunction +export default SearchFunction; + +// SEARCH FORM COMPONENT END +// onKeyDown={handleKeyDown} onChange={handleTextChange} \ No newline at end of file diff --git a/src/components/SortBy.jsx b/src/components/SortBy.jsx index 29d93f80..9870f9f2 100644 --- a/src/components/SortBy.jsx +++ b/src/components/SortBy.jsx @@ -1,9 +1,19 @@ +import { useState } from 'react' +// SORT BY COMPONENT START +function SortBy( {sortMovies} ) { + + const [sortValue, setSortValue] = useState('') + + const handleSort = (event) => { + setSortValue(event.target.value) + sortMovies(event.target.value); + } + -function SortBy() { return (
- + @@ -12,4 +22,6 @@ function SortBy() { ) }; -export default SortBy; \ No newline at end of file +export default SortBy; + +// SORT BY COMPONENT END From fd492327f1729bc44f50762e61327e19510f63f6 Mon Sep 17 00:00:00 2001 From: Paloma Levy Date: Thu, 12 Jun 2025 16:47:33 -0700 Subject: [PATCH 09/16] finished all core features and implemented movie trailer in modal --- src/App.jsx | 18 +++++-- src/components/MovieCard.jsx | 88 ++++++++++++++++++++++--------- src/components/PopupModal.jsx | 28 ++++++++-- src/components/SearchFunction.jsx | 13 +++-- src/components/SortBy.jsx | 2 +- src/utils/fetchData.js | 28 ---------- 6 files changed, 112 insertions(+), 65 deletions(-) delete mode 100644 src/utils/fetchData.js diff --git a/src/App.jsx b/src/App.jsx index 164855dd..8269d23e 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,11 +1,11 @@ import { useEffect, useState } from 'react' import './App.css' -import './utils/fetchData'; import Header from './components/Header'; import MovieCard from './components/MovieCard'; import SearchFunction from './components/SearchFunction'; import SortBy from "./components/SortBy" + const App = () => { const [movies, setMovies] = useState([]); const [pageNum, setPageNum] = useState(1) @@ -16,6 +16,12 @@ const App = () => { const ViteAPI = import.meta.env.VITE_API_KEY; useEffect(() => { + if(pageNum === 0) { + console.log("clearing..."); + setPageNum(1) + return; + } + console.log('calling use effect') fetch(`https://api.themoviedb.org/3/movie/now_playing?api_key=${ViteAPI}&page=${pageNum}`) .then(response => response.json()) .then(data => setMovies([...movies, ...data.results])) @@ -27,7 +33,6 @@ const App = () => { const response = await fetch(searchURL); const data = await response.json(); const movies = data.results; - console.log("movies:", movies) setMovies(movies); }; @@ -41,14 +46,21 @@ const App = () => { } else if (type === "Date") { const currentMovies = [...movies].sort((a, b) => new Date(b.release_date) - new Date(a.release_date)) setMovies(currentMovies); + } else if (type === "") { + onClearing() } } + const onClearing = () => { + setMovies([]) + setPageNum(0) + } + return (
- +
diff --git a/src/components/MovieCard.jsx b/src/components/MovieCard.jsx index 058fc13c..c06dd3a0 100644 --- a/src/components/MovieCard.jsx +++ b/src/components/MovieCard.jsx @@ -5,35 +5,73 @@ const MovieCard = ({movies, handleClick}) => { const [showModal, setShowModal] = useState(false) const [selectedMovie, setSelectedMovie] = useState({}) + const [likedMovies, setLikedMovies] = useState([]) + const [movieIdInfo, setMovieIdInfo] = useState({}); + const [movieVideoInfo, setMovieVideoInfo] = useState([]); + useEffect(() => { + if (selectedMovie.id) { + fetchMovieData(selectedMovie.id) + fetchVideoData(selectedMovie.id) + }; + }, [selectedMovie]); + const toggleClick = () => { - handleClick() - }; + handleClick(); + }; -// const movieId = data.results[0].id; - return ( -
- {movies.map((movie) => ( -
{ - setShowModal(true) - setSelectedMovie(movie) - }} className="App-MovieContainer"> -
- {movie.title} -

{movie.title}

-

Vote Average: {movie.vote_average}

- + const fetchMovieData = async (movieID) => { + const idURL = `https://api.themoviedb.org/3/movie/${movieID}?api_key=${import.meta.env.VITE_API_KEY}`; + const response = await fetch(idURL); + const data = await response.json(); + const movieIdInfo = data; + setMovieIdInfo(movieIdInfo) + }; + + const fetchVideoData = async (movieID) => { + const videoIdURL = `https://api.themoviedb.org/3/movie/${movieID}/videos?api_key=${import.meta.env.VITE_API_KEY}`; + const response = await fetch(videoIdURL); + const data = await response.json(); + const movieVideoInfo = data; + setMovieVideoInfo(movieVideoInfo.results); + }; + + return ( +
+ {movies.map((movie) => ( +
{ + setShowModal(true) + setSelectedMovie(movie) + }} className="App-MovieContainer"> +
+ {movie.title} +

{movie.title}

+

Vote Average: {movie.vote_average}

+ +
-
- ))} - - -
- ); + ))} + + +
+ ); }; export default MovieCard; \ No newline at end of file diff --git a/src/components/PopupModal.jsx b/src/components/PopupModal.jsx index 1bef7108..9f5d4b02 100644 --- a/src/components/PopupModal.jsx +++ b/src/components/PopupModal.jsx @@ -1,6 +1,27 @@ -import React from 'react' +import React, { useEffect, useState } from 'react' -const PopupModal = ( {showModal, selectedMovie, setShowModal} ) => { +const PopupModal = ( {showModal, selectedMovie, setShowModal, movieIdInfo, movieVideoInfo} ) => { + + const [genres, setGenres] = useState(''); + const [video, setVideo] = useState([]); + + const findGenres = () => { + setGenres(movieIdInfo.genres.map(genre => genre.name).join(', ')) + } + + useEffect(() => { + if (movieIdInfo.genres) { + findGenres(); + } + }, [movieIdInfo]) + + useEffect(() => { + if (movieVideoInfo.length) { + setVideo(movieVideoInfo.filter(video => video.type === 'Trailer' && video.official === true + )) + } + }, [movieVideoInfo]); + return ( <>
@@ -8,7 +29,8 @@ const PopupModal = ( {showModal, selectedMovie, setShowModal} ) => { {selectedMovie.title}

Release Date: {selectedMovie.release_date}

Overview: {selectedMovie.overview}

-

Run Time: {}

+ {video.length && }

Run Time: {movieIdInfo.runtime} minutes

+

Genres: {genres}

diff --git a/src/components/SearchFunction.jsx b/src/components/SearchFunction.jsx index b9c8528b..cbb9526e 100644 --- a/src/components/SearchFunction.jsx +++ b/src/components/SearchFunction.jsx @@ -3,7 +3,7 @@ import React, { useEffect, useState } from 'react' // SEARCH FORM COMPONENT START -const SearchFunction = ({fetchMovies}) => { +const SearchFunction = ({fetchMovies, onClearing}) => { const [userInput, setUserInput] = useState('') const handleTextChange = (event) => { @@ -17,17 +17,20 @@ const SearchFunction = ({fetchMovies}) => { }; }; + const handleReset = () => { + setUserInput(''); + onClearing(); + } + return (
- +
) }; export default SearchFunction; -// SEARCH FORM COMPONENT END - -// onKeyDown={handleKeyDown} onChange={handleTextChange} \ No newline at end of file +// SEARCH FORM COMPONENT END \ No newline at end of file diff --git a/src/components/SortBy.jsx b/src/components/SortBy.jsx index 9870f9f2..7fe2180f 100644 --- a/src/components/SortBy.jsx +++ b/src/components/SortBy.jsx @@ -13,7 +13,7 @@ function SortBy( {sortMovies} ) { return (
- - +
+
+ + + +
) }; From 9171b1e2469763190ff01e19112856ec860112b5 Mon Sep 17 00:00:00 2001 From: Paloma Levy Date: Fri, 13 Jun 2025 13:01:18 -0700 Subject: [PATCH 12/16] finished like & watched buttons, css --- src/App.css | 66 ++++++++++++++++++++++++++------ src/App.jsx | 8 ++++ src/components/Header.jsx | 2 +- src/components/LikeButton.jsx | 17 ++++++++ src/components/MovieCard.jsx | 37 +++++++----------- src/components/PopupModal.css | 40 ++++++++++++------- src/components/PopupModal.jsx | 10 ++--- src/components/WatchedButton.jsx | 17 ++++++++ src/index.css | 5 ++- 9 files changed, 144 insertions(+), 58 deletions(-) create mode 100644 src/components/LikeButton.jsx create mode 100644 src/components/WatchedButton.jsx diff --git a/src/App.css b/src/App.css index 6ecb231f..3284b2e1 100644 --- a/src/App.css +++ b/src/App.css @@ -3,7 +3,7 @@ } .App-header { - background-color: #282c34; + background-color: #141414; display: flex; flex-direction: row; align-items: center; @@ -28,32 +28,37 @@ gap: 800px; padding: 10px; background-color: aquamarine; - border-radius: 5px; + background: linear-gradient(to right, #8B786D, #D5CFC6); + font-family: Georgia, Times, 'Times New Roman', serif; .searchBar { border-radius: 7px; border: none; padding: 5px; background-color: #dfdfdf; + font-family:Georgia, Times, 'Times New Roman', serif; } .Submit { border-radius: 7px; padding: 5px; font-size: small; - color: black; - background-color: transparent; + color: white; + font-family: Georgia, Times, 'Times New Roman', serif; + background-color: #141414; } .Clear { border-radius: 7px; font-size: small; - color: black; - background-color: transparent; + color: white; + font-family: Georgia, Times, 'Times New Roman', serif; + background-color: #141414; } .sortByButton { - background-color:#dfdfdf; - color: rgb(108, 107, 107); + background-color: #141414; + color: #dfdfdf; border-radius: 7px; + font-family: Georgia, Times, 'Times New Roman', serif; width: 90px; padding: 4px; border: none; @@ -78,8 +83,6 @@ justify-content: center; align-items: center; padding: 10px; - background-color: aquamarine; - border-radius: 5px; gap: 10px; } .App-MovieCard { @@ -88,6 +91,9 @@ } } +.body { + background-color: #8C99A2; +} .App-MovieCard { display: flex; flex-direction: row; @@ -98,11 +104,10 @@ .App-MovieContainer { display: flex; - flex-direction: row; border-radius: 5px; font-size: x-small; width: 200px; - background-color:rgb(229, 229, 229); + background-color: #EBF5EE; box-shadow: 0px 5px 5px rgba(36, 36, 36, 0.5); } .App-MovieContainer:hover { @@ -112,10 +117,40 @@ } } +.likeButton { + background-color: transparent; + border: none; +} +.likeButton:hover { + cursor: pointer; + background-color: transparent; + transform: scale(1.2); +} + +.watchedButton { + background-color: transparent; + border: none; +} +.watchedButton:hover { + cursor: pointer; + background-color: transparent; + transform: scale(1.2); +} + .loadButton { display: flex; justify-content: center; align-items: center; + padding: 12px; + background-color: #8C99A2; + + button { + padding: 8px; + background-color: #141414; + border-radius: 10px; + justify-content: center; + align-items: center; + } } .favoriteButton { @@ -123,6 +158,13 @@ border: none; } +.footer { + background-color: #141414; + margin-bottom: 0; + bottom: 0; + color: white; + padding: 5px; +} img { width: 200px; border-top-left-radius: 5px; diff --git a/src/App.jsx b/src/App.jsx index 357d80b3..31024770 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -70,7 +70,15 @@ const App = () => {
+
+
+
+ +
+
+

Paloma Levy

+
) } diff --git a/src/components/Header.jsx b/src/components/Header.jsx index 8c07b392..11dba8b7 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -5,7 +5,7 @@ import SearchFunction from "./SearchFunction" const Header = () => { return (
-

FLIXSTER

+

FLIXSTER 🍿

) } diff --git a/src/components/LikeButton.jsx b/src/components/LikeButton.jsx new file mode 100644 index 00000000..e8031420 --- /dev/null +++ b/src/components/LikeButton.jsx @@ -0,0 +1,17 @@ +import React, { useState } from "react"; + +function LikeButton({ handleLikeButton, movieId }) { + const [emoji, setEmoji] = useState('🖤'); + const handleClick = (event) => { + event.stopPropagation(); + setEmoji(emoji === '🖤' ? '💖' : '🖤'); + handleLikeButton(movieId); + }; + return ( + + ); +} + +export default LikeButton; \ No newline at end of file diff --git a/src/components/MovieCard.jsx b/src/components/MovieCard.jsx index b9ce73cc..19711e2c 100644 --- a/src/components/MovieCard.jsx +++ b/src/components/MovieCard.jsx @@ -1,7 +1,9 @@ import { useEffect, useState } from "react"; import PopupModal from "./PopupModal"; +import LikeButton from "./LikeButton"; +import WatchedButton from "./WatchedButton"; -const MovieCard = ({movies, handleClick}) => { +const MovieCard = ({movies}) => { const [showModal, setShowModal] = useState(false) const [selectedMovie, setSelectedMovie] = useState({}) @@ -15,10 +17,6 @@ const MovieCard = ({movies, handleClick}) => { fetchVideoData(selectedMovie.id) }; }, [selectedMovie]); - - const toggleClick = () => { - handleClick(); - }; const fetchMovieData = async (movieID) => { const idURL = `https://api.themoviedb.org/3/movie/${movieID}?api_key=${import.meta.env.VITE_API_KEY}`; @@ -40,25 +38,17 @@ const MovieCard = ({movies, handleClick}) => {
{movies.map((movie) => (
{ - setShowModal(true) - setSelectedMovie(movie) - }} className="App-MovieContainer"> + setShowModal(true) + setSelectedMovie(movie) + }} className="App-MovieContainer">
- {movie.title} -

{movie.title}

-

Vote Average: {movie.vote_average}

- + {movie.title} +

{movie.title}

+

Vote Average: {movie.vote_average}

+
+ handleLikeButton(movieIdInfo)} movieId={movie.id} /> + handleWatchButton(movieIdInfo)} movieId={movie.id} /> +
))} @@ -69,7 +59,6 @@ const MovieCard = ({movies, handleClick}) => { movieIdInfo={movieIdInfo} movieVideoInfo={movieVideoInfo} /> -
); }; diff --git a/src/components/PopupModal.css b/src/components/PopupModal.css index f3cb11c8..4e47457b 100644 --- a/src/components/PopupModal.css +++ b/src/components/PopupModal.css @@ -3,7 +3,7 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); - background-color: #ffd6d6; + background-color: #7e8a92; padding: 20px; border: none; border-radius: 5px; @@ -19,7 +19,8 @@ left: 0; width: 100%; height: 100%; - background-color: rgba(0, 0, 0, 0.5); + background-color: rgba(0, 0, 0, 0.7); + backdrop-filter: blur(8px); z-index: 9; } .visible { @@ -27,6 +28,7 @@ flex-wrap: wrap; justify-content: center; overflow: scroll; + .popupHeader { position:fixed; justify-content: center; @@ -34,7 +36,7 @@ .closeButton { display: flex; padding-left: 750px; - color: #aaa; + color: #191923; background-color: transparent; font-size: x-large; border: none; @@ -59,9 +61,13 @@ display: flex; flex-wrap: wrap; flex-direction: column; - text-align: left; padding-top: 10px; } + + img { + width: 500px; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.5); + } } } .notVisible { @@ -72,24 +78,30 @@ img { width: 200px; } - .videoScreen { - width: auto; + iframe { + width: 250px; height: auto; } + .popup { + width: 300px; + } .visible { display: flex; flex-wrap: wrap; justify-content: center; overflow: scroll; + align-items: center; .popupHeader { - position:fixed; + display: flex; justify-content: center; + margin-bottom: 500px; + padding-bottom: 20px; } .closeButton { display: flex; - padding-left: 750px; - color: #aaa; + padding-left: 270px; + color: #141414; background-color: transparent; font-size: x-large; border: none; @@ -108,17 +120,17 @@ gap: 20px; padding: 20px; + img { + width: 300px; + } + .right { display: flex; flex-wrap: wrap; flex-direction: column; - text-align: left; + text-align: center; padding-top: 10px; } } } -} - -img { - width: 200px; } \ No newline at end of file diff --git a/src/components/PopupModal.jsx b/src/components/PopupModal.jsx index e6122b92..61a6ad07 100644 --- a/src/components/PopupModal.jsx +++ b/src/components/PopupModal.jsx @@ -30,12 +30,12 @@ const PopupModal = ( {showModal, selectedMovie, setShowModal, movieIdInfo, movie
- {selectedMovie.title} + {selectedMovie.title}
-

Release Date: {selectedMovie.release_date}

-

Genres: {genres}

-

Run Time: {movieIdInfo.runtime} minutes

-

Overview: {selectedMovie.overview}

+

Release Date: {selectedMovie.release_date}

+

Genres: {genres}

+

Run Time: {movieIdInfo.runtime} minutes

+

Overview: {selectedMovie.overview}

diff --git a/src/components/WatchedButton.jsx b/src/components/WatchedButton.jsx new file mode 100644 index 00000000..89f1c861 --- /dev/null +++ b/src/components/WatchedButton.jsx @@ -0,0 +1,17 @@ +import React, { useState } from "react"; + +function WatchedButton({ handleWatchButton, movieId }) { + const [emoji, setEmoji] = useState('☑️'); + const handleClick = (event) => { + event.stopPropagation(); + setEmoji(emoji === '☑️' ? '✅' : '☑️'); + handleWatchButton(movieId); + }; + return ( + + ); +} + +export default WatchedButton; \ No newline at end of file diff --git a/src/index.css b/src/index.css index e1faed1a..ad05b8a5 100644 --- a/src/index.css +++ b/src/index.css @@ -1,13 +1,14 @@ body { margin: 0; - font-family: Arial, sans-serif; - background-color: #f4f4f4; + font-family:Georgia, Times, 'Times New Roman', serif; } button { background-color: #282c34; color: white; cursor: pointer; + justify-content: center; + align-items: center; font-size: 16px; font-weight: bold; transition: background-color 0.3s ease; From b632b1a1ea01182fb22b2945a2687d5ceb8aaede Mon Sep 17 00:00:00 2001 From: Paloma Levy Date: Fri, 13 Jun 2025 15:40:16 -0700 Subject: [PATCH 13/16] testing new commit email --- src/App.css | 44 ++++++++++++++++++++++------------- src/App.jsx | 3 ++- src/components/Header.jsx | 2 +- src/components/MovieCard.jsx | 2 +- src/components/PopupModal.css | 8 +++---- src/components/SortBy.jsx | 2 +- src/index.css | 2 +- 7 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/App.css b/src/App.css index 3284b2e1..5bc35832 100644 --- a/src/App.css +++ b/src/App.css @@ -3,13 +3,14 @@ } .App-header { - background-color: #141414; + background-color: #031927; display: flex; flex-direction: row; align-items: center; justify-content: space-evenly; color: white; - padding: 10px; + padding-top: 5px; + font-size: large; .sortByButton { background-color:rgb(191, 191, 191); @@ -25,10 +26,10 @@ flex-direction: row; justify-content: center; align-items: center; - gap: 800px; - padding: 10px; - background-color: aquamarine; - background: linear-gradient(to right, #8B786D, #D5CFC6); + gap: 20px; + padding-bottom: 10px; + background-color: #031927; + /* background: linear-gradient(to right, #8B786D, #D5CFC6); */ font-family: Georgia, Times, 'Times New Roman', serif; .searchBar { @@ -42,21 +43,21 @@ border-radius: 7px; padding: 5px; font-size: small; - color: white; + color: black; font-family: Georgia, Times, 'Times New Roman', serif; - background-color: #141414; + background-color: #dfdfdf; } .Clear { border-radius: 7px; font-size: small; - color: white; + color: black; font-family: Georgia, Times, 'Times New Roman', serif; - background-color: #141414; + background-color: #dfdfdf; } .sortByButton { - background-color: #141414; - color: #dfdfdf; + background-color: #dfdfdf; + color: black; border-radius: 7px; font-family: Georgia, Times, 'Times New Roman', serif; width: 90px; @@ -92,7 +93,8 @@ } .body { - background-color: #8C99A2; + background: linear-gradient(to top, #031927 50%, rgba(51, 51, 51, 0)) no-repeat; + } .App-MovieCard { display: flex; @@ -115,6 +117,14 @@ cursor: pointer; transform: scale(1.03); } + .voteAverage { + font-size: small; + background-color:#031927; + color: white; + margin-right: 50px; + margin-left: 50px; + border-radius: 5px; + } } .likeButton { @@ -142,14 +152,16 @@ justify-content: center; align-items: center; padding: 12px; - background-color: #8C99A2; + background-color: #031927; button { padding: 8px; - background-color: #141414; + background-color: #EBF5EE; border-radius: 10px; justify-content: center; align-items: center; + color: black; + font-family: Georgia, 'Times New Roman', Times, serif; } } @@ -159,7 +171,7 @@ } .footer { - background-color: #141414; + background-color: #031927; margin-bottom: 0; bottom: 0; color: white; diff --git a/src/App.jsx b/src/App.jsx index 31024770..9d8112fa 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -77,7 +77,8 @@ const App = () => {
-

Paloma Levy

+

© 2025 Flixster. All Rights Reserved.

+

Created By: Paloma Levy

) diff --git a/src/components/Header.jsx b/src/components/Header.jsx index 11dba8b7..c0630fc5 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -5,7 +5,7 @@ import SearchFunction from "./SearchFunction" const Header = () => { return (
-

FLIXSTER 🍿

+

🎥 FLIXSTER 🍿

) } diff --git a/src/components/MovieCard.jsx b/src/components/MovieCard.jsx index 19711e2c..9cb7ad2d 100644 --- a/src/components/MovieCard.jsx +++ b/src/components/MovieCard.jsx @@ -44,7 +44,7 @@ const MovieCard = ({movies}) => {
{movie.title}

{movie.title}

-

Vote Average: {movie.vote_average}

+

⭐ {movie.vote_average}

handleLikeButton(movieIdInfo)} movieId={movie.id} /> handleWatchButton(movieIdInfo)} movieId={movie.id} /> diff --git a/src/components/PopupModal.css b/src/components/PopupModal.css index 4e47457b..f47148ab 100644 --- a/src/components/PopupModal.css +++ b/src/components/PopupModal.css @@ -3,13 +3,13 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); - background-color: #7e8a92; + background-color: #afb8be; padding: 20px; border: none; - border-radius: 5px; + border-radius: 10px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); width: 80%; - max-width: 800px; + max-width: 600px; max-height: 600px; z-index: 10; } @@ -35,7 +35,7 @@ } .closeButton { display: flex; - padding-left: 750px; + padding-left: 550px; color: #191923; background-color: transparent; font-size: x-large; diff --git a/src/components/SortBy.jsx b/src/components/SortBy.jsx index 7fe2180f..866d073b 100644 --- a/src/components/SortBy.jsx +++ b/src/components/SortBy.jsx @@ -13,7 +13,7 @@ function SortBy( {sortMovies} ) { return (