Skip to content

Conversation

@jseifferly
Copy link

Flixster

Main Features

Display Movies in Grid View-
-Using CSS flexbox I created the container for the list of movie cards
Screenshot 2025-06-16 at 5 19 38 PM

Users can load more movies using "Load More" button-
-TMDB API allows you to make requests for movies by page numbers, by pressing 'load more' the page number is incremented and an additional fetch request is made.
Screenshot 2025-06-16 at 5 21 16 PM

Search Bar which allows users to search TMDB database-
-TMDB API has an additional API call for searching movies based on a given substring, I use this to make a new fetch request and store the searched data in a new container. Following this I update the currently displayed movie data. The user can click the 'enter' key or submit to search, and they can also click 'clear' to go back to the original data.
Screenshot 2025-06-16 at 5 22 08 PM

Movie details displayed when movie card is clicked-
-Each movie card has an on click listener that opens a modal upon click. The modal is repopulated each time a new movie is clicked with the new movie details. These details require an additional fetch request from the API.
Screenshot 2025-06-16 at 5 23 05 PM

Sort currently displayed movies using sort button-
-Use a sort function in the utils folder to sort the movieData state (currently displayed data) and then update the display based on the sort. 3 different methods of sorting, alphabetical, release date, and rating.
Screenshot 2025-06-16 at 5 23 42 PM

Website should implement responsive web design-

  • Tried to use as few divs as possible switching to semantic elements like ,
    , and
  • Webpage should respond to changes in window size, I used percentages in my css sizes.

Stretch Features

Embedded Video Trailers-
-Must make an additional fetch request when accessing the movie details modal (this sometimes causes the old video to still display for a second). Used an iframe component to implement the feature.
Screenshot 2025-06-16 at 5 26 50 PM

Favorite & Watched buttons
-Simple button components, added some basic animations. I also created 2 additional containers for movies, a favMovies and watchedMovies, whenever one of the buttons is clicked it adds a copy of that movie data into the correct container.
Screenshot 2025-06-16 at 5 27 31 PM

Sidebar, switches to showing only favorited or watched movies-
-Switches between what container is being shown, clicking on the star sets the favMovies container as our data, while clicking on the flag sets the watchedMovies container as our data. Also when switching to these pages I hide the search, sort, and load more features as it doesn't make sense to show them.
Screenshot 2025-06-16 at 5 28 26 PM

Screenshot 2025-06-16 at 5 28 40 PM

