diff --git a/README.md b/README.md index 4c8fd7876..eb1ba7add 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,12 @@ # Project Movies -Replace this readme with your own information about your project. +The assignment was to produce a movie site showing popular movies, using react and react- routers, as well as APIs fetches. -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. ## The problem -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? +The styling took me more time than expected. ## View it live -Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. +https://yourmoviesiteby-ym.netlify.app/ diff --git a/code/package-lock.json b/code/package-lock.json index bb51e893e..3fa72176e 100644 --- a/code/package-lock.json +++ b/code/package-lock.json @@ -16,7 +16,8 @@ "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.9.0" }, "devDependencies": { "react-scripts": "5.0.1" @@ -3124,6 +3125,14 @@ } } }, + "node_modules/@remix-run/router": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.4.0.tgz", + "integrity": "sha512-BJ9SxXux8zAg991UmT8slpwpsd31K1dHHbD3Ba4VzD+liLQ4WAMSxQp2d2ZPRPfN0jN2NPRowcSSoM7lCaF08Q==", + "engines": { + "node": ">=14" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -14384,6 +14393,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.9.0.tgz", + "integrity": "sha512-51lKevGNUHrt6kLuX3e/ihrXoXCa9ixY/nVWRLlob4r/l0f45x3SzBvYJe3ctleLUQQ5fVa4RGgJOTH7D9Umhw==", + "dependencies": { + "@remix-run/router": "1.4.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.9.0.tgz", + "integrity": "sha512-/seUAPY01VAuwkGyVBPCn1OXfVbaWGGu4QN9uj0kCPcTyNYgL1ldZpxZUpRU7BLheKQI4Twtl/OW2nHRF1u26Q==", + "dependencies": { + "@remix-run/router": "1.4.0", + "react-router": "6.9.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -19509,6 +19548,11 @@ "source-map": "^0.7.3" } }, + "@remix-run/router": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.4.0.tgz", + "integrity": "sha512-BJ9SxXux8zAg991UmT8slpwpsd31K1dHHbD3Ba4VzD+liLQ4WAMSxQp2d2ZPRPfN0jN2NPRowcSSoM7lCaF08Q==" + }, "@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -27684,6 +27728,23 @@ "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", "dev": true }, + "react-router": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.9.0.tgz", + "integrity": "sha512-51lKevGNUHrt6kLuX3e/ihrXoXCa9ixY/nVWRLlob4r/l0f45x3SzBvYJe3ctleLUQQ5fVa4RGgJOTH7D9Umhw==", + "requires": { + "@remix-run/router": "1.4.0" + } + }, + "react-router-dom": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.9.0.tgz", + "integrity": "sha512-/seUAPY01VAuwkGyVBPCn1OXfVbaWGGu4QN9uj0kCPcTyNYgL1ldZpxZUpRU7BLheKQI4Twtl/OW2nHRF1u26Q==", + "requires": { + "@remix-run/router": "1.4.0", + "react-router": "6.9.0" + } + }, "react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", diff --git a/code/package.json b/code/package.json index 7aad26ebc..9736b71ad 100644 --- a/code/package.json +++ b/code/package.json @@ -11,7 +11,8 @@ "eslint-plugin-react": "^7.30.1", "eslint-plugin-react-hooks": "^4.6.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.9.0" }, "scripts": { "start": "react-scripts start", diff --git a/code/public/cinemaLogo.jpg b/code/public/cinemaLogo.jpg new file mode 100644 index 000000000..413ae21f9 Binary files /dev/null and b/code/public/cinemaLogo.jpg differ diff --git a/code/public/cinema_city.png b/code/public/cinema_city.png new file mode 100644 index 000000000..404fc8ac6 Binary files /dev/null and b/code/public/cinema_city.png differ diff --git a/code/src/App.js b/code/src/App.js index f2007d229..e752cdb7c 100644 --- a/code/src/App.js +++ b/code/src/App.js @@ -1,9 +1,28 @@ +import { Header } from 'components/Header'; +import { MovieDetails } from 'components/MovieDetails'; +import { MovieList } from 'components/MovieList'; +import { Tvseries } from 'components/Tvseries' +import { TvseriesDetails } from 'components/TvseriesDetails'; +import { Upcoming } from 'components/Upcoming' +import { NotFound } from 'components/NotFound'; import React from 'react'; +import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; +import { UpcomingDetails } from 'components/UpcomingDetails'; export const App = () => { return ( -
- Find me in src/app.js! -
+ +
+ + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + ); } diff --git a/code/src/components/Header.js b/code/src/components/Header.js new file mode 100644 index 000000000..02573ec9c --- /dev/null +++ b/code/src/components/Header.js @@ -0,0 +1,23 @@ +import React from 'react'; +import { NavLink } from 'react-router-dom'; + +export const Header = () => { + return ( +
+
+ +
+
    +
  • + Home +
  • +
  • + TV-serise +
  • +
  • + Upcoming +
  • +
+
+ ) +} \ No newline at end of file diff --git a/code/src/components/MovieDetails.js b/code/src/components/MovieDetails.js new file mode 100644 index 000000000..376edef44 --- /dev/null +++ b/code/src/components/MovieDetails.js @@ -0,0 +1,59 @@ + +import React, { useEffect, useState } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { MOVIE_DETAILS_URL } from 'utils/urls'; + +export const MovieDetails = () => { + const [details, setDetails] = useState([]) + const [loading, setLoading] = useState(false); + const navigate = useNavigate(); + const { id } = useParams(); + + const onBackButtonClick = () => { + navigate(-1); + } + + useEffect(() => { + fetch(MOVIE_DETAILS_URL(id)) + .then((response) => response.json()) + .then((data) => setDetails(data)) + .catch((error) => alert(error, 'error')) + .finally(() => setLoading(false)); + }, [id]) + + if (loading) { + return ( +
+

Loading..

+
+ ); + } + + return ( +
+ + poster + + + +
+
+ profile +
+
+

{details.title}

+
+ {(Math.round(details.vote_average * 10) / 10)}⭐ +
+
+

{details.overview}

+
+
+
+ +
+ ) +} \ No newline at end of file diff --git a/code/src/components/MovieList.js b/code/src/components/MovieList.js new file mode 100644 index 000000000..43278a1ac --- /dev/null +++ b/code/src/components/MovieList.js @@ -0,0 +1,45 @@ +import React, { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; +import { MOVIE_LIST_URL } from 'utils/urls'; + +export const MovieList = () => { + const [movieList, setMovieList] = useState([]); + const [loading, setLoading] = useState(false); + + useEffect(() => { + fetch(MOVIE_LIST_URL) + .then((response) => response.json()) + .then((data) => setMovieList(data.results)) + .catch((error) => alert(error, 'error')) + .finally(() => setLoading(false)); + }, []) + + if (loading) { + return ( +
+

Loading..

+
+ ); + } + + return ( +
+

Popular right now

+
+ {movieList.map((singleMovie) => { + return ( +
+ + {singleMovie.title} +
+

{singleMovie.original_title}

+

Release date: {singleMovie.release_date}

+
+ +
+ ) + })} +
+
+ ) +} diff --git a/code/src/components/NotFound.js b/code/src/components/NotFound.js new file mode 100644 index 000000000..346090725 --- /dev/null +++ b/code/src/components/NotFound.js @@ -0,0 +1,15 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; + +export const NotFound = () => { + const navigate = useNavigate() + const onBackButtonClick = () => { + navigate('/') + } + return ( +
+

Sorry,nothing here

+ +
+ ) +} \ No newline at end of file diff --git a/code/src/components/Tvseries.js b/code/src/components/Tvseries.js new file mode 100644 index 000000000..118257e03 --- /dev/null +++ b/code/src/components/Tvseries.js @@ -0,0 +1,45 @@ +import React, { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; +import { TV_SERIES_URL } from 'utils/urls'; + +export const Tvseries = () => { + const [tvList, setTvList] = useState([]); + const [loading, setLoading] = useState(false); + + useEffect(() => { + fetch(TV_SERIES_URL) + .then((response) => response.json()) + .then((data) => setTvList(data.results)) + .catch((error) => alert(error, 'error')) + .finally(() => setLoading(false)); + }, []) + + if (loading) { + return ( +
+

Loading..

+
+ ); + } + + return ( +
+

Popular TV-Series

+
+ {tvList.map((singleTv) => { + return ( +
+ + {singleTv.name} +
+

{singleTv.original_name}

+

Release date: {singleTv.first_air_date}

+
+ +
+ ) + })} +
+
+ ) +} diff --git a/code/src/components/TvseriesDetails.js b/code/src/components/TvseriesDetails.js new file mode 100644 index 000000000..54d2f549a --- /dev/null +++ b/code/src/components/TvseriesDetails.js @@ -0,0 +1,58 @@ + +import React, { useEffect, useState } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { TV_SERIES_DETAILS_URL } from 'utils/urls'; + +export const TvseriesDetails = () => { + const [details, setDetails] = useState([]) + const [loading, setLoading] = useState(false); + const navigate = useNavigate(); + const { id } = useParams(); + + const onBackButtonClick = () => { + navigate(-1); + } + + useEffect(() => { + fetch(TV_SERIES_DETAILS_URL(id)) + .then((response) => response.json()) + .then((data) => setDetails(data)) + .catch((error) => alert(error, 'error')) + .finally(() => setLoading(false)); + }, [id]) + + if (loading) { + return ( +
+

Loading..

+
+ ); + } + + return ( +
+ poster + + + +
+
+ profile +
+
+

{details.name}

+
+ {(Math.round(details.vote_average * 10) / 10)}⭐ +
+
+

{details.overview}

+
+
+
+ +
+ ) +} \ No newline at end of file diff --git a/code/src/components/Upcoming.js b/code/src/components/Upcoming.js new file mode 100644 index 000000000..9b3660309 --- /dev/null +++ b/code/src/components/Upcoming.js @@ -0,0 +1,45 @@ +import React, { useState, useEffect } from 'react'; +import { Link } from 'react-router-dom'; +import { UPCOMING_URL } from 'utils/urls'; + +export const Upcoming = () => { + const [upcoming, setUpcoming] = useState([]); + const [loading, setLoading] = useState(false); + + useEffect(() => { + fetch(UPCOMING_URL) + .then((response) => response.json()) + .then((data) => setUpcoming(data.results)) + .catch((error) => alert(error, 'error')) + .finally(() => setLoading(false)); + }, []) + + if (loading) { + return ( +
+

Loading..

+
+ ); + } + + return ( +
+

Upcoming soon

+
+ {upcoming.map((singleMovie) => { + return ( +
+ + {singleMovie.title} +
+

{singleMovie.original_title}

+

Release date: {singleMovie.release_date}

+
+ +
+ ) + })} +
+
+ ) +} diff --git a/code/src/components/UpcomingDetails.js b/code/src/components/UpcomingDetails.js new file mode 100644 index 000000000..725914f0f --- /dev/null +++ b/code/src/components/UpcomingDetails.js @@ -0,0 +1,59 @@ + +import React, { useEffect, useState } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { UPCOMING_DETAILS_URL } from 'utils/urls'; + +export const UpcomingDetails = () => { + const [details, setDetails] = useState([]) + const [loading, setLoading] = useState(false); + const navigate = useNavigate(); + const { id } = useParams(); + + const onBackButtonClick = () => { + navigate(-1); + } + + useEffect(() => { + fetch(UPCOMING_DETAILS_URL(id)) + .then((response) => response.json()) + .then((data) => setDetails(data)) + .catch((error) => alert(error, 'error')) + .finally(() => setLoading(false)); + }, [id]) + + if (loading) { + return ( +
+

Loading..

+
+ ); + } + + return ( +
+ + poster + + + +
+
+ profile +
+
+

{details.title}

+
+ {(Math.round(details.vote_average * 10) / 10)}⭐ +
+
+

{details.overview}

+
+
+
+ +
+ ) +} \ No newline at end of file diff --git a/code/src/index.css b/code/src/index.css index 4a1df4db7..dfe18ff5d 100644 --- a/code/src/index.css +++ b/code/src/index.css @@ -5,9 +5,328 @@ body { sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + background-color: grey; + box-sizing: border-box; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } + +/*header*/ +header { + position: fixed; + top: 0; + width: 100%; + height: 80px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background: black; + padding: 35px 0; + z-index: 1; +} + +.logoImg { + margin: 10px; + width: 150px; + height: 80px; + +} + +.navlinkWrapper { + display: flex; + padding: 0; +} +.link { + padding: 3px; + list-style: none; +} +.link:hover{ + transform: scale(1.1); + transition: transform 0.4s; + cursor: pointer + +} +a { + text-decoration: none; + color: #F9F9F9 +} + + +/*list*/ +.popular-movie, +.popular-tv, +.upcoming-soon{ + color: white; + font-size: 28px; + text-align: center; + font-weight: 600; + padding-top: 15px; +} +.movieListWrapper, +.tvListWrapper, +.upcomingWrapper { + margin: 160px 1px; + +} +.popular-movie-container, +.popular-tv-container, +.upcoming-container{ + display: flex; + flex-wrap: wrap; + row-gap: 10px; + +} + +.movie-wrapper, +.tv-wrapper, +.upcoming-wrapper{ + width: 50%; + position: relative; +} + +.movie-wrapper img, +.tv-wrapper img, +.upcoming-wrapper img{ + width: 97%; + border: solid #a09c9c5e 0.5px; +} + +.movie-wrapper .hoverBox, +.tv-wrapper .hoverBox, +.upcoming-wrapper .hoverBox{ + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(0,0,0,0.75); + display: none; + padding: 20px; +} +.movie-wrapper .hoverBox h1, +.tv-wrapper .hoverBox h1, +.upcoming-wrapper .hoverBox h1{ + font-size: 20px; + color: #F9F9F9; + margin-top: auto; + margin-bottom: 0; +} +.movie-wrapper .hoverBox p, +.tv-wrapper .hoverBox p, +.upcoming-wrapper .hoverBox p{ + color: #F9F9F9; +} + +.movie-wrapper:hover .hoverBox, +.tv-wrapper:hover .hoverBox{ + display: flex; + flex-wrap: wrap; + flex-direction: column; + justify-content: flex-end; +} +.upcoming-wrapper:hover .hoverBox{ + display: flex; + flex-wrap: wrap; + flex-direction: column; + justify-content: flex-end; + +} +.hoverTitle{ + font-size: 20px; + color: #F9F9F9; + margin-top: auto; + margin-bottom: 0; + +} +.hoverDate{ + color: #F9F9F9; + +} + + +/*details*/ +.movie-detailsWrapper, +.tvDetailsWrapper, +.upcoming-detailsWrapper { + height: 100vh; + display: flex; + flex-direction: column; + position: absolute; + +} + +.backBtn { + position: absolute; + top: 20%; + left: 5%; + color: white; + font-size: 20px; + font-weight: bold; + background-color: transparent; + border-radius: 10px; + cursor: pointer; + padding: 8px; + transform: scale(1.0); + transition: transform 0.6s; + +} + +.backBtn:hover { + transform: scale(1.2); +} + +.movie-imge-backdrop, +.tv-imge-backdrop, +.upcoming-imge-backdrop { + object-fit: cover; + height: 100vh; + width: 100vw; + +} + +.movie-detailsBox, +.tv-detailsBox, +.upcoming-detailsBox { + height: auto; + display: flex; + flex-direction: column; + position: absolute; + bottom: 5%; + top: 40%; + align-items: flex-end; + justify-content: space-around; +} + +.title { + display: flex; + flex-direction: column; + padding: 15px 15px 0 15px; + color: white; + background-color: rgba(30, 29, 29, 0.582); + border-radius: 15px; + margin-left: 5px; +} + + + +/***************tablet design******************************/ +@media (min-width:600px) { + + .movie-detailsBox, + .tv-detailsBox, + .upcoming-detailsBox { + height: auto; + display: flex; + flex-direction: column; + position: absolute; + bottom: 5%; + align-items: flex-end; + justify-content: space-around; + } + +.link{ + padding: 15px +} + + + +} + + + + +/****************desktop design normal*********************/ +@media (min-width:992px) { + + header { + position: fixed; + top: 0; + width: 100%; + height: auto; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + background: black; + z-index: 1; + } + + + + + +/*List*/ +.movie-wrapper, +.tv-wrapper, +.upcoming-wrapper{ + width: 25%; + position: relative; +} + +/*details page styling*/ +.movie-detailsWrapper, +.tvDetailsWrapper, +.upcoming-detailsWrapper { + display: flex; + flex-direction: column; + position: absolute; + +} + +.backBtn { + position: absolute; + top: 20%; + left: 5%; + color: white; + font-size: 20px; + font-weight: bold; + background-color: transparent; + border-radius: 10px; + cursor: pointer; + padding: 8px; + transform: scale(1.0); + transition: transform 0.6s; + +} + +.backBtn:hover { + transform: scale(1.2); +} + +.movie-imge-backdrop, +.tv-imge-backdrop, +.upcoming-imge-backdrop { + object-fit: cover; + height: 100vh; + width: 100vw; + +} + +.movie-detailsBox, +.tv-detailsBox, +.upcoming-detailsBox { + display: flex; + position: absolute; + flex-direction: row; + bottom: 5%; + padding: 20px; + align-items: flex-end; + justify-content: space-around; +} + +.title { + height: 300px; + width: 400px; + display: flex; + flex-direction: column; + padding: 15px 15px 0 15px; + color: white; + background-color: rgba(30, 29, 29, 0.582); + border-radius: 15px; + margin-left: 5px; +} +} \ No newline at end of file diff --git a/code/src/utils/urls.js b/code/src/utils/urls.js new file mode 100644 index 000000000..d3707d335 --- /dev/null +++ b/code/src/utils/urls.js @@ -0,0 +1,8 @@ +export const MOVIE_LIST_URL = 'https://api.themoviedb.org/3/movie/popular?api_key=86e13ee94ff829d42c804ae17826dc54'; +export const MOVIE_DETAILS_URL = (id) => `https://api.themoviedb.org/3/movie/${id}?api_key=86e13ee94ff829d42c804ae17826dc54&language=en-US`; + +export const TV_SERIES_URL = 'https://api.themoviedb.org/3/tv/popular?api_key=86e13ee94ff829d42c804ae17826dc54'; +export const TV_SERIES_DETAILS_URL = (id) => `https://api.themoviedb.org/3/tv/${id}?api_key=86e13ee94ff829d42c804ae17826dc54&language=en-US`; + +export const UPCOMING_URL = 'https://api.themoviedb.org/3/movie/upcoming?api_key=86e13ee94ff829d42c804ae17826dc54&language=en-US&page=1®ion=SE'; +export const UPCOMING_DETAILS_URL = (id) => `https://api.themoviedb.org/3/movie/${id}?api_key=86e13ee94ff829d42c804ae17826dc54&language=en-US`; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..99196158c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "project-movies", + "lockfileVersion": 3, + "requires": true, + "packages": {} +}