-
Notifications
You must be signed in to change notification settings - Fork 197
[Flixster] Movie Explorer Project #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
0ee98c9
acb6def
cf8c998
861ce74
1fb1c32
0bfdf01
40a1d50
3f3ba8e
a920a47
62dcd79
31a8d55
616f421
39138ab
59869d1
9fb192e
8862c59
51e9dc2
6476a48
750075f
4189a07
80f7928
9fb2d7f
6fe33c9
47071b1
c0bcdc5
db9930e
30c6cca
5ab3ed1
ceec3f7
b8108ef
3db32a8
fad4964
830f249
366d61e
454ac61
7d7be1f
e9ad9af
3b5703a
9e9419d
c7038a5
b57d67d
95a7961
2f2c90b
73e6768
229aeb1
06b118f
22f3bf7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,3 +22,4 @@ dist-ssr | |
| *.njsproj | ||
| *.sln | ||
| *.sw? | ||
| .env | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,129 @@ | ||
| # 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: **Jackson Seifferly** | ||
|
|
||
| Currently, two official plugins are available: | ||
| Estimated time spent: **20** 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**): [https://flixster-bs8p.onrender.com/] | ||
|
|
||
| ### Application Features | ||
|
|
||
| #### REQUIRED FEATURES | ||
|
|
||
| - [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 | ||
| - [x] Users can load more current movies by clicking a button which adds more movies to the grid without reloading the entire page. | ||
| - [x] **Search Functionality** | ||
| - [x] 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 | ||
| - [x] Movies with a title containing the search query in the text input field are displayed in a grid view when the user either: | ||
| - [x] Presses the Enter key | ||
| - [x] Clicks the Submit/Search button | ||
| - [x] Users can click the Clear button. When clicked: | ||
| - [x] 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 | ||
| - [x] **Design Features** | ||
| - [x] Website implements all of the following accessibility features: | ||
| - [x] Semantic HTML | ||
| - [x] [Color contrast](https://webaim.org/resources/contrastchecker/) | ||
| - [x] Alt text for images | ||
| - [x] Website implements responsive web design. | ||
| - [x] Uses CSS Flexbox or CSS Grid | ||
| - [x] Movie tiles and images shrink/grow in response to window size | ||
| - [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 | ||
| - [x] Genres | ||
| - [x] An overview | ||
| - [x] Users can use a drop-down menu to sort movies. | ||
| - [x] Drop-down allows movies to be sorted by: | ||
| - [x] Title (alphabetic, A-Z) | ||
| - [x] Release date (chronologically, most recent to oldest) | ||
| - [x] Vote average (descending, highest to lowest) | ||
| - [x] When a sort option is clicked, movies display in a grid according to selected criterion. | ||
| - [x] Website displays: | ||
| - [x] Header section | ||
| - [x] Banner section | ||
| - [x] Search bar | ||
| - [x] Movie grid | ||
| - [x] Footer section | ||
| - [x] **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. | ||
| - [x] **Deployment** | ||
| - [x] Website is deployed via Render. | ||
| - [x] **VIDEO WALKTHROUGH SPECIAL INSTRUCTIONS**: For ease of grading, please use the deployed version of your website when creating your walkthrough. | ||
|
|
||
| #### STRETCH FEATURES | ||
|
|
||
|
|
||
| - [x] **Embedded Movie Trailers** | ||
| - [x] Within the pop-up modal displaying a movie's details, the movie trailer is viewable. | ||
| -[x] When the trailer is clicked, users can play the movie trailer. | ||
| - [x] **Favorite Button** | ||
| - [x] For each movie displayed, users can favorite the movie. | ||
| - [x] 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. | ||
| - [x] If the movie is not favorited: | ||
| - [x] Clicking on the visual element should mark the movie as favorited | ||
| - [x] There should be visual feedback (such as the heart turning a different color) to show that the movie has been favorited by the user. | ||
| - [x] If the movie is already favorited: | ||
| - [x] Clicking on the visual element should mark the movie as *not* favorited. | ||
| - [x] There should be visual feedback (such as the heart turning a different color) to show that the movie has been unfavorited. | ||
| - [x] **Watched Checkbox** | ||
| - [x] For each movie displayed, users can mark the movie as watched. | ||
| - [x] 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. | ||
| - [x] If the movie has not been watched: | ||
| - [x] Clicking on the visual element should mark the movie as watched | ||
| - [x] There should be visual feedback (such as the eye turning a different color) to show that the movie has been watched by the user. | ||
| - [x] If the movie is already watched: | ||
| - [x] Clicking on the visual element should mark the movie as *not* watched. | ||
| - [x] There should be visual feedback (such as the eye turning a different color) to show that the movie has not been watched. | ||
| - [x] **Sidebar** | ||
| - [x] The website includes a side navigation bar. | ||
| - [x] The sidebar has three pages: | ||
| - [x] Home | ||
| - [x] Favorites | ||
| - [x] Watched | ||
| - [x] The Home page displays all current movies in a grid view, the search bar, and the sort movies drop-down. | ||
| - [x] The Favorites page displays all favorited movies in a grid view. | ||
| - [x] The Watched page displays all watched movies in a grid view. | ||
|
|
||
| ### Walkthrough Video | ||
|
|
||
| [(https://www.loom.com/share/c29c8d99c6b341ce9b32ac4571128836?sid=e821737c-e09b-48e1-9be8-35ce3c17b324)] | ||
|
|
||
| ### 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? | ||
|
|
||
| I felt that almost all of the features that we had to implement in the project were covered by ideas in the labs, I found myself constantly looking back at past labs to refrence code I wrote there for my project. The only thing I felt a little unprepared for was the video embedding but with a bit of web searching it was actually relativly simple to implement. | ||
|
|
||
| * 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. | ||
|
|
||
| If I had more time I wish I could add more features to the site, maybe a list of theaters in the area playing the movie for each movie. And maybe some more filtering options for the type of search you can perform, like search for movies within a particular year or by rating. | ||
|
|
||
| * 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? | ||
|
|
||
| Talking with my peers a big thing I noticed was that a lot of people started the project by implementing some of the smaller features that were less code intensive which helped them quickly understand the layout of their project and how components would communicate with each other. I started with one of the bigger features which required a lot of inter-connected components which made it hard initially to figure out how the app features would communicate. | ||
|
|
||
| ### 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. | ||
|
|
||
| - First shout out to bravemaster619 at [https://dev.to/bravemaster619/simplest-way-to-embed-a-youtube-video-in-your-react-app-3bk2] for his very good guide on embedding youtube video saved me a lot of headache | ||
|
|
||
| - Shout out to my table mates again Ben, Kevin, and Thomas who all helped me bounce ideas of them and compare our features. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,157 @@ | ||
| import { useState } from 'react' | ||
| import { useState, useEffect } from 'react' | ||
| import Header from './Header' | ||
| import Body from './Body' | ||
| import SideNav from './components/SideNav' | ||
| import Footer from './Footer' | ||
| import sort from './utils/utils' | ||
| import './App.css' | ||
|
|
||
| //API Info for fetch | ||
| const API_KEY = import.meta.env.VITE_API_KEY | ||
| const options = {method: 'GET', headers: {accept: 'application/json', | ||
| Authorization: `Bearer ${API_KEY}`}} | ||
|
|
||
| const App = () => { | ||
| //States to store the current movie data | ||
| const [movieData, setMovieData] = useState([]) | ||
| const [pageNum, setPageNum] = useState(1) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Controversial, arrays start at 0 D= |
||
|
|
||
| //State Arrays for fav and watched movies | ||
| const [page,setPage] = useState('Home') | ||
| const [favMovies, setFavMovies] = useState([]) | ||
| const [watchedMovies, setWatchedMovies] = useState([]) | ||
|
|
||
| //States to store search information | ||
| const [searchData, setSearchData] = useState([]) | ||
| const [searchPage, setSearchPage] = useState(0) | ||
| const [searchString, setSearchString] = useState('') | ||
| const [searchTrigger, setSearchTrigger] = useState(0) | ||
|
|
||
| //URL for featching data from API | ||
| const nowPlayingURL = `https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${pageNum}` | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also consider using a URL builder if possible |
||
| const searchURL = `https://api.themoviedb.org/3/search/movie?query=${searchString}&page=${searchPage}` | ||
|
|
||
| //States for sort | ||
| const [sortType, setSortType] = useState('none') | ||
|
|
||
| //Rerender movie data whenever url for api call changed and append to movie data | ||
| useEffect(() => { | ||
| const fetchMovieData = async () => { | ||
| try{ | ||
| let searching = (searchString !== ''); | ||
| let URL = searching ? searchURL : nowPlayingURL; | ||
| var res = await fetch(URL, options) | ||
| if(res.ok){ | ||
| const data = await res.json(); | ||
|
|
||
| //Update movie data to store liked/watched info | ||
| data.results.forEach((movie) => { | ||
| movie.liked = false | ||
| movie.watched = false | ||
| }) | ||
|
|
||
| if(searchString === ''){ | ||
| setMovieData([...movieData, ...data.results]); | ||
| }else { | ||
| setSearchData([...searchData, ...data.results]) | ||
| } | ||
| }else{ | ||
| throw new Error("API Not Responding") | ||
| } | ||
| }catch(error){ | ||
| console.error("Error: ", error.message) | ||
| } | ||
| } | ||
| fetchMovieData(); | ||
| },[pageNum, searchTrigger]) | ||
|
|
||
| //Load more movie data by updating page num on url | ||
| const load = () => { | ||
| if(searchString === '') { | ||
| setPageNum(pageNum + 1) | ||
| }else { | ||
| setSearchPage(searchPage + 1) | ||
| setSearchTrigger(searchTrigger + 1) | ||
| } | ||
| } | ||
|
|
||
| //**-----------------Search Function-----------------**// | ||
| const clearSearch = () => { | ||
| setSearchData([]) | ||
| setSearchString('') | ||
| setPage('Home') | ||
| } | ||
|
|
||
| const search = () => { | ||
| if(searchString === ''){ | ||
| return; | ||
| } | ||
| setSearchPage(1, setPage('Search')) | ||
| setSearchTrigger(searchTrigger + 1) | ||
| } | ||
|
|
||
| const updateSearchTerm = evt => { | ||
| setSearchString(evt.target.value) | ||
| } | ||
| //**--------------------------------------------------**// | ||
|
|
||
| //**------------------Sort Function-------------------**// | ||
|
|
||
| useEffect(() => { | ||
| const sortedMovies = sort(movieData,sortType) | ||
| setMovieData(sortedMovies) | ||
| },[sortType]) | ||
|
|
||
|
|
||
| const updateSortType = evt => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you may not need this + the useEffect consider having the update setter to be part of the click handler const updateSortType = (evt) => {
setSortType(evt.target.value);
const sortedMovies = sort(movieData,sortType)
setMovieData(sortedMovies)
} |
||
| setSortType(evt.target.value) | ||
| } | ||
|
|
||
| //**--------------------------------------------------**// | ||
|
|
||
| //**---------------Like/Watch Function----------------**// | ||
|
|
||
| const openHome = () => { | ||
| setPage('Home') | ||
| } | ||
|
|
||
| const updateFavs = (movie) => { | ||
| if(movie.liked){ | ||
| setFavMovies([...favMovies, movie]) | ||
| }else{ | ||
| setFavMovies(favMovies.filter(element => element !== movie)); | ||
| } | ||
| } | ||
|
|
||
| const openFavorites = () => { | ||
| setPage('Favorites') | ||
| } | ||
|
|
||
| const updateWatched = (movie) => { | ||
| if(movie.watched){ | ||
| setWatchedMovies([...watchedMovies, movie]) | ||
| }else{ | ||
| setWatchedMovies(watchedMovies.filter(element => element !== movie)); | ||
| } | ||
| } | ||
|
|
||
| const openWatched = () => { | ||
| setPage('Watched') | ||
| } | ||
|
|
||
| //**--------------------------------------------------**// | ||
|
|
||
| return ( | ||
| <div className="App"> | ||
|
|
||
| <Header clear={clearSearch} search={search} searchTermFunction={updateSearchTerm} | ||
| searchString={searchString} sortFunc={updateSortType} display={page === 'Home' || page === 'Search'}/> | ||
|
|
||
| <SideNav homeFunc={openHome} favFunc={openFavorites} watchFunc={openWatched}/> | ||
|
|
||
| <Body data={page !== 'Home' ? (page !== 'Favorites' ? (page === 'Watched' ? watchedMovies : searchData) : favMovies) : movieData} load={load} | ||
| addToFav={updateFavs} addToWatch={updateWatched} display={page === 'Home' || page === 'Search'}/> | ||
|
|
||
| <Footer /> | ||
| </div> | ||
| ) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| .Body { | ||
| background-color: #1C1C1C; | ||
| color: #f0ffff; | ||
|
|
||
| min-height: 100vh; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import MovieList from "./components/MovieList"; | ||
| import './Body.css' | ||
|
|
||
| export default function Body({data, load, addToFav, addToWatch, display}) { | ||
|
|
||
| return ( | ||
| <section className="Body"> | ||
| <MovieList data={data} load={load} addToFav={addToFav} addToWatch={addToWatch} display={display}/> | ||
| </section> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| .Footer { | ||
| background-color: #5AE26A; | ||
| color: #1C1C1C; | ||
|
|
||
| margin-top: auto; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import './Footer.css' | ||
|
|
||
| export default function Footer() { | ||
|
|
||
| return ( | ||
| <section className="Footer"> | ||
| <p>@https://github.com/jseifferly/flixster</p> | ||
| </section> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| .Header { | ||
| background-color: #5AE26A; | ||
| color: #1C1C1C; | ||
| } | ||
|
|
||
| .Header h1 { | ||
| font-size: 300%; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import SearchForm from "./components/SearchForm"; | ||
| import './Header.css' | ||
|
|
||
| export default function Header({search,clear,searchTermFunction,searchString, sortFunc, display}) { | ||
| return ( | ||
| <section className="Header"> | ||
| <h1>Flixster</h1> | ||
| <SearchForm display={display} searchTerm={searchString} | ||
| searchFunction={search} clearFunction={clear} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can improve on naming the props, in most cases I've seen that the callbacks/functions you pass may be easier to interpet by using naming like in short drop the |
||
| searchTermFunc={searchTermFunction} sortFunc={sortFunc}/> | ||
| </section> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| .Sort { | ||
| border-radius: 20px; | ||
| background-color: gray; | ||
| color: aliceblue; | ||
|
|
||
| margin: 1vw; | ||
| padding: 0.5vw; | ||
| } | ||
|
|
||
| .Sort:hover { | ||
| cursor: pointer; | ||
| background-color: rgb(144, 144, 144); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import '../styles/LoadButton.css' | ||
|
|
||
| export default function LoadButton() { | ||
|
|
||
| return ( | ||
| <div> | ||
| <button className="loadMore">Load More</button> | ||
| </div> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this I think you can try to generate utils for fetching