Jackson Kenneth Seifferly and others added 30 commits June 9, 2025 17:32
… Movie List, and Footer done. Movie Cards should be responsive to window size and are being updated dynamically by data file
Added Search and Sort features
…buttons and updating displayed movies using sidebar
src/Header.jsx Outdated
function Header({search,clear,searchTermFunction,searchString, sortFunc}) {
function Header({search,clear,searchTermFunction,searchString, sortFunc, display}) {

console.log(display)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove console logs :-)

<h1>Flixster</h1>
<SearchForm searchTerm={searchString} searchFunction={search} clearFunction={clear} searchTermFunc={searchTermFunction} sortFunc={sortFunc}/>
<SearchForm display={display} searchTerm={searchString}
searchFunction={search} clearFunction={clear}

Choose a reason for hiding this comment

The 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 onSearch vs searchFunction & onClear vs clearFunction

in short drop the Function add the on

Comment on lines +31 to +38
}else {
return (
<div className="no-movies-splash">
<h2 className="splash-text">No movies here...</h2>
<h3 className="splash-icon">{'\u{2639}'}</h3>
</div>
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As If condition is already returning, we don't need specific else here. You can do something like:
if(condition){
return <...>;
}
return <...> // this will work as else condition without explicit else :)

Comment on lines +6 to +14
if (type === 'alpha'){
return sortedArr.sort((a,b) => a.title.localeCompare(b.title));
}else if (type === 'release'){
return sortedArr.sort((a,b) => b.release_date.localeCompare(a.release_date))
}else if (type === 'vote'){
return sortedArr.sort((a,b) => b.vote_average - a.vote_average)
}else {
return arr
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about updating this into a switch case instead of long if..else..if... conditions?

src/App.jsx Outdated
}

const updateFavs = (movie,faved) => {
faved ? setFavMovies([...favMovies, movie]) : setFavMovies(favMovies.filter(element => element !== movie));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Maybe try to generate the object to be set and call setFavMovies only once

const toggleFavs = (movie,faved) => {
    const favs = faved ? [...favMovies, movie] : favMovies.filter(element => element !== movie);
    setFavMovies(favs);

src/App.jsx Outdated
<Header clear={clearSearch} search={search} searchTermFunction={updateSearchTerm} searchString={searchString} sortFunc={updateSortType}/>
<SideNav homeFunc={openHome} favFunc={openFavorites} watchFunc={openWatched}/>
<Body data={page !== 'Home' ? (page === 'Favorite' ? favMovies : watchedMovies) : movieData} load={load}/>
<Body data={page !== 'Home' ? (page === 'Favorites' ? favMovies : watchedMovies) : movieData} load={load}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the load name is not clear on what it does, maybe consider using pageNumber as prop name to make it easy to read

const [faved, setFaved] = useState(false);
const [watched, setWatched] = useState(false)

useEffect(() => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you may not need these useEffect functions you can simplify this by simply passing the addToFav & addToWatched callbacks directly to the onClick prop example

const handleFavClick = (evt) => {
  evt.stopPropagation();
  setFaved(!faved);
  addToFav(movie, !faved);
}
<span className='favButton' onClick={handleFavClick}>{faved? '\u{1F496}':'\u{1F90D}'}</span>

<h3><strong>{title}</strong></h3>
<p>Rating: {rating}</p>
<MovieButtons />
<img src={imgURL + posterSize + movie.poster_path} alt={movie.title + " poster"} />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine, but I'd suggest you try to use URL js builder just to make sure the URL is well formed https://developer.mozilla.org/en-US/docs/Web/API/URL/URL

// Base URLs:
let baseUrl = "https://developer.mozilla.org";

let A = new URL("/", baseUrl);
// => 'https://developer.mozilla.org/'

let B = new URL(baseUrl);
// => 'https://developer.mozilla.org/'

new URL("en-US/docs", B);
// => 'https://developer.mozilla.org/en-US/docs'

let D = new URL("/en-US/docs", B);
// => 'https://developer.mozilla.org/en-US/docs'

new URL("/en-US/docs", D);
// => 'https://developer.mozilla.org/en-US/docs'

new URL("/en-US/docs", A);
// => 'https://developer.mozilla.org/en-US/docs'

new URL("/en-US/docs", "https://developer.mozilla.org/fr-FR/toto");
// => 'https://developer.mozilla.org/en-US/docs'


useEffect(() => {
const fetchMovieData = async () => {
if(movie.id === undefined){

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unfortunately in JS world you want to check for undefined & null at the same time, try using

movie.id == null

Comment on lines 100 to 110
const openHome = () => {
setPage('Home')
}

const openFavorites = () => {
setPage('Favorites')
}

const openWatched = () => {
setPage('Watched')
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks repetitive, when you see something repetitive it means you can DRY (Dont Repeat Yourself)

To transform wetcode to DRY code you can do something like

const openPage(pageName) {
   setPage(pageName);
}

export default function MovieButtons() {

const handleClick = evt => {
evt.stopPropigation();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this intentionally blank?

Comment on lines +20 to +21
var res1 = await fetch(url, options)
var res2 = await fetch(videoUrl, options)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to use const instead of var keyword

also you can group promises in a single call like

const [moveies, videos] = await Promise.all([fetch(url, options), fetch(videoUrl, options)])

const App = () => {
//States to store the current movie data
const [movieData, setMovieData] = useState([])
const [pageNum, setPageNum] = useState(1)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Controversial, arrays start at 0 D=

const [searchString, setSearchString] = useState('')

//URL for featching data from API
const nowPlayingURL = `https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${pageNum}`

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also consider using a URL builder if possible

src/App.jsx Outdated
}
}
fetchMovieData();
},[url])

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it feels odd that we are relying on the url for refetching movie data based on that, I'd recommend

  1. generating the url inside the fetchMovie data function
  2. create a function outside the component scope that encapsulates the fetching logic

example:

const FETCH_OPTIONS = {method: 'GET', headers: {accept: 'application/json',
                Authorization: `Bearer ${API_KEY}`}};

async function fetchMovies(page) {
     const url = `https://api.themoviedb.org/3/movie/now_playing?language=en-US&page=${pageNum ?? 0}`;
     try{
              var res = await fetch(url, FETCH_OPTIONS)
              if(res.ok){
                  const data = await res.json();
                  return {movies: data.results, error: null };
              }else{
                  return {error: "Error Message"};
              }
          }catch(error){
              return {error: error.message};
          }
} 

src/App.jsx Outdated

//Update the url for loading more movies
useEffect(() => {
setUrl(nowPlayingURL)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we drop the fetching dependency on url, you may no longer need this

src/App.jsx Outdated

useEffect(() => {

console.log('do sort')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clean up console logs :-)

},[sortType])


const updateSortType = evt => {

Choose a reason for hiding this comment

The 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)
}

src/App.jsx Outdated
fetchMovieData();
},[pageNum])

const load = () => setPageNum(pageNum + 1)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider renaming this to loadNext or just nextPage to make it clear


//API Info for fetch
const API_KEY = import.meta.env.VITE_API_KEY
const options = {method: 'GET', headers: {accept: 'application/json',

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

// Movies.api.js

export async function fetchMovies() {
   // fetch logic here
   return {movies, error};
}

// App.jsx
const {movies, error} = await fetchMovies(page);
if (error) {
  setErrorMsg(error);
}
... 

.env Outdated
@@ -1 +1 @@
VITE_API_KEY=eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIwZmJhOGUxMThkYjlkYjQ1MDViZjJlNDIxOTA2YjBiMCIsIm5iZiI6MTc0OTUwOTQ1OC41NDU5OTk4LCJzdWIiOiI2ODQ3NjU1MmU5ODg5ZGMzMzMyMDY4OTYiLCJzY29wZXMiOlsiYXBpX3JlYWQiXSwidmVyc2lvbiI6MX0.hC15ggnHAhw1hfT_FHTOmwbKnG9c7qFyvMJeSw7LbHs'}};
VITE_API_KEY=eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIwZmJhOGUxMThkYjlkYjQ1MDViZjJlNDIxOTA2YjBiMCIsIm5iZiI6MTc0OTUwOTQ1OC41NDU5OTk4LCJzdWIiOiI2ODQ3NjU1MmU5ODg5ZGMzMzMyMDY4OTYiLCJzY29wZXMiOlsiYXBpX3JlYWQiXSwidmVyc2lvbiI6MX0.hC15ggnHAhw1hfT_FHTOmwbKnG9c7qFyvMJeSw7LbHs No newline at end of file

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙈

return ret.slice(0, -2)
}

if(movieDetails === null){

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if the movieDetails are empty?

function genreString(genres) {
let ret = ''
for(var i = 0; i < genres.length; ++i){
ret += (genres[i].name + ', ')

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const [show, setShow] = useState(false);

const close = () => setShow(false);
const open = id => event => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I follow what the id is doing for us, we are always returning the same function

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants