From 15ca0dbb328fa7a8bc4f1f466796980d06b3f30a Mon Sep 17 00:00:00 2001 From: slikNET Date: Mon, 31 Jul 2023 15:02:33 +0300 Subject: [PATCH] module work --- package-lock.json | 121 ++++++++++++++++++ package.json | 5 + public/index.html | 4 + src/App.css | 0 src/App.js | 11 -- src/components/AsideNav/AsideNav.js | 23 ++++ src/components/Header/Header.js | 13 ++ src/components/Header/Nav/Nav.js | 22 ++++ .../GenreBadges/GenreBadges.js | 16 +++ .../GenreBadges/badge.module.css | 6 + .../MoviesContainer/MovieCard/MovieCard.js | 47 +++++++ .../MovieDetail/ImgPlaceholder.js | 17 +++ .../MovieDetail/MovieDetail.js | 64 +++++++++ .../MovieDetail/movie.module.css | 33 +++++ .../MoviesContainer/MovieInfo/MovieInfo.js | 17 +++ .../MoviesContainer/MoviesList/MoviesList.js | 56 ++++++++ .../MoviesList/movies.module.css | 8 ++ .../PosterPreview/ImgPlaceholder.js | 17 +++ .../PosterPreview/PosterPreview.js | 27 ++++ .../PosterPreview/poster.module.css | 3 + src/components/PageLoader/PageLoader.js | 11 ++ src/components/Pagination/Pagination.js | 53 ++++++++ src/components/index.js | 12 ++ src/constants/index.js | 1 + src/constants/urls.js | 39 ++++++ src/index.css | 13 -- src/index.js | 9 +- src/layouts/MainLayout.js | 26 ++++ src/layouts/SingleLayout.js | 18 +++ src/layouts/index.js | 2 + src/pages/HomePage/HomePage.js | 11 ++ src/pages/MovieDetailPage/MovieDetailPage.js | 29 +++++ src/pages/MoviesPage/MoviesPage.js | 29 +++++ src/pages/UserPage/UserPage.js | 12 ++ src/pages/index.js | 4 + src/roter.js | 41 ++++++ src/services/apiService.js | 14 ++ src/services/genreService.js | 8 ++ src/services/index.js | 2 + src/services/movieService.js | 10 ++ 40 files changed, 825 insertions(+), 29 deletions(-) delete mode 100644 src/App.css delete mode 100644 src/App.js create mode 100644 src/components/AsideNav/AsideNav.js create mode 100644 src/components/Header/Header.js create mode 100644 src/components/Header/Nav/Nav.js create mode 100644 src/components/MoviesContainer/GenreBadges/GenreBadges.js create mode 100644 src/components/MoviesContainer/GenreBadges/badge.module.css create mode 100644 src/components/MoviesContainer/MovieCard/MovieCard.js create mode 100644 src/components/MoviesContainer/MovieDetail/ImgPlaceholder.js create mode 100644 src/components/MoviesContainer/MovieDetail/MovieDetail.js create mode 100644 src/components/MoviesContainer/MovieDetail/movie.module.css create mode 100644 src/components/MoviesContainer/MovieInfo/MovieInfo.js create mode 100644 src/components/MoviesContainer/MoviesList/MoviesList.js create mode 100644 src/components/MoviesContainer/MoviesList/movies.module.css create mode 100644 src/components/MoviesContainer/PosterPreview/ImgPlaceholder.js create mode 100644 src/components/MoviesContainer/PosterPreview/PosterPreview.js create mode 100644 src/components/MoviesContainer/PosterPreview/poster.module.css create mode 100644 src/components/PageLoader/PageLoader.js create mode 100644 src/components/Pagination/Pagination.js create mode 100644 src/components/index.js create mode 100644 src/constants/index.js create mode 100644 src/constants/urls.js delete mode 100644 src/index.css create mode 100644 src/layouts/MainLayout.js create mode 100644 src/layouts/SingleLayout.js create mode 100644 src/layouts/index.js create mode 100644 src/pages/HomePage/HomePage.js create mode 100644 src/pages/MovieDetailPage/MovieDetailPage.js create mode 100644 src/pages/MoviesPage/MoviesPage.js create mode 100644 src/pages/UserPage/UserPage.js create mode 100644 src/pages/index.js create mode 100644 src/roter.js create mode 100644 src/services/apiService.js create mode 100644 src/services/genreService.js create mode 100644 src/services/index.js create mode 100644 src/services/movieService.js diff --git a/package-lock.json b/package-lock.json index ece0ffc..e3b37a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,14 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.4.0", "react": "^18.2.0", + "react-content-loader": "^6.2.1", "react-dom": "^18.2.0", + "react-paginate": "^8.2.0", + "react-router-dom": "^6.14.2", "react-scripts": "5.0.1", + "react-star-ratings": "^2.3.0", "web-vitals": "^2.1.4" } }, @@ -3214,6 +3219,14 @@ } } }, + "node_modules/@remix-run/router": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.2.tgz", + "integrity": "sha512-7Lcn7IqGMV+vizMPoEl5F0XDshcdDYtMI6uJLQdQz5CfZAwy3vvGKYSUk789qndt5dEC4HfSjviSYlSoHGL2+A==", + "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", @@ -5209,6 +5222,29 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -5869,6 +5905,11 @@ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==" }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/clean-css": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", @@ -14156,6 +14197,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -14300,6 +14346,17 @@ "node": ">=14" } }, + "node_modules/react-content-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/react-content-loader/-/react-content-loader-6.2.1.tgz", + "integrity": "sha512-6ONbFX+Hi3SHuP66JB8CPvJn372pj+qwltJV0J8z/8MFrq98I1cbFdZuhDWeQXu3CFxiiDTXJn7DFxx2ZvrO7g==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16.0.0" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -14439,6 +14496,17 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-paginate": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/react-paginate/-/react-paginate-8.2.0.tgz", + "integrity": "sha512-sJCz1PW+9PNIjUSn919nlcRVuleN2YPoFBOvL+6TPgrH/3lwphqiSOgdrLafLdyLDxsgK+oSgviqacF4hxsDIw==", + "dependencies": { + "prop-types": "^15" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18" + } + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -14447,6 +14515,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.2.tgz", + "integrity": "sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ==", + "dependencies": { + "@remix-run/router": "1.7.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.2.tgz", + "integrity": "sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg==", + "dependencies": { + "@remix-run/router": "1.7.2", + "react-router": "6.14.2" + }, + "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", @@ -14519,6 +14617,29 @@ } } }, + "node_modules/react-star-ratings": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-star-ratings/-/react-star-ratings-2.3.0.tgz", + "integrity": "sha512-34Z/oFNDRRn4ZcX7F3t9ccnpo7SQ32gD/vsusQOBc6B6vlqaGR6tke1/Yx3jTDjemKRSmXqhKgpPTR7/JAXq6A==", + "dependencies": { + "classnames": "^2.2.1", + "prop-types": "^15.6.0", + "react": "^16.1.0" + } + }, + "node_modules/react-star-ratings/node_modules/react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/package.json b/package.json index 4d6876b..24f506b 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,14 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.4.0", "react": "^18.2.0", + "react-content-loader": "^6.2.1", "react-dom": "^18.2.0", + "react-paginate": "^8.2.0", + "react-router-dom": "^6.14.2", "react-scripts": "5.0.1", + "react-star-ratings": "^2.3.0", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/public/index.html b/public/index.html index 949e559..df41a19 100644 --- a/public/index.html +++ b/public/index.html @@ -10,10 +10,14 @@ /> + + React App
+ + diff --git a/src/App.css b/src/App.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/App.js b/src/App.js deleted file mode 100644 index 3c96b66..0000000 --- a/src/App.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; - -const App = () => { - return ( - <> - - - ); -}; - -export default App; \ No newline at end of file diff --git a/src/components/AsideNav/AsideNav.js b/src/components/AsideNav/AsideNav.js new file mode 100644 index 0000000..dbe026e --- /dev/null +++ b/src/components/AsideNav/AsideNav.js @@ -0,0 +1,23 @@ +import React from 'react'; +import {NavLink} from "react-router-dom"; + +import {urls} from "../../constants"; + +const AsideNav = () => { + return ( + <> +
+ + movie + Movies + + + account_circle + User Account + +
+ + ); +}; + +export {AsideNav}; \ No newline at end of file diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js new file mode 100644 index 0000000..9c1989d --- /dev/null +++ b/src/components/Header/Header.js @@ -0,0 +1,13 @@ +import React from 'react'; + +import {Nav} from "./Nav/Nav"; + +const Header = () => { + return ( +
+
+ ); +}; + +export {Header}; \ No newline at end of file diff --git a/src/components/Header/Nav/Nav.js b/src/components/Header/Nav/Nav.js new file mode 100644 index 0000000..6bf5670 --- /dev/null +++ b/src/components/Header/Nav/Nav.js @@ -0,0 +1,22 @@ +import React from 'react'; +import {NavLink} from "react-router-dom"; + +import {urls} from "../../../constants"; + +const Nav = () => { + return ( + + ); +}; + +export {Nav}; \ No newline at end of file diff --git a/src/components/MoviesContainer/GenreBadges/GenreBadges.js b/src/components/MoviesContainer/GenreBadges/GenreBadges.js new file mode 100644 index 0000000..ebd0c5b --- /dev/null +++ b/src/components/MoviesContainer/GenreBadges/GenreBadges.js @@ -0,0 +1,16 @@ +import React from 'react'; + +import css from './badge.module.css'; +const GenreBadges = ({genres}) => { + return ( + <> + {genres &&
+ {genres.map(genre => + {genre.name} + )} +
} + + ); +}; + +export {GenreBadges}; \ No newline at end of file diff --git a/src/components/MoviesContainer/GenreBadges/badge.module.css b/src/components/MoviesContainer/GenreBadges/badge.module.css new file mode 100644 index 0000000..c333382 --- /dev/null +++ b/src/components/MoviesContainer/GenreBadges/badge.module.css @@ -0,0 +1,6 @@ +.badges{ + overflow: hidden; +} +.badge{ + margin: 0 5px 5px 0 !important; +} \ No newline at end of file diff --git a/src/components/MoviesContainer/MovieCard/MovieCard.js b/src/components/MoviesContainer/MovieCard/MovieCard.js new file mode 100644 index 0000000..c73f6ae --- /dev/null +++ b/src/components/MoviesContainer/MovieCard/MovieCard.js @@ -0,0 +1,47 @@ +import React from 'react'; +import {Link} from "react-router-dom"; +import StarRatings from "react-star-ratings/build/star-ratings"; + +import {PosterPreview} from "../../../components"; +import {MovieInfo} from "../MovieInfo/MovieInfo"; +import {urls} from "../../../constants"; +import {GenreBadges} from "../GenreBadges/GenreBadges"; + +const MovieCard = ({movie, genres: {genres}}) => { + const {id, title, poster_path, genre_ids, vote_average} = movie; + + const movieGenres = genres?.filter(genre => { + return genre_ids.includes(genre.id); + }); + + return ( +
+ + +
+ + + + {title} + + + + +

+ + Details + +

+
+ + +
+ ); +}; + +export {MovieCard}; \ No newline at end of file diff --git a/src/components/MoviesContainer/MovieDetail/ImgPlaceholder.js b/src/components/MoviesContainer/MovieDetail/ImgPlaceholder.js new file mode 100644 index 0000000..1fa60d3 --- /dev/null +++ b/src/components/MoviesContainer/MovieDetail/ImgPlaceholder.js @@ -0,0 +1,17 @@ +import React from 'react'; +import ContentLoader from "react-content-loader"; + +const ImgPlaceholder = (props) => ( + + + +); + +export {ImgPlaceholder}; \ No newline at end of file diff --git a/src/components/MoviesContainer/MovieDetail/MovieDetail.js b/src/components/MoviesContainer/MovieDetail/MovieDetail.js new file mode 100644 index 0000000..c5f5c03 --- /dev/null +++ b/src/components/MoviesContainer/MovieDetail/MovieDetail.js @@ -0,0 +1,64 @@ +import React, {useContext, useEffect, useState} from 'react'; +import {useNavigate} from "react-router-dom"; + +import css from './movie.module.css'; +import {imgPath, urls} from "../../../constants"; +import {movieService} from "../../../services"; +import {MovieDetailContext} from "../../../pages"; +import {ImgPlaceholder} from "./ImgPlaceholder"; +import {GenreBadges} from "../GenreBadges/GenreBadges"; + +const MovieDetail = ({movie}) => { + const navigate = useNavigate(); + const {id, setMovie, setPageLoading} = useContext(MovieDetailContext); + const [loading, setLoading] = useState(true); + + useEffect(() => { + movieService.betById(id).then(res => { + if( res?.status === 200 ){ + setMovie(res.data); + setPageLoading(false); + } + }).catch((error)=>{ + alert(`Error: ${error.message}`); + + navigate(urls.movie.root); + }) + }, []) + + return ( + <> +
+
+ {loading && } + {movie?.title} setLoading(false)} + /> +

{movie?.title}

+
+
+
+
+
+ + +

{movie?.overview}

+
+
+ +
+
+
+ + ); +}; + +export {MovieDetail}; \ No newline at end of file diff --git a/src/components/MoviesContainer/MovieDetail/movie.module.css b/src/components/MoviesContainer/MovieDetail/movie.module.css new file mode 100644 index 0000000..4044e4f --- /dev/null +++ b/src/components/MoviesContainer/MovieDetail/movie.module.css @@ -0,0 +1,33 @@ +.card{ + margin-top: 0; +} +.image{ + height: 500px; + overflow: hidden; +} +.image::after{ + content: ''; + z-index: 0; + position: absolute; + left: 0; + top: 50%; + right: 0; + bottom: 0; + background: rgb(0,0,0); + background: linear-gradient(0deg, rgba(0,0,0,1) 0%, rgba(0,0,0,0) 100%); +} +.title{ + z-index: 10; + position: relative; + font-size: 4.2rem !important; +} +.image img{ + width: 100%; + height: 100%; + display: block; + object-fit: cover; + object-position: center; +} +.placeholder{ + +} \ No newline at end of file diff --git a/src/components/MoviesContainer/MovieInfo/MovieInfo.js b/src/components/MoviesContainer/MovieInfo/MovieInfo.js new file mode 100644 index 0000000..7c7c577 --- /dev/null +++ b/src/components/MoviesContainer/MovieInfo/MovieInfo.js @@ -0,0 +1,17 @@ +import React from 'react'; + +const MovieInfo = ({movie}) => { + const {title, overview} = movie; + + return ( +
+ + {title} + close + +

{overview}

+
+ ); +}; + +export {MovieInfo}; \ No newline at end of file diff --git a/src/components/MoviesContainer/MoviesList/MoviesList.js b/src/components/MoviesContainer/MoviesList/MoviesList.js new file mode 100644 index 0000000..8c55a6f --- /dev/null +++ b/src/components/MoviesContainer/MoviesList/MoviesList.js @@ -0,0 +1,56 @@ +import React, {useContext, useEffect, useState} from 'react'; + +import css from "./movies.module.css"; +import {MovieCard} from "../../../components"; +import {genreService, movieService} from "../../../services"; +import {MoviesContext} from "../../../pages"; +import {useLocation} from "react-router-dom"; + +const MoviesList = () => { + const [movies, setMovies] = useState([]); + const [genres, setGenres] = useState([]); + const {currentPage, setCurrentPage, setPageLoading, searchParams, setSearchParams} = useContext(MoviesContext); + const {state} = useLocation(); + + useEffect(() => { + movieService.betByPage(currentPage).then(res => { + setMovies(res.data.results) + setPageLoading(false) + }).catch((error)=>{ + alert(`Error: ${error.message}`); + + setCurrentPage(1); + setSearchParams({ + ...searchParams, + page: 1 + }); + }) + }, [currentPage]) + + useEffect(() => { + if( state?.page ){ + setCurrentPage(state.page); + } + },[state?.page]) + + useEffect(() => { + genreService.getAllFromMovies().then(res => { + setGenres(res.data) + }) + },[]) + + return ( +
+
+ { movies?.map(movie => +
+
+ +
+
)} +
+
+ ); +}; + +export {MoviesList}; \ No newline at end of file diff --git a/src/components/MoviesContainer/MoviesList/movies.module.css b/src/components/MoviesContainer/MoviesList/movies.module.css new file mode 100644 index 0000000..ed60641 --- /dev/null +++ b/src/components/MoviesContainer/MoviesList/movies.module.css @@ -0,0 +1,8 @@ +.moviesRow{ + display: flex; + flex-wrap: wrap; +} + +.moviesRow .item{ + flex: 0 1 33.333%; +} diff --git a/src/components/MoviesContainer/PosterPreview/ImgPlaceholder.js b/src/components/MoviesContainer/PosterPreview/ImgPlaceholder.js new file mode 100644 index 0000000..c64d4d1 --- /dev/null +++ b/src/components/MoviesContainer/PosterPreview/ImgPlaceholder.js @@ -0,0 +1,17 @@ +import React from 'react'; +import ContentLoader from "react-content-loader"; + +const ImgPlaceholder = (props) => ( + + + +); + +export {ImgPlaceholder}; \ No newline at end of file diff --git a/src/components/MoviesContainer/PosterPreview/PosterPreview.js b/src/components/MoviesContainer/PosterPreview/PosterPreview.js new file mode 100644 index 0000000..614a104 --- /dev/null +++ b/src/components/MoviesContainer/PosterPreview/PosterPreview.js @@ -0,0 +1,27 @@ +import React, {useState} from 'react'; + +import css from './poster.module.css'; +import {imgPath} from "../../../constants"; +import {ImgPlaceholder} from "./ImgPlaceholder"; + +const PosterPreview = ({path, alt}) => { + const [loading, setLoading] = useState(true); + + return ( + <> + {loading && } +
+ {alt} setLoading(false)} + /> +
+ + ); +}; + +export {PosterPreview}; \ No newline at end of file diff --git a/src/components/MoviesContainer/PosterPreview/poster.module.css b/src/components/MoviesContainer/PosterPreview/poster.module.css new file mode 100644 index 0000000..4518483 --- /dev/null +++ b/src/components/MoviesContainer/PosterPreview/poster.module.css @@ -0,0 +1,3 @@ +.placeholder{ + min-height: 450px; +} \ No newline at end of file diff --git a/src/components/PageLoader/PageLoader.js b/src/components/PageLoader/PageLoader.js new file mode 100644 index 0000000..361409e --- /dev/null +++ b/src/components/PageLoader/PageLoader.js @@ -0,0 +1,11 @@ +import React from 'react'; + +const PageLoader = () => { + return ( +
+
+
+ ); +}; + +export {PageLoader}; \ No newline at end of file diff --git a/src/components/Pagination/Pagination.js b/src/components/Pagination/Pagination.js new file mode 100644 index 0000000..68cbc63 --- /dev/null +++ b/src/components/Pagination/Pagination.js @@ -0,0 +1,53 @@ +import React, {useContext} from 'react'; +import ReactPaginate from "react-paginate"; + +import {MoviesContext} from "../../pages"; + +const Pagination = () => { + const { + currentPage, + setCurrentPage, + searchParams, + setSearchParams, + setPageLoading + } = useContext(MoviesContext); + + const handlePageChange = (e) => { + const page = e.selected + 1; + if( page > 1 ){ + setSearchParams({ + ...searchParams, + page + }); + }else{ + setSearchParams({}); + } + + setCurrentPage(page); + }; + + const handleClick = (e) => { + const page = e?.nextSelectedPage ? e.nextSelectedPage+1: e.selected+1; + if( page !== currentPage ){ + setPageLoading(true); + } + } + + return ( +
+ chevron_right} + onClick={handleClick} + onPageChange={handlePageChange} + forcePage={currentPage - 1} + pageCount={500} + previousLabel={chevron_left} + containerClassName={'pagination'} + pageClassName='waves-effect' + activeClassName='active' + /> +
+ ); +}; + +export {Pagination}; \ No newline at end of file diff --git a/src/components/index.js b/src/components/index.js new file mode 100644 index 0000000..f303ac2 --- /dev/null +++ b/src/components/index.js @@ -0,0 +1,12 @@ +export * from './Header/Header'; + +export * from './MoviesContainer/MoviesList/MoviesList'; +export * from './MoviesContainer/MovieCard/MovieCard'; +export * from './MoviesContainer/PosterPreview/PosterPreview'; +export * from './MoviesContainer/MovieDetail/MovieDetail'; + +export * from './Pagination/Pagination'; + +export * from './AsideNav/AsideNav'; + +export * from './PageLoader/PageLoader'; \ No newline at end of file diff --git a/src/constants/index.js b/src/constants/index.js new file mode 100644 index 0000000..f49d57b --- /dev/null +++ b/src/constants/index.js @@ -0,0 +1 @@ +export * from './urls'; \ No newline at end of file diff --git a/src/constants/urls.js b/src/constants/urls.js new file mode 100644 index 0000000..9dec4e4 --- /dev/null +++ b/src/constants/urls.js @@ -0,0 +1,39 @@ +const API_key = '42b2dbca38ef021828e064a574016bb2'; +const token = 'eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI0MmIyZGJjYTM4ZWYwMjE4MjhlMDY0YTU3NDAxNmJiMiIsInN1YiI6IjY0YmZiNzJmNmQ0Yzk3MDBhZDU1MTdlNiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.lBUW-wAi0jnLDLwABo4Os4K_f5_TotHMQY9LQfNc_Z4'; + +const baseURL = 'https://api.themoviedb.org/3'; + +const home = '/' +const movie = '/movies' +const user = '/account' + +const urls = { + home, + movie: { + root: movie, + single: '/movie', + service: `/discover/movie`, + byId: (id) => `/movie/${id}`, + byPage: (pageNum) => `/discover/movie?page=${pageNum}` + }, + user: { + root: user + }, + genre: { + movies: `/genre/movie/list` + } +} + +const imgPathRoot = 'http://image.tmdb.org/t/p'; +const imgPath = { + original: `${imgPathRoot}/original`, + w300: `${imgPathRoot}/w300` +} + +export { + API_key, + token, + baseURL, + urls, + imgPath +} \ No newline at end of file diff --git a/src/index.css b/src/index.css deleted file mode 100644 index ec2585e..0000000 --- a/src/index.css +++ /dev/null @@ -1,13 +0,0 @@ -body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; -} diff --git a/src/index.js b/src/index.js index d563c0f..3e3e2dc 100644 --- a/src/index.js +++ b/src/index.js @@ -1,14 +1,13 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; -import './index.css'; -import App from './App'; import reportWebVitals from './reportWebVitals'; +import {RouterProvider} from "react-router-dom"; + +import {router} from "./roter"; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( - - - + ); // If you want to start measuring performance in your app, pass a function diff --git a/src/layouts/MainLayout.js b/src/layouts/MainLayout.js new file mode 100644 index 0000000..fe7a86c --- /dev/null +++ b/src/layouts/MainLayout.js @@ -0,0 +1,26 @@ +import React from 'react'; +import {Outlet} from "react-router-dom"; + +import {AsideNav, Header} from "../components"; + +const MainLayout = () => { + return ( + <> +
+ +
+
+
+ +
+ +
+ +
+
+
+ + ); +}; + +export {MainLayout}; \ No newline at end of file diff --git a/src/layouts/SingleLayout.js b/src/layouts/SingleLayout.js new file mode 100644 index 0000000..671cd76 --- /dev/null +++ b/src/layouts/SingleLayout.js @@ -0,0 +1,18 @@ +import React from 'react'; +import {Outlet} from "react-router-dom"; + +import {Header} from "../components"; + +const SingleLayout = () => { + return ( + <> +
+ +
+ +
+ + ); +}; + +export {SingleLayout}; \ No newline at end of file diff --git a/src/layouts/index.js b/src/layouts/index.js new file mode 100644 index 0000000..54f6785 --- /dev/null +++ b/src/layouts/index.js @@ -0,0 +1,2 @@ +export * from './SingleLayout'; +export * from './MainLayout'; \ No newline at end of file diff --git a/src/pages/HomePage/HomePage.js b/src/pages/HomePage/HomePage.js new file mode 100644 index 0000000..f2e7eb6 --- /dev/null +++ b/src/pages/HomePage/HomePage.js @@ -0,0 +1,11 @@ +import React from 'react'; + +const HomePage = () => { + return ( +
+ Home Page +
+ ); +}; + +export {HomePage}; \ No newline at end of file diff --git a/src/pages/MovieDetailPage/MovieDetailPage.js b/src/pages/MovieDetailPage/MovieDetailPage.js new file mode 100644 index 0000000..37f4bf6 --- /dev/null +++ b/src/pages/MovieDetailPage/MovieDetailPage.js @@ -0,0 +1,29 @@ +import React, {createContext, useState} from 'react'; +import {useParams} from "react-router-dom"; + +import {MovieDetail, PageLoader} from "../../components"; + +const MovieDetailContext = createContext(null); + +const MovieDetailPage = () => { + const [pageLoading, setPageLoading] = useState(true); + const [movie, setMovie] = useState(null); + const {id} = useParams(); + + + + return ( + <> + + {pageLoading && } + + + + + ); +}; + +export { + MovieDetailPage, + MovieDetailContext +}; \ No newline at end of file diff --git a/src/pages/MoviesPage/MoviesPage.js b/src/pages/MoviesPage/MoviesPage.js new file mode 100644 index 0000000..4797a78 --- /dev/null +++ b/src/pages/MoviesPage/MoviesPage.js @@ -0,0 +1,29 @@ +import React, {createContext, useState} from 'react'; +import {useSearchParams} from "react-router-dom"; + +import {MoviesList, PageLoader, Pagination} from "../../components"; + +const MoviesContext = createContext(null); + +const MoviesPage = () => { + const [pageLoading, setPageLoading] = useState(true); + const [searchParams, setSearchParams] = useSearchParams(); + const [currentPage, setCurrentPage] = useState(parseInt(searchParams.get('page')) || 1); + + return ( + <> + + {pageLoading && } + + {!pageLoading && } + + + + + ); +}; + +export { + MoviesPage, + MoviesContext +}; \ No newline at end of file diff --git a/src/pages/UserPage/UserPage.js b/src/pages/UserPage/UserPage.js new file mode 100644 index 0000000..8f8a3eb --- /dev/null +++ b/src/pages/UserPage/UserPage.js @@ -0,0 +1,12 @@ +import React from 'react'; + +const UserPage = () => { + return ( + <> +

Kokos

+

It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).

+ + ); +}; + +export {UserPage}; \ No newline at end of file diff --git a/src/pages/index.js b/src/pages/index.js new file mode 100644 index 0000000..6a59872 --- /dev/null +++ b/src/pages/index.js @@ -0,0 +1,4 @@ +export * from './HomePage/HomePage'; +export * from './MoviesPage/MoviesPage'; +export * from './MovieDetailPage/MovieDetailPage'; +export * from './UserPage/UserPage'; \ No newline at end of file diff --git a/src/roter.js b/src/roter.js new file mode 100644 index 0000000..75d6239 --- /dev/null +++ b/src/roter.js @@ -0,0 +1,41 @@ +import {createBrowserRouter} from "react-router-dom"; + +import {urls} from "./constants"; +import {MainLayout, SingleLayout} from "./layouts"; +import {HomePage, MoviesPage, MovieDetailPage, UserPage} from "./pages"; + +const router = createBrowserRouter([ + { + path: '', + children: [ + { + element: , + children: [ + { + path: urls.home, + element: + }, + { + path: urls.movie.root, + element: + }, + { + path: urls.user.root, + element: + } + ] + }, + { + element: , + children: [ + { + path: `${urls.movie.single}/:id`, + element: + } + ] + } + ] + } +]); + +export {router}; \ No newline at end of file diff --git a/src/services/apiService.js b/src/services/apiService.js new file mode 100644 index 0000000..e5f0074 --- /dev/null +++ b/src/services/apiService.js @@ -0,0 +1,14 @@ +import axios from "axios"; + +import {baseURL, token} from "../constants"; + +const apiService = axios.create({ + baseURL, + method: 'GET', + headers: { + Accept: 'application/json', + Authorization: `Bearer ${token}` + } +}) + +export {apiService} \ No newline at end of file diff --git a/src/services/genreService.js b/src/services/genreService.js new file mode 100644 index 0000000..8e33849 --- /dev/null +++ b/src/services/genreService.js @@ -0,0 +1,8 @@ +import {apiService} from "./apiService"; +import {urls} from "../constants"; + +const genreService = { + getAllFromMovies: () => apiService.get(urls.genre.movies) +} + +export {genreService} \ No newline at end of file diff --git a/src/services/index.js b/src/services/index.js new file mode 100644 index 0000000..1a1d9aa --- /dev/null +++ b/src/services/index.js @@ -0,0 +1,2 @@ +export * from './movieService'; +export * from './genreService'; \ No newline at end of file diff --git a/src/services/movieService.js b/src/services/movieService.js new file mode 100644 index 0000000..dcd237d --- /dev/null +++ b/src/services/movieService.js @@ -0,0 +1,10 @@ +import {apiService} from "./apiService"; +import {urls} from "../constants"; + +const movieService = { + getAll: () => apiService.get(urls.movie.service), + betById: (id) => apiService.get(urls.movie.byId(id)), + betByPage: (pageNum) => apiService.get(urls.movie.byPage(pageNum)) +} + +export {movieService} \ No newline at end of file