Advertisement
fakhrycodepolitan

React App.js

Dec 16th, 2024
16
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 9.05 KB | Source Code | 0 0
  1. import { useEffect, useState } from "react";
  2. import StarRating from "./StarRating";
  3.  
  4. const tempMovieData = [
  5.   {
  6.     imdbID: "tt15398776",
  7.     Title: "Oppenheimer",
  8.     Year: "2013",
  9.     Poster: "https://m.media-amazon.com/images/M/MV5BMDBmYTZjNjUtN2M1MS00MTQ2LTk2ODgtNzc2M2QyZGE5NTVjXkEyXkFqcGdeQXVyNzAwMjU2MTY@._V1_SX300.jpg",
  10.   },
  11.   {
  12.     imdbID: "tt1517268",
  13.     Title: "Barbie",
  14.     Year: "2023",
  15.     Poster: "https://m.media-amazon.com/images/M/MV5BNjU3N2QxNzYtMjk1NC00MTc4LTk1NTQtMmUxNTljM2I0NDA5XkEyXkFqcGdeQXVyODE5NzE3OTE@._V1_SX300.jpg",
  16.   },
  17.   {
  18.     imdbID: "tt8589698",
  19.     Title: "Teenage Mutant Ninja Turtles: Mutant Mayhem",
  20.     Year: "2023",
  21.     Poster: "https://m.media-amazon.com/images/M/MV5BYzE4MTllZTktMTIyZS00Yzg1LTg1YzAtMWQwZTZkNjNkODNjXkEyXkFqcGdeQXVyMTUzMTg2ODkz._V1_SX300.jpg",
  22.   },
  23. ];
  24.  
  25. const tempWatchedData = [
  26.   {
  27.     imdbID: "tt15398776",
  28.     Title: "Oppenheimer",
  29.     Year: "2013",
  30.     Poster: "https://m.media-amazon.com/images/M/MV5BMDBmYTZjNjUtN2M1MS00MTQ2LTk2ODgtNzc2M2QyZGE5NTVjXkEyXkFqcGdeQXVyNzAwMjU2MTY@._V1_SX300.jpg",
  31.     runtime: 180,
  32.     imdbRating: 8.6,
  33.     userRating: 10,
  34.   },
  35.   {
  36.     imdbID: "tt1517268",
  37.     Title: "Barbie",
  38.     Year: "2023",
  39.     Poster: "https://m.media-amazon.com/images/M/MV5BNjU3N2QxNzYtMjk1NC00MTc4LTk1NTQtMmUxNTljM2I0NDA5XkEyXkFqcGdeQXVyODE5NzE3OTE@._V1_SX300.jpg",
  40.     runtime: 114,
  41.     imdbRating: 7.2,
  42.     userRating: 8,
  43.   },
  44. ];
  45.  
  46. const average = (arr) => arr.reduce((acc, cur, i, arr) => acc + cur / arr.length, 0);
  47.  
  48. function Logo() {
  49.   return (
  50.     <div className="logo">
  51.       <span role="img">🎫</span>
  52.       <h1>Movie</h1>
  53.     </div>
  54.   );
  55. }
  56.  
  57. function Search({ query, setQuery }) {
  58.   return <input className="search" type="text" placeholder="Search movies..." value={query} onChange={(e) => setQuery(e.target.value)} />;
  59. }
  60.  
  61. function NumResult({ movies }) {
  62.   return (
  63.     <p className="num-results">
  64.       Found <strong>{movies?.length}</strong> results
  65.     </p>
  66.   );
  67. }
  68.  
  69. function NavBar({ children }) {
  70.   return <nav className="nav-bar">{children}</nav>;
  71. }
  72.  
  73. function MovieItem({ movie, onSelectMovieId }) {
  74.   return (
  75.     <li key={movie.imdbID} onClick={() => onSelectMovieId(movie.imdbID)}>
  76.       <img src={movie.Poster} alt={`${movie.Title} poster`} />
  77.       <h3>{movie.Title}</h3>
  78.       <div>
  79.         <p>
  80.           <span>📅</span>
  81.           <span>{movie.Year}</span>
  82.         </p>
  83.       </div>
  84.     </li>
  85.   );
  86. }
  87.  
  88. function MovieList({ movies, onSelectMovieId }) {
  89.   return (
  90.     <ul className="list list-movies">
  91.       {movies?.map((movie, index) => (
  92.         <MovieItem key={index} movie={movie} onSelectMovieId={onSelectMovieId} />
  93.       ))}
  94.     </ul>
  95.   );
  96. }
  97.  
  98. function WatchedSummary({ watched }) {
  99.   const avgImdbRating = average(watched.map((movie) => movie.imdbRating));
  100.   const avgUserRating = average(watched.map((movie) => movie.userRating));
  101.   const avgRuntime = average(watched.map((movie) => movie.runtime));
  102.   return (
  103.     <div className="summary">
  104.       <h2>Movies you watched</h2>
  105.       <div>
  106.         <p>
  107.           <span>#️⃣</span>
  108.           <span>{watched.length} movies</span>
  109.         </p>
  110.         <p>
  111.           <span>🎬</span>
  112.           <span>{avgImdbRating.toFixed(1)}</span>
  113.         </p>
  114.         <p>
  115.           <span>🌟</span>
  116.           <span>{avgUserRating.toFixed(1)}</span>
  117.         </p>
  118.         <p>
  119.           <span></span>
  120.           <span>{Math.trunc(avgRuntime)} min</span>
  121.         </p>
  122.       </div>
  123.     </div>
  124.   );
  125. }
  126.  
  127. function WatchedItem({ movie }) {
  128.   return (
  129.     <li key={movie.imdbID}>
  130.       <img src={movie.poster} alt={`${movie.title} poster`} />
  131.       <h3>{movie.title}</h3>
  132.       <div>
  133.         <p>
  134.           <span>🎬</span>
  135.           <span>{movie.imdbRating.toFixed(1)}</span>
  136.         </p>
  137.         <p>
  138.           <span>🌟</span>
  139.           <span>{movie.userRating}</span>
  140.         </p>
  141.         <p>
  142.           <span></span>
  143.           <span>{Math.trunc(movie.runtime)} min</span>
  144.         </p>
  145.       </div>
  146.     </li>
  147.   );
  148. }
  149.  
  150. function WatchedList({ watched }) {
  151.   return (
  152.     <ul className="list">
  153.       {watched.map((movie, index) => (
  154.         <WatchedItem key={index} movie={movie} />
  155.       ))}
  156.     </ul>
  157.   );
  158. }
  159.  
  160. function BoxMovies({ children }) {
  161.   const [isOpen, setIsOpen] = useState(true);
  162.   return (
  163.     <div className="box">
  164.       <button className="btn-toggle" onClick={() => setIsOpen((open) => !open)}>
  165.         {isOpen ? "–" : "+"}
  166.       </button>
  167.       {isOpen && children}
  168.     </div>
  169.   );
  170. }
  171.  
  172. function MovieDetails({ selectedId, onCloseMovie, onAddWatched }) {
  173.   const [movie, setMovie] = useState([]);
  174.   const [isLoading, setIsLoading] = useState(false);
  175.  
  176.   const {
  177.     Title: title,
  178.     Year: year,
  179.     Poster: poster,
  180.     imdbRating,
  181.     Runtime: runtime,
  182.     Plot: plot,
  183.     Genre: genre,
  184.     Actors: actors,
  185.     Director: director
  186.   } = movie;
  187.  
  188.   function handleAddWatched() {
  189.     const newWatchedMovie = {
  190.       imdbID: selectedId,
  191.       title,
  192.       year,
  193.       poster,
  194.       imdbRating: Number(imdbRating),
  195.       runtime: Number(runtime.split(" ").at(0)),
  196.     };
  197.     onAddWatched(newWatchedMovie);
  198.     onCloseMovie();
  199.   }
  200.  
  201.   useEffect(() => {
  202.     async function getMovieDetails() {
  203.       setIsLoading(true);
  204.       const response = await fetch(`https://www.omdbapi.com/?apikey=${API_KEY}&i=${selectedId}`);
  205.       const data = await response.json();
  206.       setMovie(data);
  207.       setIsLoading(false);
  208.     }
  209.     getMovieDetails();
  210.   }, [selectedId]);
  211.   return (
  212.     <div className="details">
  213.       {isLoading ? (
  214.         <Loader />
  215.       ) : (
  216.         <>
  217.           <header>
  218.             <button className="btn-back" onClick={onCloseMovie}>
  219.               &#x2715;
  220.             </button>
  221.             <img src={poster} alt={"${title} poster"} />
  222.             <div className="details-overview">
  223.               <h2>{title}</h2>
  224.               <p>
  225.                 <span></span>
  226.                 <span>{imdbRating}</span>
  227.               </p>
  228.               <p>
  229.                 <span></span>
  230.                 <span>{runtime}</span>
  231.               </p>
  232.             </div>
  233.           </header>
  234.           <section>
  235.             <p>
  236.               <em>{plot}</em>
  237.             </p>
  238.             <p>Genre : {genre}</p>
  239.             <p>Straing : {actors}</p>
  240.             <div className="rating">
  241.               <StarRating max={10} size={24} />
  242.               <button className="btn-add" onClick={handleAddWatched}>
  243.                 + Add to Watched
  244.               </button>
  245.             </div>
  246.           </section>
  247.         </>
  248.       )}
  249.     </div>
  250.   );
  251. }
  252.  
  253. function Main({ children }) {
  254.   return <main className="main">{children}</main>;
  255. }
  256.  
  257. function Loader() {
  258.   return (
  259.     <div className="loader">
  260.       <div className="Loading-bar">
  261.         <div className="bar"></div>
  262.       </div>
  263.     </div>
  264.   );
  265. }
  266.  
  267. function ErrorMessage({ message }) {
  268.   return (
  269.     <div className="error">
  270.       <span>X</span> {message}
  271.     </div>
  272.   );
  273. }
  274.  
  275. const API_KEY = "54a84779";
  276. export default function App() {
  277.   const [movies, setMovies] = useState([]);
  278.   const [watched, setWatched] = useState([]);
  279.   const [isLoading, setIsLoading] = useState(false);
  280.   const [error, setError] = useState("");
  281.   const [query, setQuery] = useState("hitler");
  282.   const [selectedMovieId, setSelectedMovieId] = useState(null);
  283.  
  284.   function handleSelectMovieId(id) {
  285.     setSelectedMovieId((selectedId) => (selectedId === id ? null : id));
  286.   }
  287.  
  288.   function handleAddWatched(movie) {
  289.     setWatched((watched) => [...watched, movie]);
  290.   }
  291.  
  292.   function handleCloseMovie() {
  293.     setSelectedMovieId(null);
  294.   }
  295.  
  296.   useEffect(() => {
  297.     async function fetchMovie() {
  298.       try {
  299.         setIsLoading(true);
  300.         setError("");
  301.         const res = await fetch(`https://www.omdbapi.com/?s=${query}&apikey=${API_KEY}`);
  302.         if (!res.ok) throw new Error("Something went wrong");
  303.         const data = await res.json();
  304.  
  305.         if (data.Response === "false") throw new Error(data.Error);
  306.  
  307.         setMovies(data.Search);
  308.       } catch (err) {
  309.         setError(err.message);
  310.       } finally {
  311.         setIsLoading(false);
  312.       }
  313.     }
  314.  
  315.     if (query.length < 3) {
  316.       setMovies([]);
  317.       setError("");
  318.       return;
  319.     }
  320.  
  321.     fetchMovie();
  322.   }, [query]);
  323.  
  324.   return (
  325.     <>
  326.       <NavBar>
  327.         <Logo />
  328.         <Search query={query} setQuery={setQuery} />
  329.         <NumResult movies={movies} />
  330.       </NavBar>
  331.       <Main>
  332.         <BoxMovies>
  333.           {" "}
  334.           {isLoading && <Loader />}
  335.           {error && <ErrorMessage message={error} />}
  336.           {!isLoading && !error && <MovieList movies={movies} onSelectMovieId={handleSelectMovieId} />}
  337.         </BoxMovies>
  338.  
  339.         <BoxMovies>
  340.           {selectedMovieId ? (
  341.             <MovieDetails selectedId={selectedMovieId} onCloseMovie={handleCloseMovie} onAddWatched={handleAddWatched} />
  342.           ) : (
  343.             <>
  344.               <WatchedSummary watched={watched} />
  345.               <WatchedList watched={watched} />
  346.             </>
  347.           )}
  348.         </BoxMovies>
  349.       </Main>
  350.     </>
  351.   );
  352. }
  353.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement