├── .env
├── src
├── img
│ ├── ava.jpg
│ ├── best-series-bg.jpg
│ └── spinner.svg
├── Pages
│ ├── Person
│ │ ├── Person.module.css
│ │ └── Person.js
│ ├── SingleTvPage
│ │ ├── SingleTvPage.module.css
│ │ └── SingleTvPage.js
│ ├── SingleMoviePage
│ │ ├── SingleMoviePage.module.css
│ │ └── SingleMoviePage.js
│ ├── Dashboard
│ │ └── Dashboard.js
│ ├── NotFoundPage
│ │ ├── NotFoundPage.js
│ │ └── NotFoundPage.module.css
│ ├── Home
│ │ └── Home.js
│ ├── Discover
│ │ ├── Discover.module.css
│ │ ├── Upcoming.js
│ │ ├── TopRated.js
│ │ ├── NowPlaying.js
│ │ ├── Popular.js
│ │ └── MovieBySearch.js
│ ├── PricingPlans
│ │ ├── PricingPlans.module.css
│ │ └── PricingPlans.js
│ └── MovieByGenrePage
│ │ └── MovieByGenrePage.js
├── components
│ ├── Loader
│ │ ├── Loader.js
│ │ └── LazyLoader.js
│ ├── TopTitleMovies
│ │ ├── TopTitleMovies.js
│ │ └── TopTitleMovies.module.css
│ ├── Sliders
│ │ ├── CurrentSlider
│ │ │ ├── CurrentSlider.css
│ │ │ └── CurrentSlider.js
│ │ ├── ResponsiveSlider
│ │ │ └── ResponsiveSlider.js
│ │ └── MainHomeSlider
│ │ │ └── MainSlider.js
│ ├── Top10Movies
│ │ ├── Top10Movies.module.css
│ │ └── Top10Movies.js
│ ├── PopularMovies
│ │ ├── PopularMovies.module.css
│ │ └── PopularMovies.js
│ ├── RelatedMovies
│ │ ├── RelatedMovies.module.css
│ │ └── RelatedMovies.js
│ ├── TrendingNow
│ │ ├── TrendingNow.module.css
│ │ └── TrendingNow.js
│ ├── SuggestedForYou
│ │ ├── SuggestedForYou.module.css
│ │ └── SuggestedForYou.js
│ ├── MainMovies
│ │ ├── MainMovies.module.css
│ │ ├── Movies.js
│ │ ├── Shows.js
│ │ ├── Featured.js
│ │ └── MainMovies.js
│ ├── Cards
│ │ ├── Card
│ │ │ ├── Card.module.css
│ │ │ └── Card.js
│ │ ├── MovieCard
│ │ │ ├── MovieCard.module.css
│ │ │ └── MovieCard.js
│ │ ├── OverlayCard
│ │ │ ├── OverlayCard.js
│ │ │ └── OverlayCard.module.css
│ │ ├── PricingPlansCard
│ │ │ ├── PricingPlansCard.module.css
│ │ │ └── PricingPlansCard.js
│ │ ├── PersonCard
│ │ │ ├── PersonCard.module.css
│ │ │ └── PersonCard.js
│ │ ├── OverlayMainCard
│ │ │ ├── OverlayMainCard.js
│ │ │ └── OverlayMainCard.module.css
│ │ ├── WatchListCard
│ │ │ ├── WatchListCard.module.css
│ │ │ └── WatchListCard.js
│ │ └── SinglePageCard
│ │ │ ├── SinglePageCard.module.css
│ │ │ ├── MoviePageCard
│ │ │ └── MoviePageCard.js
│ │ │ └── TvPageCard
│ │ │ └── TvPageCard.js
│ ├── Pagination
│ │ ├── Pagination.module.css
│ │ └── Pagination.js
│ ├── BestSeries
│ │ ├── NewSeries.js
│ │ ├── TopRatedSeries.js
│ │ ├── PopularSeries.js
│ │ ├── BestSeries.module.css
│ │ └── BestSeries.js
│ ├── DashboardComponents
│ │ ├── WatchList.js
│ │ ├── DashboardComponents.js
│ │ ├── DashboardComponents.module.css
│ │ └── ProfileSettings.js
│ └── ByGenreSideBar
│ │ ├── ByGenreSideBar.js
│ │ └── ByGenreSideBar.module.css
├── Layout
│ ├── Layout.js
│ ├── Header
│ │ ├── Header.module.css
│ │ ├── Navbar
│ │ │ ├── Navbar.module.css
│ │ │ └── Navbar.js
│ │ ├── SearchBar
│ │ │ ├── SearchBar.module.css
│ │ │ └── SearchBar.js
│ │ └── Header.js
│ └── Footer
│ │ ├── Footer.module.css
│ │ └── Footer.js
├── index.js
├── Redux
│ ├── watchListSlice.js
│ ├── trendingSlice.js
│ ├── tvSlice.js
│ ├── Store.js
│ ├── searchSlice.js
│ ├── videoSlice.js
│ ├── discoverSlice.js
│ ├── personSlice.js
│ ├── genreSlice.js
│ ├── singleTvSlice.js
│ └── singleMovieSlice.js
├── index.css
└── App.js
├── public
├── favicon.ico
├── logo192.png
├── logo512.png
├── images
│ ├── cast.jpg
│ ├── hulu.png
│ ├── user.jpg
│ ├── movie1.jpg
│ ├── movie2.jpg
│ ├── movie3.jpg
│ ├── movie4.jpg
│ ├── movie5.jpg
│ ├── netflix.png
│ ├── header-logo.png
│ └── top10-movies-badge.png
├── robots.txt
├── manifest.json
└── index.html
├── .gitignore
├── package.json
└── README.md
/.env:
--------------------------------------------------------------------------------
1 | REACT_APP_MOVIE_API_KEY = 81e6c83ddfd9e06a0fb3cf0012fcf182
--------------------------------------------------------------------------------
/src/img/ava.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/src/img/ava.jpg
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/public/images/cast.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/public/images/cast.jpg
--------------------------------------------------------------------------------
/public/images/hulu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/public/images/hulu.png
--------------------------------------------------------------------------------
/public/images/user.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/public/images/user.jpg
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/public/images/movie1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/public/images/movie1.jpg
--------------------------------------------------------------------------------
/public/images/movie2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/public/images/movie2.jpg
--------------------------------------------------------------------------------
/public/images/movie3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/public/images/movie3.jpg
--------------------------------------------------------------------------------
/public/images/movie4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/public/images/movie4.jpg
--------------------------------------------------------------------------------
/public/images/movie5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/public/images/movie5.jpg
--------------------------------------------------------------------------------
/public/images/netflix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/public/images/netflix.png
--------------------------------------------------------------------------------
/src/img/best-series-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/src/img/best-series-bg.jpg
--------------------------------------------------------------------------------
/public/images/header-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/public/images/header-logo.png
--------------------------------------------------------------------------------
/public/images/top10-movies-badge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Mehdih77/Movie-App/HEAD/public/images/top10-movies-badge.png
--------------------------------------------------------------------------------
/src/Pages/Person/Person.module.css:
--------------------------------------------------------------------------------
1 | /* Prallax */
2 | .parallax {
3 | background-position: 50%;
4 | background-size: cover;
5 | background-attachment: fixed;
6 | background-repeat: no-repeat;
7 | padding: 250px 0;
8 | }
9 |
--------------------------------------------------------------------------------
/src/Pages/SingleTvPage/SingleTvPage.module.css:
--------------------------------------------------------------------------------
1 | /* Prallax */
2 | .parallax {
3 | background-position: 50%;
4 | background-size: cover;
5 | background-attachment: fixed;
6 | background-repeat: no-repeat;
7 | padding: 250px 0;
8 | }
9 |
--------------------------------------------------------------------------------
/src/Pages/SingleMoviePage/SingleMoviePage.module.css:
--------------------------------------------------------------------------------
1 | /*Prallax*/
2 |
3 | .parallax {
4 | background-position: 50%;
5 | background-size: cover;
6 | background-attachment: fixed;
7 | background-repeat: no-repeat;
8 | padding: 250px 0;
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/Loader/Loader.js:
--------------------------------------------------------------------------------
1 | import spinner from "../../img/spinner.svg";
2 |
3 | export default function Loader() {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/src/Pages/Dashboard/Dashboard.js:
--------------------------------------------------------------------------------
1 | import DashboardComponents from "../../components/DashboardComponents/DashboardComponents";
2 |
3 | export default function Dashboard() {
4 | return (
5 | <>
6 |
7 | >
8 | )
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/TopTitleMovies/TopTitleMovies.js:
--------------------------------------------------------------------------------
1 | import styles from './TopTitleMovies.module.css';
2 |
3 | export default function TopTitleMovies({name}) {
4 |
5 | // used in Discover Pages && Genres Page
6 | return (
7 | <>
8 | {name} Movies
9 | >
10 | )
11 | }
12 |
--------------------------------------------------------------------------------
/src/Layout/Layout.js:
--------------------------------------------------------------------------------
1 | import Header from './Header/Header';
2 | import Footer from "./Footer/Footer";
3 | import "slick-carousel/slick/slick.css";
4 | import "slick-carousel/slick/slick-theme.css";
5 |
6 | export default function Layout({children}) {
7 | return (
8 | <>
9 |
10 | {children}
11 |
12 | >
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/Loader/LazyLoader.js:
--------------------------------------------------------------------------------
1 | import spinner from "../../img/spinner.svg";
2 |
3 | export default function LazyLoader() {
4 | return (
5 |
12 |
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/components/Sliders/CurrentSlider/CurrentSlider.css:
--------------------------------------------------------------------------------
1 | .custom_current_slider {
2 | margin: 0 20px !important;
3 | }
4 |
5 | /* customize arrows */
6 | .current_slider_arrows .slick-prev{
7 | left: 0px!important;
8 | }
9 | .current_slider_arrows .slick-next{
10 | right: 5px!important;
11 | }
12 |
13 | @media screen and (min-width: 982px) {
14 | .custom_current_slider {
15 | margin: 0 50px !important;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Pages/NotFoundPage/NotFoundPage.js:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom";
2 | import styles from "./NotFoundPage.module.css";
3 |
4 | export default function NotFoundPage() {
5 | return (
6 |
7 |
404
8 |
Oops... Page Not Found!
9 |
Try using the button below to go to main page of the site
10 |
11 |
Back to Homa Page
12 |
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/Top10Movies/Top10Movies.module.css:
--------------------------------------------------------------------------------
1 | .top10_movies_title {
2 | position: relative;
3 | color: #fff;
4 | margin: 100px 0 30px 20px;
5 | font-size: 20px;
6 | font-weight: 600;
7 | }
8 | .top10_movies_title::after {
9 | content: "";
10 | position: absolute;
11 | left: 0;
12 | bottom: -10px;
13 | width: 55px;
14 | height: 3px;
15 | background-color: red;
16 | }
17 |
18 | @media screen and (min-width: 982px) {
19 | .top10_movies_title {
20 | margin-left: 40px;
21 | font-size: 28px;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import store from './Redux/Store';
6 | import {Provider} from 'react-redux';
7 | // css style react Modal video (adding to singleCards)
8 | import '../node_modules/react-modal-video/css/modal-video.min.css';
9 |
10 | ReactDOM.render(
11 |
12 |
13 |
14 |
15 | ,
16 | document.getElementById('root')
17 | );
18 |
--------------------------------------------------------------------------------
/src/components/PopularMovies/PopularMovies.module.css:
--------------------------------------------------------------------------------
1 | .popular_movies_title {
2 | position: relative;
3 | color: #fff;
4 | margin: 60px 0 20px 20px;
5 | font-size: 20px;
6 | font-weight: 600;
7 | }
8 | .popular_movies_title::after {
9 | content: "";
10 | position: absolute;
11 | left: 0;
12 | bottom: -10px;
13 | width: 55px;
14 | height: 3px;
15 | background-color: red;
16 | }
17 |
18 | @media screen and (min-width: 982px) {
19 | .popular_movies_title {
20 | margin-left: 40px;
21 | font-size: 28px;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/RelatedMovies/RelatedMovies.module.css:
--------------------------------------------------------------------------------
1 | .related_movies_title {
2 | position: relative;
3 | color: #fff;
4 | margin: 60px 0 20px 20px;
5 | font-size: 20px;
6 | font-weight: 600;
7 | }
8 | .related_movies_title::after {
9 | content: "";
10 | position: absolute;
11 | left: 0;
12 | bottom: -10px;
13 | width: 55px;
14 | height: 3px;
15 | background-color: red;
16 | }
17 |
18 | @media screen and (min-width: 982px) {
19 | .related_movies_title {
20 | margin-left: 40px;
21 | font-size: 28px;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/TrendingNow/TrendingNow.module.css:
--------------------------------------------------------------------------------
1 | .trending_movies_title {
2 | position: relative;
3 | color: #fff;
4 | margin: 70px 0 30px 0px;
5 | font-size: 20px;
6 | font-weight: 600;
7 | }
8 | .trending_movies_title::after {
9 | content: "";
10 | position: absolute;
11 | left: 0;
12 | bottom: -10px;
13 | width: 55px;
14 | height: 3px;
15 | background-color: red;
16 | }
17 |
18 | @media screen and (min-width: 982px) {
19 | .trending_movies_title {
20 | margin-left: 30px;
21 | font-size: 28px;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/SuggestedForYou/SuggestedForYou.module.css:
--------------------------------------------------------------------------------
1 | .suggested_for_you_title {
2 | position: relative;
3 | color: #fff;
4 | margin: 170px 0 20px 20px;
5 | font-size: 20px;
6 | font-weight: 600;
7 | }
8 | .suggested_for_you_title::after {
9 | content: "";
10 | position: absolute;
11 | left: 0;
12 | bottom: -10px;
13 | width: 55px;
14 | height: 3px;
15 | background-color: red;
16 | }
17 |
18 | @media screen and (min-width: 982px) {
19 | .suggested_for_you_title {
20 | margin-left: 40px;
21 | font-size: 28px;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Layout/Header/Header.module.css:
--------------------------------------------------------------------------------
1 | .header {
2 | background-color: #272829;
3 | }
4 | .header img {
5 | width: 80%;
6 | margin-top: -7px;
7 | }
8 | .custom_navbar_toggler {
9 | background-color: #ff0000;
10 | }
11 | .header button:focus {
12 | outline: none;
13 | box-shadow: none;
14 | }
15 | .navbar_icon {
16 | color: #fff !important;
17 | font-size: 30px;
18 | }
19 | @media screen and (min-width: 992px) {
20 | .header img {
21 | width: 86%;
22 | margin-top: -7px;
23 | margin-right: -30px;
24 | margin-left: 30px;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/MainMovies/MainMovies.module.css:
--------------------------------------------------------------------------------
1 | /* main style */
2 | .main_movies_tabs {
3 | margin-top: 40px;
4 | }
5 | .main_movies_tabs button {
6 | font-size: 14px;
7 | color: #fff;
8 | background-color: transparent;
9 | outline: none;
10 | border: none;
11 | padding: 7px 15px;
12 | margin-right: 10px;
13 | border-radius: 4px;
14 | }
15 | button.active_tabs {
16 | background-color: red;
17 | transition: all 0.4s linear;
18 | }
19 | button.not_active_tab:hover {
20 | color: red;
21 | transition: all 0.3s linear;
22 | }
23 |
24 | @media screen and (min-width: 982px) {
25 | .main_movies_tabs {
26 | padding-left: 45px;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/Cards/Card/Card.module.css:
--------------------------------------------------------------------------------
1 | .card_wrapper {
2 | color: #fff;
3 | text-align: center;
4 | margin-bottom: 20px;
5 | }
6 | .card_wrapper a {
7 | color: #fff;
8 | }
9 | .card_wrapper:hover {
10 | text-shadow: 0px 0px 5px rgb(184 184 184);
11 | }
12 | .card_wrapper:hover img {
13 | transform: scale(1.05);
14 | transition: all 0.3s ease-in-out;
15 | }
16 | .card_wrapper img {
17 | width: 170px;
18 | height: 255px;
19 | }
20 | .card_content p {
21 | font-weight: 500;
22 | margin-top: 10px;
23 | margin-bottom: 5px;
24 | font-size: 17px;
25 | }
26 | .card_content_bottom span {
27 | margin: 0 10px;
28 | }
29 | .card_content_bottom i {
30 | font-size: 14px;
31 | vertical-align: 2px;
32 | color: #ffc107;
33 | }
34 |
--------------------------------------------------------------------------------
/src/Redux/watchListSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice, createEntityAdapter } from "@reduxjs/toolkit";
2 |
3 | const watchListAdapter = createEntityAdapter();
4 |
5 | const initialState = watchListAdapter.getInitialState();
6 |
7 | export const {
8 | selectAll: selectAllWatchList
9 | } = watchListAdapter.getSelectors((state) => state.watchList);
10 |
11 | const watchListSlice = createSlice({
12 | name: "watchList",
13 | initialState,
14 | reducers: {
15 | addToWatchList: watchListAdapter.addOne,
16 | removeFromWatchList: (state, action) => {
17 | watchListAdapter.removeOne(state, action);
18 | },
19 | },
20 | });
21 |
22 | export const { addToWatchList, removeFromWatchList } = watchListSlice.actions;
23 |
24 | export default watchListSlice.reducer;
25 |
--------------------------------------------------------------------------------
/src/Pages/NotFoundPage/NotFoundPage.module.css:
--------------------------------------------------------------------------------
1 | .not_found_page {
2 | color: #fff;
3 | display: flex;
4 | flex-direction: column;
5 | justify-content: center;
6 | align-items: center;
7 | min-height: 460px;
8 | margin-top: 60px;
9 | }
10 | .not_found_page h2 {
11 | font-size: 185px;
12 | font-weight: 600;
13 | }
14 | .not_found_page p:nth-child(2) {
15 | font-size: 27px;
16 | font-weight: 600;
17 | margin-bottom: 5px;
18 | }
19 | .not_found_page p:nth-child(3) {
20 | font-size: 13px;
21 | }
22 | .not_found_page button {
23 | color: #fff;
24 | background-color: red;
25 | outline: none;
26 | border: none;
27 | padding: 13px 35px;
28 | border-radius: 8px;
29 | }
30 | .not_found_page button:hover {
31 | background-color: #333;
32 | transition: all 0.2s ease-in;
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/Pagination/Pagination.module.css:
--------------------------------------------------------------------------------
1 | .page_numbers {
2 | margin-top: 20px;
3 | color: #fff;
4 | display: flex;
5 | justify-content: center;
6 | align-items: center;
7 | cursor: pointer;
8 | }
9 | .page_numbers li {
10 | padding: 0 7px;
11 | }
12 | .page_numbers li:hover {
13 | transform: scale(1.1);
14 | transition: all 0.2s linear;
15 | }
16 | .page_numbers button {
17 | color: #fff;
18 | background-color: red;
19 | border: none;
20 | padding: 3px 5px;
21 | cursor: pointer;
22 | border-radius: 5px;
23 | }
24 | .active_page_nubmer {
25 | background-color: rgb(161, 5, 5);
26 | border-radius: 50%;
27 | }
28 | @media screen and (min-width: 780px) {
29 | .page_numbers li {
30 | padding: 0 13px;
31 | }
32 | .page_numbers button {
33 | padding: 5px 10px;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Pages/Home/Home.js:
--------------------------------------------------------------------------------
1 | import MainMovies from "../../components/MainMovies/MainMovies";
2 | import MainSlider from "../../components/Sliders/MainHomeSlider/MainSlider";
3 | import PopularMovies from "../../components/PopularMovies/PopularMovies";
4 | import Top10Movies from "../../components/Top10Movies/Top10Movies";
5 | import TrendingNow from "../../components/TrendingNow/TrendingNow";
6 | import BestSeries from "../../components/BestSeries/BestSeries";
7 | import SuggestedForYou from "../../components/SuggestedForYou/SuggestedForYou";
8 |
9 | export default function Home() {
10 | return (
11 | <>
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | >
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/src/Redux/trendingSlice.js:
--------------------------------------------------------------------------------
1 | import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
2 | import axios from "axios";
3 |
4 | export const fetchTrending = createAsyncThunk("trending/fetchTrending", async(params) => {
5 | const response = await axios
6 | .get(`https://api.themoviedb.org/3/trending/${params.type}/week?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182`);
7 | return await response.data.results;
8 | })
9 |
10 | const initialState = {
11 | trending: [],
12 | }
13 |
14 | const trendingSlice = createSlice({
15 | name: 'trending',
16 | initialState,
17 | extraReducers:{
18 | [fetchTrending.fulfilled]: (state,action) => {
19 | state.trending = action.payload;
20 | }
21 | }
22 | })
23 |
24 | export default trendingSlice.reducer;
25 |
26 | export const getTrending = (state) => state.trending.trending;
27 |
--------------------------------------------------------------------------------
/src/Redux/tvSlice.js:
--------------------------------------------------------------------------------
1 | import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
2 | import axios from "axios";
3 |
4 | //! get top rated tv OR popular
5 |
6 | export const fetchTvs = createAsyncThunk("tv/fetchTvs", async(params) => {
7 | const response = await axios
8 | .get(`https://api.themoviedb.org/3/tv/${params.path}?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182&language=en-US&page=1`)
9 | return await response.data.results;
10 | })
11 |
12 | const initialState = {
13 | tvs: [],
14 | }
15 |
16 | const tvSlice = createSlice({
17 | name: 'tvs',
18 | initialState,
19 | extraReducers:{
20 | [fetchTvs.fulfilled]: (state,action) => {
21 | state.tvs = action.payload;
22 | }
23 | }
24 | })
25 |
26 | export default tvSlice.reducer;
27 |
28 | // selectors
29 | export const getTvs = (state) => state.tvs.tvs;
30 |
--------------------------------------------------------------------------------
/src/components/Sliders/CurrentSlider/CurrentSlider.js:
--------------------------------------------------------------------------------
1 | import Slider from "react-slick";
2 | import "./CurrentSlider.css";
3 |
4 | export default function CurrentSlider({children}) {
5 |
6 | const settings = {
7 | className: "center custom_current_slider",
8 | infinite: true,
9 | centerPadding: "60px",
10 | slidesToShow: 2,
11 | speed: 500,
12 | responsive: [
13 | {
14 | breakpoint: 1024,
15 | settings: {
16 | slidesToShow: 1,
17 | slidesToScroll: 1,
18 | infinite: true,
19 | }
20 | }
21 | ]
22 | };
23 |
24 | return (
25 |
26 |
27 | {children}
28 |
29 |
30 | )
31 | }
--------------------------------------------------------------------------------
/src/components/BestSeries/NewSeries.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useDispatch, useSelector } from "react-redux";
3 | import { fetchTvs, getTvs } from "../../Redux/tvSlice";
4 | import MovieCard from "../Cards/MovieCard/MovieCard";
5 |
6 | export default function NewSeries() {
7 | const tvsList = useSelector(getTvs);
8 | const dispatch = useDispatch();
9 |
10 | useEffect(() => {
11 | dispatch(
12 | fetchTvs({
13 | path: "airing_today",
14 | })
15 | );
16 | }, [dispatch]);
17 |
18 | const showTvsList = tvsList.slice(6,12).map((tv) => (
19 |
28 | ));
29 |
30 | return <>{showTvsList}>;
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/TopTitleMovies/TopTitleMovies.module.css:
--------------------------------------------------------------------------------
1 | .discover_title {
2 | position: relative;
3 | color: #fff;
4 | font-size: 26px;
5 | padding-left: 30px;
6 | padding-bottom: 30px;
7 | }
8 | .discover_title::after {
9 | content: "";
10 | position: absolute;
11 | width: 270px;
12 | height: 3px;
13 | background-color: red;
14 | left: 30px;
15 | top: 40px;
16 | }
17 | .search_keyword_title {
18 | position: relative;
19 | padding-left: 30px;
20 | padding-bottom: 30px;
21 | }
22 | .search_keyword_title span:first-child {
23 | color: rgb(194, 194, 194);
24 | font-size: 20px;
25 | }
26 | .search_keyword_title span:last-child {
27 | color: rgb(255, 255, 255);
28 | font-size: 25px;
29 | }
30 | .search_keyword_title::after {
31 | content: "";
32 | position: absolute;
33 | width: 410px;
34 | height: 3px;
35 | background-color: red;
36 | left: 30px;
37 | top: 40px;
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/BestSeries/TopRatedSeries.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useDispatch, useSelector } from "react-redux";
3 | import { fetchTvs, getTvs } from "../../Redux/tvSlice";
4 | import MovieCard from "../Cards/MovieCard/MovieCard";
5 |
6 | export default function TopRatedSeries() {
7 | const tvsList = useSelector(getTvs);
8 | const dispatch = useDispatch();
9 |
10 | useEffect(() => {
11 | dispatch(
12 | fetchTvs({
13 | path: "top_rated",
14 | })
15 | );
16 | }, [dispatch]);
17 |
18 | const showTvsList = tvsList
19 | .slice(0,6)
20 | .map((tv) => (
21 |
30 | ));
31 |
32 | return <>{showTvsList}>;
33 | }
34 |
--------------------------------------------------------------------------------
/src/Pages/Discover/Discover.module.css:
--------------------------------------------------------------------------------
1 | .discover_page {
2 | padding-right: 30px !important;
3 | }
4 | .discover_title {
5 | position: relative;
6 | color: #fff;
7 | font-size: 26px;
8 | padding-left: 30px;
9 | padding-bottom: 30px;
10 | }
11 | .discover_title::after {
12 | content: "";
13 | position: absolute;
14 | width: 270px;
15 | height: 3px;
16 | background-color: red;
17 | left: 30px;
18 | top: 40px;
19 | }
20 | .search_keyword_title {
21 | position: relative;
22 | padding-left: 30px;
23 | padding-bottom: 30px;
24 | }
25 | .search_keyword_title span:first-child {
26 | color: rgb(194, 194, 194);
27 | font-size: 20px;
28 | }
29 | .search_keyword_title span:last-child {
30 | color: rgb(255, 255, 255);
31 | font-size: 25px;
32 | }
33 | .search_keyword_title::after {
34 | content: "";
35 | position: absolute;
36 | width: 410px;
37 | height: 3px;
38 | background-color: red;
39 | left: 30px;
40 | top: 40px;
41 | }
42 |
--------------------------------------------------------------------------------
/src/Redux/Store.js:
--------------------------------------------------------------------------------
1 | import { configureStore } from "@reduxjs/toolkit";
2 | import discoverSlice from "./discoverSlice";
3 | import genreSlice from "./genreSlice";
4 | import personSlice from "./personSlice";
5 | import searchSlice from "./searchSlice";
6 | import singleMovieSlice from "./singleMovieSlice";
7 | import singleTvSlice from "./singleTvSlice";
8 | import trendingSlice from "./trendingSlice";
9 | import tvSlice from "./tvSlice";
10 | import videoSlice from "./videoSlice";
11 | import watchListSlice from "./watchListSlice";
12 |
13 | const store = configureStore({
14 | reducer: {
15 | discover: discoverSlice,
16 | singleMovie: singleMovieSlice,
17 | person: personSlice,
18 | trending: trendingSlice,
19 | singleTv: singleTvSlice,
20 | tvs: tvSlice,
21 | genre: genreSlice,
22 | search: searchSlice,
23 | watchList: watchListSlice,
24 | video: videoSlice
25 | }
26 | })
27 |
28 | export default store;
--------------------------------------------------------------------------------
/src/components/BestSeries/PopularSeries.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useDispatch, useSelector } from "react-redux";
3 | import { fetchTvs, getTvs } from "../../Redux/tvSlice";
4 | import MovieCard from '../Cards/MovieCard/MovieCard';
5 |
6 | export default function PopularSeries() {
7 | const tvsList = useSelector(getTvs);
8 | const dispatch = useDispatch();
9 |
10 | useEffect(() => {
11 | dispatch(
12 | fetchTvs({
13 | path: "popular",
14 | })
15 | );
16 | }, [dispatch]);
17 |
18 | const showTvsList = tvsList
19 | .slice(0,6)
20 | .map((tv) => (
21 |
31 | ));
32 |
33 | return <>{showTvsList}>;
34 | }
35 |
--------------------------------------------------------------------------------
/src/Layout/Footer/Footer.module.css:
--------------------------------------------------------------------------------
1 | .footer {
2 | margin-top: 90px;
3 | background-color: #272829;
4 | color: #fff;
5 | padding: 50px 100px;
6 | }
7 | .footer_left img {
8 | width: 270px;
9 | }
10 | .footer_left p {
11 | font-size: 13px;
12 | color: #6c757d !important;
13 | margin-top: 15px;
14 | }
15 | .footer_social_icons {
16 | margin-top: 40px;
17 | }
18 | .footer_social_icons i {
19 | margin: 0 10px;
20 | font-size: 22px;
21 | cursor: pointer;
22 | }
23 |
24 | /* footer_rights */
25 | .footer_rights h5 {
26 | text-transform: uppercase;
27 | font-size: 17px;
28 | font-weight: 600;
29 | }
30 | .footer_rights li {
31 | font-size: 14px;
32 | padding: 5px 0;
33 | margin-left: -40px;
34 | cursor: pointer;
35 | }
36 |
37 | /* HOVER */
38 | .footer_social_icons i:hover,
39 | .footer_rights li:hover {
40 | color: #ff0000;
41 | transition: all 0.3s linear;
42 | }
43 |
44 | @media screen and (max-width: 980px) {
45 | .footer_left {
46 | text-align: center;
47 | }
48 | .footer_social_icons {
49 | margin-top: 20px;
50 | }
51 | .footer_rights {
52 | padding: 0px;
53 | padding-top: 30px;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/components/DashboardComponents/WatchList.js:
--------------------------------------------------------------------------------
1 | import { useSelector } from "react-redux";
2 | import { selectAllWatchList } from "../../Redux/watchListSlice";
3 | import WatchListCard from "../Cards/WatchListCard/WatchListCard";
4 | import styles from "./DashboardComponents.module.css";
5 |
6 | export default function WatchList() {
7 | const watchListItems = useSelector(selectAllWatchList);
8 |
9 | const showWatchList = watchListItems.map((item) => (
10 |
21 | ));
22 |
23 | return (
24 |
25 | {showWatchList.length > 0 ? (
26 | showWatchList
27 | ) : (
28 |
29 |
30 |
Your Watch List Is Empty...
31 |
32 | )}
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/src/Redux/searchSlice.js:
--------------------------------------------------------------------------------
1 | import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
2 | import axios from "axios";
3 |
4 | export const fetchSearch = createAsyncThunk(
5 | "search/fetchSearch",
6 | async (params) => {
7 | const response = await axios.get(
8 | `https://api.themoviedb.org/3/search/multi?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182&language=en-US&query=${params.query}&page=${params.pageNumber}&include_adult=false`
9 | );
10 | return await {
11 | response: response.data.results,
12 | totalResult: response.data.total_results,
13 | };
14 | }
15 | );
16 |
17 | const initialState = {
18 | searchMovie: [],
19 | serachTotalResult: "",
20 | };
21 |
22 | const searchSlice = createSlice({
23 | name: "search",
24 | initialState,
25 | extraReducers: {
26 | [fetchSearch.fulfilled]: (state, action) => {
27 | state.searchMovie = action.payload.response;
28 | state.serachTotalResult = action.payload.totalResult;
29 | },
30 | },
31 | });
32 |
33 | export default searchSlice.reducer;
34 |
35 | export const getSearchMovie = (state) => state.search.searchMovie;
36 | export const getSerachTotalResult = (state) => state.search.serachTotalResult;
37 |
--------------------------------------------------------------------------------
/src/components/Sliders/ResponsiveSlider/ResponsiveSlider.js:
--------------------------------------------------------------------------------
1 | import Slider from "react-slick";
2 |
3 | export default function ResponsiveSlider({children}) {
4 |
5 | var settings = {
6 | className: "mx-4",
7 | arrows: false,
8 | dots: true,
9 | infinite: true,
10 | speed: 1000,
11 | slidesToShow: 6,
12 | slidesToScroll: 6,
13 | initialSlide: 0,
14 | responsive: [
15 | {
16 | breakpoint: 1024,
17 | settings: {
18 | slidesToShow: 3,
19 | slidesToScroll: 3,
20 | }
21 | },
22 | {
23 | breakpoint: 600,
24 | settings: {
25 | slidesToShow: 2,
26 | slidesToScroll: 2,
27 | initialSlide: 2
28 | }
29 | },
30 | {
31 | breakpoint: 480,
32 | settings: {
33 | slidesToShow: 2,
34 | slidesToScroll: 2
35 | }
36 | }
37 | ]
38 | };
39 |
40 | return (
41 | <>
42 |
43 | {children}
44 |
45 | >
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "movie-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@reduxjs/toolkit": "^1.6.2",
7 | "@testing-library/jest-dom": "^5.14.1",
8 | "@testing-library/react": "^11.2.7",
9 | "@testing-library/user-event": "^12.8.3",
10 | "axios": "^0.22.0",
11 | "react": "^17.0.2",
12 | "react-dom": "^17.0.2",
13 | "react-modal-video": "^1.2.8",
14 | "react-redux": "^7.2.5",
15 | "react-router-dom": "^5.3.0",
16 | "react-scripts": "4.0.3",
17 | "react-slick": "^0.28.1",
18 | "slick-carousel": "^1.8.1",
19 | "web-vitals": "^1.1.2"
20 | },
21 | "scripts": {
22 | "start": "react-scripts start",
23 | "build": "react-scripts build",
24 | "test": "react-scripts test",
25 | "eject": "react-scripts eject"
26 | },
27 | "eslintConfig": {
28 | "extends": [
29 | "react-app",
30 | "react-app/jest"
31 | ]
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.2%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/MainMovies/Movies.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { useDispatch , useSelector } from 'react-redux';
3 | import { fetchTrending , getTrending} from '../../Redux/trendingSlice';
4 | import MovieCard from '../Cards/MovieCard/MovieCard';
5 |
6 | export default function Movies() {
7 | const trendingList = useSelector(getTrending);
8 | const dispatch = useDispatch();
9 |
10 | useEffect(() => {
11 | dispatch(fetchTrending({
12 | type: "movie"
13 | }));
14 | }, [dispatch])
15 |
16 | const showTrending = trendingList?.slice(0,6).map(movie => (
17 |
29 | ))
30 |
31 |
32 | return (
33 | <>
34 | {showTrending}
35 | >
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/MainMovies/Shows.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { useDispatch , useSelector } from 'react-redux';
3 | import { fetchTrending , getTrending} from '../../Redux/trendingSlice';
4 | import MovieCard from '../Cards/MovieCard/MovieCard';
5 |
6 | export default function Shows() {
7 |
8 | const trendingList = useSelector(getTrending);
9 | const dispatch = useDispatch();
10 |
11 | useEffect(() => {
12 | dispatch(fetchTrending({
13 | type: "tv"
14 | }));
15 | }, [dispatch])
16 |
17 | const showTrending = trendingList?.slice(0,6).map(movie => (
18 |
30 | ))
31 |
32 |
33 | return (
34 | <>
35 | {showTrending}
36 | >
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/MainMovies/Featured.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { useDispatch, useSelector } from 'react-redux';
3 | import { fetchTrending, getTrending } from '../../Redux/trendingSlice';
4 | import MovieCard from '../Cards/MovieCard/MovieCard';
5 |
6 | export default function Featured() {
7 |
8 | const trendingList = useSelector(getTrending);
9 | const dispatch = useDispatch();
10 |
11 | useEffect(() => {
12 | dispatch(fetchTrending({
13 | type: "all"
14 | }));
15 | }, [dispatch])
16 |
17 | const showTrending = trendingList?.slice(0,6).map(movie => (
18 |
30 | ))
31 |
32 |
33 | return (
34 | <>
35 | {showTrending}
36 | >
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/src/Pages/PricingPlans/PricingPlans.module.css:
--------------------------------------------------------------------------------
1 | /* Prallax */
2 | .parallax {
3 | background-image: linear-gradient(
4 | to right,
5 | rgb(12, 21, 26),
6 | rgba(0, 0, 0, 0.69)
7 | ),
8 | url(../../img/best-series-bg.jpg);
9 | background-position: center;
10 | background-size: cover;
11 | background-attachment: fixed;
12 | position: relative;
13 | background-repeat: no-repeat;
14 | padding: 100px 0;
15 | }
16 | .parallax h3 {
17 | text-align: center;
18 | color: #fff;
19 | font-size: 53px;
20 | font-weight: 700;
21 | margin-bottom: 20px;
22 | }
23 | .parallax div,
24 | .parallax div a {
25 | text-align: center;
26 | color: #777;
27 | font-size: 14px;
28 | }
29 | .parallax div a:hover {
30 | color: #c80000;
31 | transition: all 0.3s linear;
32 | }
33 | .parallax div i:nth-child(1) {
34 | padding-right: 12px;
35 | }
36 | .parallax div i:nth-child(3) {
37 | padding: 0 12px;
38 | font-size: 10px;
39 | }
40 | /* pricing_title */
41 | .pricing_title {
42 | text-align: center;
43 | color: #fff;
44 | margin-top: 100px;
45 | }
46 | .pricing_title h4 {
47 | font-size: 32px;
48 | font-weight: 600;
49 | margin-bottom: 20px;
50 | }
51 | .pricing_title p {
52 | font-size: 14px;
53 | }
54 |
--------------------------------------------------------------------------------
/src/components/ByGenreSideBar/ByGenreSideBar.js:
--------------------------------------------------------------------------------
1 | import styles from './ByGenreSideBar.module.css';
2 |
3 | export default function ByGenreSideBar() {
4 | return (
5 | <>
6 |
27 | >
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/src/Redux/videoSlice.js:
--------------------------------------------------------------------------------
1 | import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
2 | import axios from "axios";
3 |
4 | export const fetchVideoMovie = createAsyncThunk("video/fetchVideoMovie", async(params) => {
5 | const response = await axios
6 | .get(`https://api.themoviedb.org/3/movie/${params.id}/videos?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182&language=en-US`);
7 | return await response.data.results;
8 | })
9 |
10 | export const fetchVideoTv = createAsyncThunk("video/fetchVideoTv", async(params) => {
11 | const response = await axios
12 | .get(`https://api.themoviedb.org/3/tv/${params.id}/videos?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182&language=en-US`);
13 | return await response.data.results;
14 | })
15 |
16 | const initialState = {
17 | video: [],
18 | }
19 |
20 | const videoSlice = createSlice({
21 | name: 'video',
22 | initialState,
23 | extraReducers:{
24 | [fetchVideoMovie.fulfilled]: (state,action) => {
25 | state.video = action.payload;
26 | },
27 | [fetchVideoTv.fulfilled]: (state,action) => {
28 | state.video = action.payload;
29 | },
30 | }
31 | })
32 |
33 | export default videoSlice.reducer;
34 |
35 | export const getVideo = (state) => state.video.video;
36 |
--------------------------------------------------------------------------------
/src/components/ByGenreSideBar/ByGenreSideBar.module.css:
--------------------------------------------------------------------------------
1 | .movie_by_genre_title {
2 | color: #fff;
3 | font-size: 16px;
4 | position: relative;
5 | }
6 | .movie_by_genre_title span {
7 | font-weight: 600;
8 | position: relative;
9 | }
10 | .movie_by_genre_title span::after {
11 | content: "";
12 | position: absolute;
13 | width: 180px;
14 | height: 3px;
15 | background-color: red;
16 | top: 30px;
17 | left: -55px;
18 | }
19 | .movie_by_genre_sorting {
20 | display: flex;
21 | flex-direction: column;
22 | color: #fff;
23 | }
24 | .movie_by_genre_sorting p {
25 | margin-bottom: 5px;
26 | }
27 | .movie_by_genre_sorting form {
28 | display: flex;
29 | flex-direction: column;
30 | color: #fff;
31 | }
32 | .movie_by_genre_sorting input {
33 | margin-right: 10px;
34 | }
35 | @media screen and (max-width: 767px) {
36 | .movie_by_genre_title {
37 | text-align: center;
38 | }
39 | .movie_by_genre_sorting {
40 | align-items: center;
41 | flex-direction: row;
42 | justify-content: center;
43 | }
44 | .movie_by_genre_sorting p {
45 | margin-right: 25px;
46 | }
47 | }
48 | @media screen and (min-width: 768px) and (max-width: 991px) {
49 | .movie_by_genre_title span::after {
50 | top: 60px;
51 | left: -55px;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Layout/Header/Navbar/Navbar.module.css:
--------------------------------------------------------------------------------
1 | .custom_navbar li a {
2 | position: relative;
3 | color: #fff;
4 | font-size: 12px;
5 | font-weight: 600;
6 | letter-spacing: 1px;
7 | }
8 | .custom_navbar li a:hover,
9 | .custom_navbar li a:focus {
10 | color: red;
11 | }
12 |
13 | /* Dropdown */
14 | .custom_dropdown {
15 | padding: 5px;
16 | border-radius: 0;
17 | padding: 0;
18 | }
19 | .custom_dropdown a {
20 | color: #000 !important;
21 | padding: 10px;
22 | }
23 | .custom_dropdown a:hover {
24 | background-color: red;
25 | color: #fff !important;
26 | width: 100.1%;
27 | transition: all 0.3s linear;
28 | }
29 |
30 | .custom_dropdown_genres div {
31 | display: flex;
32 | flex-wrap: wrap;
33 | width: 300px;
34 | }
35 | .custom_dropdown_genres div a {
36 | max-width: 50%;
37 | flex: 50%;
38 | padding-right: 70px;
39 | text-transform: uppercase;
40 | }
41 |
42 | @media screen and (min-width: 992px) {
43 | .custom_navbar li a {
44 | padding: 10px 20px !important;
45 | }
46 | .custom_navbar li a::before {
47 | content: "";
48 | position: absolute;
49 | top: 0;
50 | bottom: 0;
51 | left: 0;
52 | width: 1px;
53 | height: 20px;
54 | margin: auto;
55 | background-color: rgba(255, 255, 255, 0.2);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/components/Top10Movies/Top10Movies.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import styles from './Top10Movies.module.css';
3 | import { fetchDiscoverMovies, getMoviesList } from '../../Redux/discoverSlice';
4 | import { useDispatch, useSelector } from 'react-redux';
5 | import CurrentSlider from '../Sliders/CurrentSlider/CurrentSlider';
6 | import OverlayCard from '../Cards/OverlayCard/OverlayCard';
7 |
8 | export default function Top10Movies() {
9 |
10 | const moviesList = useSelector(getMoviesList);
11 | const dispatch = useDispatch();
12 |
13 | useEffect(() => {
14 | dispatch(fetchDiscoverMovies({
15 | path: "top_rated",
16 | }));
17 | }, [dispatch])
18 |
19 | const showMoviesList = moviesList.slice(6,12).map(movie => (
20 |
27 | ))
28 |
29 | return (
30 | <>
31 | TOP10 MOVIES
32 |
33 | {showMoviesList}
34 |
35 | >
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/src/Redux/discoverSlice.js:
--------------------------------------------------------------------------------
1 | import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
2 | import axios from "axios";
3 |
4 | export const fetchDiscoverMovies = createAsyncThunk("discover/fetchDiscover", async(params) => {
5 | const response = await axios
6 | .get(`https://api.themoviedb.org/3/movie/${params.path}?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182&language=en-US&page=${params.pageNumber}`)
7 | return await {
8 | response: response.data.results,
9 | totalPages: response.data.total_pages,
10 | totalResults: response.data.total_results
11 | };
12 | })
13 |
14 | const initialState = {
15 | movies: [],
16 | totalPages: '',
17 | totalResults: ''
18 | }
19 |
20 |
21 | const discoverSlice = createSlice({
22 | name: 'discover',
23 | initialState,
24 | reducers:{},
25 | extraReducers:{
26 | [fetchDiscoverMovies.fulfilled]: (state,action) => {
27 | state.movies = action.payload.response;
28 | state.totalPages = action.payload.totalPages;
29 | state.totalResults = action.payload.totalResults;
30 | }
31 | }
32 | })
33 |
34 | export default discoverSlice.reducer;
35 |
36 | export const getMoviesList = (state) => state.discover.movies;
37 | export const getTotalResults = (state) => state.discover.totalResults;
--------------------------------------------------------------------------------
/src/components/BestSeries/BestSeries.module.css:
--------------------------------------------------------------------------------
1 | /* main style */
2 | .main_movies_tabs {
3 | margin-top: 20px;
4 | text-align: center;
5 | z-index: 2;
6 | margin-bottom: -50px;
7 | }
8 | .main_movies_tabs button {
9 | font-size: 14px;
10 | color: #fff;
11 | background-color: transparent;
12 | outline: none;
13 | border: none;
14 | padding: 7px 15px;
15 | margin-right: 10px;
16 | border-radius: 4px;
17 | }
18 | button.active_tabs {
19 | background-color: red;
20 | transition: all 0.4s linear;
21 | }
22 | button.not_active_tab:hover {
23 | color: red;
24 | transition: all 0.3s linear;
25 | }
26 | .best_series_wrapper {
27 | position: relative;
28 | margin: 100px 0;
29 | }
30 | .best_series_wrapper::before {
31 | content: "";
32 | position: absolute;
33 | top: 0;
34 | left: 0;
35 | width: 100%;
36 | height: 100%;
37 | background-color: rgba(0, 0, 0, 0.9);
38 | }
39 | .best_series_title {
40 | color: #fff;
41 | text-align: center;
42 | z-index: 2;
43 | padding-top: 50px;
44 | }
45 | .best_series_title p:nth-child(1) {
46 | font-weight: 600;
47 | }
48 | .best_series_title p:nth-child(2) {
49 | font-weight: 700;
50 | font-size: 50px;
51 | }
52 | .best_series_title p:nth-child(3) {
53 | font-size: 14px;
54 | }
55 | .best_series_content {
56 | position: relative;
57 | top: 105px;
58 | }
59 |
--------------------------------------------------------------------------------
/src/Pages/Person/Person.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useDispatch, useSelector } from "react-redux";
3 | import { useParams } from "react-router";
4 | import PersonCard from "../../components/Cards/PersonCard/PersonCard";
5 | import RelatedMovies from "../../components/RelatedMovies/RelatedMovies";
6 | import { fetchPerson, getPerson } from "../../Redux/personSlice";
7 | import styles from "./Person.module.css";
8 |
9 | export default function Person() {
10 | const { id } = useParams();
11 | const personData = useSelector(getPerson);
12 | const dispatch = useDispatch();
13 |
14 | useEffect(() => {
15 | dispatch(
16 | fetchPerson({
17 | id,
18 | })
19 | );
20 | }, [id, dispatch]);
21 |
22 | const style = {
23 | backgroundImage: `linear-gradient(
24 | to right,
25 | rgb(12, 21, 26),
26 | rgba(0, 0, 0, 0.69)
27 | ),
28 | url(https://image.tmdb.org/t/p/original/${personData.profile_path})`,
29 | };
30 |
31 | return (
32 | <>
33 |
34 |
40 | >
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;700;900&display=swap");
2 | * {
3 | margin: 0;
4 | padding: 0;
5 | box-sizing: border-box;
6 | }
7 | body {
8 | font-family: "Poppins", sans-serif;
9 | background-color: #202020;
10 | }
11 | a {
12 | text-decoration: none;
13 | }
14 | li {
15 | list-style: none;
16 | }
17 | button:focus {
18 | box-shadow: none !important;
19 | outline: none !important;
20 | }
21 | input {
22 | border-radius: 8px !important;
23 | outline: none;
24 | }
25 | input:focus {
26 | box-shadow: none !important;
27 | border: 1px solid red !important;
28 | }
29 |
30 | /* Slick Custom Style */
31 | .slick-list {
32 | margin-right: -12px !important;
33 | margin-left: -12px !important;
34 | }
35 | .slick-prev {
36 | z-index: 999 !important;
37 | }
38 | .slick-next {
39 | z-index: 999 !important;
40 | }
41 | .slick-prev:before,
42 | .slick-next:before {
43 | font-size: 30px !important;
44 | }
45 | .slick-dots li {
46 | margin: 0 15px !important;
47 | }
48 | .slick-dots li button::before {
49 | color: red !important;
50 | font-size: 35px !important;
51 | top: 10px !important;
52 | content: "ـــــ" !important;
53 | }
54 |
55 | /* Modal Video Custom Style */
56 | .modal-video-close-btn {
57 | background-color: transparent !important;
58 | width: 30px !important;
59 | margin-top: 5px !important;
60 | margin-right: 5px !important;
61 | }
62 |
--------------------------------------------------------------------------------
/src/components/TrendingNow/TrendingNow.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import styles from './TrendingNow.module.css';
3 | import { useDispatch, useSelector } from 'react-redux';
4 | import {fetchTrending, getTrending} from '../../Redux/trendingSlice';
5 | import MovieCard from '../Cards/MovieCard/MovieCard';
6 |
7 | export default function TrendingNow() {
8 |
9 | const trendingList = useSelector(getTrending);
10 | const dispatch = useDispatch();
11 |
12 | useEffect(() => {
13 | dispatch(fetchTrending({
14 | type: "all"
15 | }));
16 | }, [dispatch])
17 |
18 | const showTrending = trendingList?.slice(6,12).map(movie => (
19 |
31 | ))
32 |
33 | return (
34 |
35 | TRENDING NOW
36 |
37 | {showTrending}
38 |
39 |
40 | )
41 | }
42 |
--------------------------------------------------------------------------------
/src/Layout/Header/SearchBar/SearchBar.module.css:
--------------------------------------------------------------------------------
1 | .search_bar {
2 | display: flex;
3 | justify-content: flex-end;
4 | }
5 | .search_bar input {
6 | height: 37px;
7 | background-color: #fff;
8 | margin-top: 3px;
9 | }
10 | .search_bar input:enabled {
11 | padding-left: 30px;
12 | font-size: 15px;
13 | }
14 | input.input_error{
15 | border: 3px solid red;
16 | }
17 | .search_bar div {
18 | width: 100% !important;
19 | position: relative;
20 | margin-right: 15px;
21 | }
22 | .search_bar div button {
23 | color: #333;
24 | background-color: transparent;
25 | border: none;
26 | position: absolute;
27 | top: 9px;
28 | left: 6px;
29 | font-size: 17px;
30 | }
31 | .search_bar div button:hover {
32 | color: red;
33 | }
34 | .search_bar div button:focus {
35 | transform: scale(0.9);
36 | }
37 |
38 | .user_btn {
39 | width: 42px;
40 | height: 42px;
41 | background-color: red;
42 | border-radius: 50%;
43 | }
44 | .user_btn i {
45 | color: #fff;
46 | }
47 | @media screen and (min-width: 992px) {
48 | .search_bar {
49 | width: 450px;
50 | }
51 | }
52 |
53 | /* access_key_btn */
54 | .access_key {
55 | color: #000;
56 | position: absolute;
57 | right: 10px;
58 | top: 8px;
59 | border: 1px solid #ccc;
60 | border-radius: 4px;
61 | padding: 0.1em 0.5em;
62 | box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #fff inset;
63 | background-color: #f7f7f7;
64 | }
65 | .search_bar input:focus ~ .access_key {
66 | display: none;
67 | }
68 |
--------------------------------------------------------------------------------
/src/components/PopularMovies/PopularMovies.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { useDispatch,useSelector } from 'react-redux';
3 | import { fetchDiscoverMovies, getMoviesList } from '../../Redux/discoverSlice';
4 | import MovieCard from '../Cards/MovieCard/MovieCard';
5 | import ResponsiveSlider from "../Sliders/ResponsiveSlider/ResponsiveSlider";
6 | import styles from './PopularMovies.module.css';
7 |
8 | export default function PopularMovies() {
9 |
10 | const moviesList = useSelector(getMoviesList);
11 | const dispatch = useDispatch();
12 |
13 | useEffect(() => {
14 | dispatch(fetchDiscoverMovies({
15 | path: "popular",
16 | }));
17 | }, [dispatch])
18 |
19 | const showMoviesList = moviesList.slice(0,12).map(movie => (
20 |
21 |
31 |
32 | ))
33 |
34 | return (
35 | <>
36 | POPULAR MOVIES
37 |
38 | {showMoviesList}
39 |
40 | >
41 | )
42 | }
--------------------------------------------------------------------------------
/src/Redux/personSlice.js:
--------------------------------------------------------------------------------
1 | import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
2 | import axios from "axios";
3 |
4 | export const fetchPerson = createAsyncThunk("person/fetchPerson", async(params) => {
5 | const response = await axios
6 | .get(`https://api.themoviedb.org/3/person/${params.id}?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182&language=en-US`);
7 | return await response.data;
8 | })
9 |
10 | export const fetchPersonSimilar = createAsyncThunk("person/fetchPersonSimilar", async(params) => {
11 | const response = await axios
12 | .get(`https://api.themoviedb.org/3/discover/movie?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1&with_cast=${params.id}&with_watch_monetization_types=flatrate`);
13 | return await response.data.results;
14 | })
15 |
16 | const initialState = {
17 | person: [],
18 | similar: []
19 | }
20 |
21 | const personSlice = createSlice({
22 | name: 'person',
23 | initialState,
24 | extraReducers:{
25 | [fetchPerson.fulfilled]: (state,action) => {
26 | state.person = action.payload;
27 | },
28 | [fetchPersonSimilar.fulfilled]: (state,action) => {
29 | state.similar = action.payload;
30 | }
31 | }
32 | })
33 |
34 | export default personSlice.reducer;
35 |
36 | export const getPerson = (state) => state.person.person;
37 | export const getSinglePersonSimilar = (state) => state.person.similar;
38 |
--------------------------------------------------------------------------------
/src/Layout/Header/Header.js:
--------------------------------------------------------------------------------
1 | import SearchBar from './SearchBar/SearchBar';
2 | import {Link} from "react-router-dom";
3 | import Navbar from './Navbar/Navbar';
4 | import styles from './Header.module.css';
5 |
6 | export default function Header() {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/DashboardComponents/DashboardComponents.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import styles from './DashboardComponents.module.css';
3 | import ProfileSettings from "./ProfileSettings";
4 | import WatchList from "./WatchList";
5 |
6 | export default function DashboardComponents() {
7 |
8 | const [profileSetting, setProfileSetting] = useState(true);
9 | const [watchList, setWatchList] = useState(false);
10 |
11 | const handleShowProfileSetting = () => {
12 | setProfileSetting(true);
13 | setWatchList(false)
14 | };
15 |
16 | const handleShowWatchList = () => {
17 | setProfileSetting(false);
18 | setWatchList(true);
19 | }
20 |
21 | return (
22 | <>
23 |
24 |
25 |
26 | Profile Settings
27 | Watch List
28 |
29 |
30 |
31 | {profileSetting &&
}
32 | {watchList &&
}
33 |
34 |
35 |
36 | >
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/SuggestedForYou/SuggestedForYou.js:
--------------------------------------------------------------------------------
1 | import styles from './SuggestedForYou.module.css';
2 | import ResponsiveSlider from '../Sliders/ResponsiveSlider/ResponsiveSlider';
3 | import MovieCard from '../Cards/MovieCard/MovieCard';
4 | import { useEffect } from 'react';
5 | import { useDispatch, useSelector } from 'react-redux';
6 | import {fetchDiscoverMovies, getMoviesList} from '../../Redux/discoverSlice';
7 |
8 | export default function SuggestedForYou() {
9 |
10 | const moviesList = useSelector(getMoviesList);
11 | const dispatch = useDispatch();
12 |
13 | useEffect(() => {
14 | dispatch(fetchDiscoverMovies({
15 | path: "top_rated",
16 | }));
17 | }, [dispatch])
18 |
19 | const showMoviesList = moviesList.slice(8,20).map(movie => (
20 |
21 |
31 |
32 | ))
33 |
34 | return (
35 | <>
36 | SUGGESTED FOR YOU
37 |
38 | {showMoviesList}
39 |
40 | >
41 | )
42 | }
43 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
13 |
14 | Movie App
15 |
16 |
17 |
18 | You need to enable JavaScript to run this app.
19 |
20 |
21 |
24 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/Pages/SingleTvPage/SingleTvPage.js:
--------------------------------------------------------------------------------
1 | import styles from './SingleTvPage.module.css';
2 | import RelatedMovies from '../../components/RelatedMovies/RelatedMovies';
3 | import { useParams } from 'react-router';
4 | import TvPageCard from '../../components/Cards/SinglePageCard/TvPageCard/TvPageCard';
5 | import { useDispatch, useSelector } from 'react-redux';
6 | import { fetchSingleTv, getSingleTv } from '../../Redux/singleTvSlice';
7 | import { useEffect } from 'react';
8 |
9 | export default function SingleTvPage() {
10 |
11 | const {id} = useParams();
12 |
13 | const tvData = useSelector(getSingleTv);
14 | const dispatch = useDispatch();
15 |
16 | useEffect(() => {
17 | dispatch(fetchSingleTv({
18 | id
19 | }));
20 | }, [id,dispatch]);
21 |
22 | const style = {
23 | backgroundImage: `linear-gradient(
24 | to right,
25 | rgb(12, 21, 26),
26 | rgba(0, 0, 0, 0.69)
27 | ),
28 | url(https://image.tmdb.org/t/p/original/${tvData.backdrop_path})`
29 | };
30 |
31 |
32 | return (
33 | <>
34 | {id &&
35 | <>
36 |
37 |
43 | >}
44 | >
45 | )
46 | }
47 |
--------------------------------------------------------------------------------
/src/Pages/SingleMoviePage/SingleMoviePage.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { useParams } from 'react-router';
3 | import { useDispatch, useSelector } from 'react-redux';
4 | import { fetchSingleMovie, getSingleMovie } from '../../Redux/singleMovieSlice';
5 | import styles from './SingleMoviePage.module.css';
6 | import MoviePageCard from '../../components/Cards/SinglePageCard/MoviePageCard/MoviePageCard';
7 | import RelatedMovies from '../../components/RelatedMovies/RelatedMovies';
8 |
9 | export default function SingleMoviePage() {
10 |
11 | const {id} = useParams();
12 | const movieData = useSelector(getSingleMovie);
13 | const dispatch = useDispatch();
14 |
15 | useEffect(() => {
16 | dispatch(fetchSingleMovie({
17 | id
18 | }));
19 | }, [id,dispatch]);
20 |
21 | const style = {
22 | backgroundImage: `linear-gradient(
23 | to right,
24 | rgb(12, 21, 26),
25 | rgba(0, 0, 0, 0.69)
26 | ),
27 | url(https://image.tmdb.org/t/p/original/${movieData.backdrop_path})`
28 | };
29 |
30 |
31 | return (
32 | <>
33 | {id &&
34 | <>
35 |
36 |
42 | >}
43 | >
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/src/Layout/Header/SearchBar/SearchBar.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { Link, useHistory } from "react-router-dom";
3 | import styles from "./SearchBar.module.css";
4 |
5 | export default function SearchBar() {
6 | const history = useHistory();
7 | const [query, setQuery] = useState("");
8 | const [error, setError] = useState(false);
9 |
10 | // use query with params in Discover/MovieBySearchPage
11 | const handleSearchByKeyword = (e) => {
12 | e.preventDefault();
13 | if (query.length > 0) {
14 | setError(false);
15 | history.push(`/searched/${query}`);
16 | setQuery("");
17 | } else {
18 | setError(true);
19 | }
20 | };
21 |
22 | return (
23 |
45 | );
46 | }
47 |
--------------------------------------------------------------------------------
/src/Pages/PricingPlans/PricingPlans.js:
--------------------------------------------------------------------------------
1 | import styles from './PricingPlans.module.css';
2 | import PricingPlansCard from "../../components/Cards/PricingPlansCard/PricingPlansCard";
3 | import { Link } from 'react-router-dom';
4 |
5 | export default function PricingPlans() {
6 | return (
7 | <>
8 |
9 |
Pricing Plans
10 |
11 |
12 | Home
13 |
14 | Pricing Plans
15 |
16 |
17 |
18 |
19 |
20 |
Our Monthly Plans
21 |
Choose the ideal plan for what you need. We work with affordable prices for all types of pocket.
22 |
23 |
26 |
29 |
32 |
33 |
34 | >
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/src/components/Cards/MovieCard/MovieCard.module.css:
--------------------------------------------------------------------------------
1 | /* Card */
2 | .card {
3 | margin-top: 15px;
4 | position: relative;
5 | }
6 | .card:hover .card_icon {
7 | opacity: 1;
8 | visibility: visible;
9 | transition: opacity 0.5s linear;
10 | }
11 | .card_no_image{
12 | height: 400px;
13 | padding-bottom: 20px;
14 | }
15 | .card_content {
16 | color: #fff;
17 | }
18 | .card:hover .card_content p,
19 | .card:hover .card_content div span {
20 | text-shadow: 0px 0px 5px rgb(184 184 184);
21 | }
22 | .card_content p {
23 | font-size: 16px;
24 | font-weight: 600;
25 | text-transform: uppercase;
26 | margin-top: -10px;
27 | margin-bottom: 0px;
28 | }
29 | .card_content div span {
30 | color: #a3a3a3;
31 | padding-right: 10px;
32 | font-size: 12px;
33 | }
34 | .card_content_movie_type{
35 | text-transform: uppercase;
36 | }
37 | /* Card Icon */
38 | .card_icon {
39 | opacity: 0;
40 | visibility: hidden;
41 | position: absolute;
42 | top: 0;
43 | right: 0;
44 | display: flex;
45 | flex-direction: column;
46 | }
47 | .card_icon button {
48 | font-size: 16px;
49 | border: none;
50 | background-color: red;
51 | width: 40px;
52 | color: #fff;
53 | padding-top: 15px;
54 | }
55 | .card_icon button:hover a {
56 | color: #333;
57 | }
58 | .card_icon button:hover {
59 | color: #333;
60 | }
61 | .card_icon button a {
62 | color: #fff;
63 | }
64 | .card_icon button:nth-child(2) {
65 | padding: 15px;
66 | border-bottom-left-radius: 8px;
67 | }
68 |
69 | @media screen and (min-width: 982px) {
70 | .card {
71 | padding: 0 25px;
72 | }
73 | .card_icon {
74 | right: 25px;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/components/Cards/OverlayCard/OverlayCard.js:
--------------------------------------------------------------------------------
1 | import { useDispatch } from 'react-redux';
2 | import { Link } from 'react-router-dom';
3 | import { addToWatchList } from '../../../Redux/watchListSlice';
4 | import styles from './OverlayCard.module.css';
5 |
6 | export default function OverlayCard({id,img,name,detail,allInformation}) {
7 |
8 | const imageUrl = `https://image.tmdb.org/t/p/original/${img}`;
9 |
10 | const dispatch = useDispatch();
11 |
12 | const handleAddToWatchList = () => {
13 | dispatch(addToWatchList(allInformation));
14 | };
15 |
16 | return (
17 |
18 |
21 |
22 |
23 |
{name}
24 |
{detail}
25 |
26 |
27 |
28 | Details
29 |
30 |
31 |
32 | My List
33 |
34 |
35 |
36 |
37 |
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/Cards/Card/Card.js:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect, useState } from "react";
2 | import { Link } from "react-router-dom";
3 | import styles from "./Card.module.css";
4 | import noImage from "../../../img/ava.jpg";
5 | import Loader from "../../Loader/Loader";
6 |
7 | export default function Card({ id, img, name, year, star, type }) {
8 | const noPoster = img === "" || img === null || img === undefined;
9 |
10 | let imageUrl = "";
11 | if (noPoster) {
12 | imageUrl = noImage;
13 | } else {
14 | imageUrl = `https://image.tmdb.org/t/p/w342/${img}`;
15 | }
16 |
17 | // show spinner before loading poster/image
18 | const [loading, setLoading] = useState(false);
19 |
20 | useEffect(() => {
21 | return () => setLoading(false);
22 | }, []);
23 |
24 | const onLoading = useCallback(() => {
25 | setLoading(true);
26 | }, [setLoading]);
27 |
28 | return (
29 |
30 |
31 |
32 | {noPoster ? (
33 |
34 | ) : (
35 | <>
36 | {!loading ?
: null}
37 |
43 | >
44 | )}
45 |
46 |
47 |
{name ? name : "Unknown"}
48 |
49 | {year}
50 |
51 | {star}
52 |
53 |
54 |
55 |
56 |
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/src/Redux/genreSlice.js:
--------------------------------------------------------------------------------
1 | import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
2 | import axios from "axios";
3 |
4 | export const fetchGenresType = createAsyncThunk(
5 | "genre/fetchGenresType",
6 | async () => {
7 | const response = await axios.get(
8 | `https://api.themoviedb.org/3/genre/movie/list?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182&language=en-US`
9 | );
10 | return await response.data.genres;
11 | }
12 | );
13 |
14 | export const fetchGenresMovie = createAsyncThunk(
15 | "genre/fetchGenresMovie",
16 | async (params) => {
17 | const response = await axios.get(
18 | `https://api.themoviedb.org/3/discover/movie?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=${params.pageNumber}&with_genres=${params.genreId}&with_watch_monetization_types=flatrate`
19 | );
20 | return await {
21 | response: response.data.results,
22 | totalResults: response.data.total_results,
23 | };
24 | });
25 |
26 | const initialState = {
27 | genresType: [],
28 | genresMovie: [],
29 | genreTotalResult: ""
30 | };
31 |
32 | const genreSlice = createSlice({
33 | name: "genre",
34 | initialState,
35 | extraReducers: {
36 | [fetchGenresType.fulfilled]: (state, action) => {
37 | state.genresType = action.payload;
38 | },
39 | [fetchGenresMovie.fulfilled]: (state, action) => {
40 | state.genresMovie = action.payload.response;
41 | state.genreTotalResult = action.payload.totalResults
42 | },
43 | },
44 | });
45 |
46 | export default genreSlice.reducer;
47 |
48 | export const getGenresType = (state) => state.genre.genresType;
49 | export const getGenresMovie = (state) => state.genre.genresMovie;
50 | export const getGenresTotalResult = (state) => state.genre.genreTotalResult;
51 |
--------------------------------------------------------------------------------
/src/Pages/Discover/Upcoming.js:
--------------------------------------------------------------------------------
1 | import { useState,useEffect,useCallback } from 'react';
2 | import { useDispatch, useSelector } from 'react-redux';
3 | import { fetchDiscoverMovies, getMoviesList, getTotalResults } from '../../Redux/discoverSlice';
4 | import Card from '../../components/Cards/Card/Card';
5 | import Pagination from '../../components/Pagination/Pagination';
6 | import TopTitleMovies from '../../components/TopTitleMovies/TopTitleMovies';
7 |
8 | export default function Upcoming() {
9 |
10 | const [currentPage, setCurrentPage] = useState(1)
11 | const moviesList = useSelector(getMoviesList);
12 | const totalResults = useSelector(getTotalResults);
13 | const dispatch = useDispatch();
14 |
15 | useEffect(() => {
16 | dispatch(fetchDiscoverMovies({
17 | path: "upcoming",
18 | pageNumber: currentPage
19 | }));
20 | }, [currentPage,dispatch])
21 |
22 | // change the page number
23 | const changePage = useCallback((page) => {setCurrentPage(page)}, [setCurrentPage]);
24 |
25 | const showMoviesList = moviesList?.map(movie => (
26 |
27 |
33 |
34 | ))
35 |
36 | return (
37 |
38 |
39 |
40 | {showMoviesList}
41 |
42 |
43 |
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/src/Pages/Discover/TopRated.js:
--------------------------------------------------------------------------------
1 | import { useState,useEffect,useCallback } from 'react';
2 | import { useDispatch, useSelector } from 'react-redux';
3 | import { fetchDiscoverMovies, getMoviesList, getTotalResults } from '../../Redux/discoverSlice';
4 | import Card from '../../components/Cards/Card/Card';
5 | import Pagination from '../../components/Pagination/Pagination';
6 | import TopTitleMovies from '../../components/TopTitleMovies/TopTitleMovies';
7 |
8 | export default function TopRated() {
9 |
10 | const [currentPage, setCurrentPage] = useState(1)
11 | const moviesList = useSelector(getMoviesList);
12 | const totalResults = useSelector(getTotalResults);
13 | const dispatch = useDispatch();
14 |
15 | useEffect(() => {
16 | dispatch(fetchDiscoverMovies({
17 | path: "top_rated",
18 | pageNumber: currentPage
19 | }));
20 | }, [currentPage,dispatch])
21 |
22 | // change the page number
23 | const changePage = useCallback((page) => {setCurrentPage(page)}, [setCurrentPage]);
24 |
25 | const showMoviesList = moviesList?.map(movie => (
26 |
27 |
33 |
34 | ))
35 |
36 | return (
37 |
38 |
39 |
40 | {showMoviesList}
41 |
42 |
43 |
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/Cards/PricingPlansCard/PricingPlansCard.module.css:
--------------------------------------------------------------------------------
1 | .pricing_card_wrapper {
2 | background-color: #000;
3 | text-align: center;
4 | margin-top: 50px;
5 | border-radius: 0 50px 0 50px;
6 | }
7 | .pricing_card_wrapper:hover {
8 | transform: translateY(-10px);
9 | transition: all 0.3s linear;
10 | }
11 | .pricing_card_title {
12 | position: relative;
13 | }
14 | .pricing_card_title::after {
15 | content: "";
16 | position: absolute;
17 | min-width: 75%;
18 | height: 1px;
19 | bottom: -10px;
20 | left: 50px;
21 | background-color: #fff;
22 | }
23 | .pricing_card_title h5 {
24 | color: #6c757da1;
25 | font-size: 20px;
26 | padding-top: 60px;
27 | letter-spacing: 2px;
28 | font-weight: 600;
29 | }
30 | .pricing_card_title p {
31 | color: #fff;
32 | font-size: 48px;
33 | font-weight: 600;
34 | }
35 | .pricing_card_title p sub {
36 | color: #fff;
37 | font-size: 13px;
38 | margin-left: -9px;
39 | }
40 | /* pricing_card_content */
41 | .pricing_card_content_standard,
42 | .pricing_card_content_platinum,
43 | .pricing_card_content_premium {
44 | display: flex;
45 | flex-direction: column;
46 | align-items: flex-start;
47 | }
48 | .pricing_card_content_standard li,
49 | .pricing_card_content_platinum li,
50 | .pricing_card_content_premium li {
51 | padding: 7px 65px;
52 | color: #6c757da1;
53 | font-size: 15px;
54 | }
55 | .pricing_card_content_platinum li:not(li:last-child) {
56 | color: #fff;
57 | }
58 | .pricing_card_content_premium li {
59 | color: #fff;
60 | }
61 | .pricing_card_btn {
62 | background-color: red;
63 | padding: 13px;
64 | min-width: 75%;
65 | border: none;
66 | color: #fff;
67 | margin-bottom: 40px;
68 | margin-top: 10px;
69 | border-radius: 0 0px 0 20px;
70 | }
71 | .pricing_card_btn:hover {
72 | background-color: #333;
73 | transition: all 0.2s ease-in;
74 | }
75 |
--------------------------------------------------------------------------------
/src/Pages/Discover/NowPlaying.js:
--------------------------------------------------------------------------------
1 | import { useState,useEffect,useCallback } from 'react';
2 | import { useDispatch, useSelector } from 'react-redux';
3 | import { fetchDiscoverMovies, getMoviesList, getTotalResults } from '../../Redux/discoverSlice';
4 | import Card from '../../components/Cards/Card/Card';
5 | import Pagination from '../../components/Pagination/Pagination';
6 | import TopTitleMovies from '../../components/TopTitleMovies/TopTitleMovies';
7 |
8 | export default function NowPlaying() {
9 |
10 | const [currentPage, setCurrentPage] = useState(1)
11 | const moviesList = useSelector(getMoviesList);
12 | const totalResults = useSelector(getTotalResults);
13 | const dispatch = useDispatch();
14 |
15 | useEffect(() => {
16 | dispatch(fetchDiscoverMovies({
17 | path: "now_playing",
18 | pageNumber: currentPage
19 | }));
20 | }, [currentPage,dispatch])
21 |
22 | // change the page number
23 | const changePage = useCallback((page) => {setCurrentPage(page)}, [setCurrentPage]);
24 |
25 | const showMoviesList = moviesList?.map(movie => (
26 |
27 |
33 |
34 | ))
35 |
36 | return (
37 |
38 |
39 |
40 | {showMoviesList}
41 |
42 |
43 |
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/src/Pages/Discover/Popular.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, useCallback } from "react";
2 | import { useDispatch, useSelector } from "react-redux";
3 | import {
4 | fetchDiscoverMovies,
5 | getMoviesList,
6 | getTotalResults,
7 | } from "../../Redux/discoverSlice";
8 | import Card from "../../components/Cards/Card/Card";
9 | import Pagination from "../../components/Pagination/Pagination";
10 | import TopTitleMovies from "../../components/TopTitleMovies/TopTitleMovies";
11 |
12 | export default function Popular() {
13 | const [currentPage, setCurrentPage] = useState(1);
14 | const moviesList = useSelector(getMoviesList);
15 | const totalResults = useSelector(getTotalResults);
16 | const dispatch = useDispatch();
17 |
18 | useEffect(() => {
19 | dispatch(
20 | fetchDiscoverMovies({
21 | path: "popular",
22 | pageNumber: currentPage,
23 | })
24 | );
25 | }, [currentPage, dispatch]);
26 |
27 | // change the page number
28 | const changePage = useCallback(
29 | (page) => {
30 | setCurrentPage(page);
31 | },
32 | [setCurrentPage]
33 | );
34 |
35 | const showMoviesList = moviesList?.map((movie) => (
36 |
37 |
44 |
45 | ));
46 |
47 | return (
48 |
49 |
50 |
51 | {showMoviesList}
52 |
53 |
58 |
59 | );
60 | }
61 |
--------------------------------------------------------------------------------
/src/components/Cards/PersonCard/PersonCard.module.css:
--------------------------------------------------------------------------------
1 | .person_card_wrapper {
2 | display: flex;
3 | color: #fff;
4 | }
5 | /* LEFT */
6 | .person_card_left {
7 | margin-right: 40px;
8 | display: flex;
9 | flex-direction: column;
10 | align-items: center;
11 | margin-left: 30px;
12 | }
13 | .person_card_left img {
14 | width: 400px;
15 | border-radius: 20px;
16 | box-shadow: 6px 6px 32px -12px rgb(0 0 0 / 75%);
17 | }
18 | .person_card_left_content {
19 | margin: 15px;
20 | }
21 | .person_card_left_content span {
22 | margin: 0 25px;
23 | color: #a3a3a3;
24 | font-size: 16px;
25 | }
26 | .person_card_left_content span:nth-child(1) i {
27 | color: #ffc107;
28 | font-size: 15px;
29 | vertical-align: 0px;
30 | padding-right: 5px;
31 | }
32 | .person_card_left_content a i {
33 | color: #ffc107;
34 | font-size: 21px;
35 | vertical-align: -1px;
36 | }
37 | .person_card_left_content a i:hover {
38 | transform: scale(1.1);
39 | }
40 | /* RIGHT */
41 | .person_card_right {
42 | margin-left: 30px;
43 | width: 90%;
44 | }
45 | .person_card_right h2 {
46 | font-weight: 600;
47 | margin: 20px 0;
48 | }
49 | .person_card_right_info {
50 | color: #dddddd;
51 | }
52 | .person_card_right_info span {
53 | color: #a3a3a3;
54 | margin-right: 35px;
55 | }
56 | .person_card_right_desc {
57 | font-size: 15px;
58 | margin: 40px 0;
59 | width: 60%;
60 | }
61 | .person_card_right_desc p:first-child {
62 | font-size: 30px;
63 | }
64 |
65 | @media screen and (max-width: 1146px) {
66 | .person_card_wrapper {
67 | flex-wrap: wrap;
68 | }
69 | .person_card_left {
70 | margin-left: 0px;
71 | margin-top: -150px;
72 | }
73 | .person_card_left img {
74 | max-width: 100%;
75 | text-align: center;
76 | margin-left: 20px;
77 | }
78 | .person_card_right {
79 | margin-left: 0px;
80 | }
81 | .person_card_right_desc {
82 | width: 90%;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/components/MainMovies/MainMovies.js:
--------------------------------------------------------------------------------
1 | import { useCallback, useState } from 'react';
2 | import styles from './MainMovies.module.css';
3 | import Featured from './Featured';
4 | import Movies from './Movies';
5 | import Shows from './Shows';
6 |
7 | const MainMovies = () => {
8 |
9 | const [featured, setFeatured] = useState(true);
10 | const [movies, setMovies] = useState(false);
11 | const [shows, setShows] = useState(false);
12 |
13 | const handleShowFeatured = useCallback(() => {
14 | setMovies(false);
15 | setShows(false);
16 | setFeatured(true);
17 | },[]);
18 |
19 | const handleShowMovies = useCallback(() => {
20 | setFeatured(false);
21 | setShows(false);
22 | setMovies(true);
23 | },[]);
24 |
25 | const handleShowShows = useCallback(() => {
26 | setShows(true);
27 | setMovies(false);
28 | setFeatured(false);
29 | },[]);
30 |
31 | return (
32 | <>
33 |
34 |
35 |
36 | Featured
37 | Movies
38 | Shows
39 |
40 |
41 |
42 | {featured && }
43 | {movies && }
44 | {shows && }
45 |
46 |
47 | >
48 | )
49 | }
50 |
51 | export default MainMovies;
--------------------------------------------------------------------------------
/src/components/Cards/OverlayMainCard/OverlayMainCard.js:
--------------------------------------------------------------------------------
1 | import { useDispatch } from 'react-redux';
2 | import { Link } from 'react-router-dom';
3 | import { addToWatchList } from '../../../Redux/watchListSlice';
4 | import styles from './OverlayMainCard.module.css';
5 |
6 | export default function OverlayMainCard({id,img,name,year,age,time,detail,allInformation}) {
7 |
8 | const imageUrl = `https://image.tmdb.org/t/p/original/${img}`;
9 | const dispatch = useDispatch();
10 |
11 | const handleAddToWatchList = () => {
12 | dispatch(addToWatchList(allInformation));
13 | }
14 |
15 | return (
16 |
17 |
22 |
23 |
New
24 |
{name}
25 |
26 | {year}
27 | {age}
28 | {time}
29 |
30 |
{detail}
31 |
32 |
33 |
34 | Details
35 |
36 |
37 |
38 | My List
39 |
40 |
41 |
42 |
43 |
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/src/Redux/singleTvSlice.js:
--------------------------------------------------------------------------------
1 | import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
2 | import axios from "axios";
3 |
4 | export const fetchSingleTv = createAsyncThunk("singleTv/fetchSingleTv", async(params) => {
5 | const response = await axios
6 | .get(`https://api.themoviedb.org/3/tv/${params.id}?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182&language=en-US`);
7 | return await response.data;
8 | })
9 |
10 | export const fetchSingleTvCredits = createAsyncThunk("singleTv/fetchSingleTvCredits", async(params) => {
11 | const response = await axios
12 | .get(`https://api.themoviedb.org/3/tv/${params.id}/credits?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182&language=en-US`);
13 | return await response.data;
14 | })
15 |
16 | export const fetchSingleTvSimilar = createAsyncThunk("singleTv/fetchSingleTvSimilar", async(params) => {
17 | const response = await axios
18 | .get(`https://api.themoviedb.org/3/tv/${params.id}/similar?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182&language=en-US&page=1`);
19 | return await response.data.results;
20 | })
21 |
22 | const initialState = {
23 | tv: [],
24 | credits: [],
25 | similar: []
26 | }
27 |
28 | const singleTvSlice = createSlice({
29 | name: 'singleTv',
30 | initialState,
31 | extraReducers:{
32 | [fetchSingleTv.fulfilled]: (state,action) => {
33 | state.tv = action.payload;
34 | },
35 | [fetchSingleTvCredits.fulfilled]: (state,action) => {
36 | state.credits = action.payload;
37 | },
38 | [fetchSingleTvSimilar.fulfilled]: (state,action) => {
39 | state.similar = action.payload;
40 | }
41 | }
42 | })
43 |
44 | export default singleTvSlice.reducer;
45 |
46 | // selectors
47 | export const getSingleTv = (state) => state.singleTv.tv;
48 | export const getSingleTvCredits = (state) => state.singleTv.credits;
49 | export const getSingleTvSimilar = (state) => state.singleTv.similar;
50 |
--------------------------------------------------------------------------------
/src/Pages/MovieByGenrePage/MovieByGenrePage.js:
--------------------------------------------------------------------------------
1 | import { useState,useEffect,useCallback } from 'react';
2 | import { useDispatch, useSelector } from 'react-redux';
3 | import Card from '../../components/Cards/Card/Card';
4 | import Pagination from '../../components/Pagination/Pagination';
5 | import { fetchGenresMovie, getGenresMovie, getGenresTotalResult } from '../../Redux/genreSlice';
6 | import { useParams } from 'react-router';
7 | import TopTitleMovies from '../../components/TopTitleMovies/TopTitleMovies';
8 |
9 | export default function MovieByGenrePage() {
10 |
11 | const [currentPage, setCurrentPage] = useState(1);
12 | const genreList = useSelector(getGenresMovie);
13 | const totalResults = useSelector(getGenresTotalResult);
14 | const dispatch = useDispatch();
15 |
16 | const {name,id} =useParams();
17 |
18 | useEffect(() => {
19 | dispatch(fetchGenresMovie({
20 | genreId: id,
21 | pageNumber: currentPage
22 | }));
23 | }, [currentPage,dispatch,id])
24 |
25 | // change the page number
26 | const changePage = useCallback((page) => {setCurrentPage(page)}, [setCurrentPage]);
27 |
28 | const showMoviesList = genreList.map(movie => (
29 |
30 |
37 |
38 | ))
39 |
40 | return (
41 |
42 |
43 |
44 | {showMoviesList}
45 |
46 |
47 |
48 | )
49 | }
50 |
--------------------------------------------------------------------------------
/src/Redux/singleMovieSlice.js:
--------------------------------------------------------------------------------
1 | import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
2 | import axios from "axios";
3 |
4 | export const fetchSingleMovie = createAsyncThunk("singleMovie/fetchSingleMovie", async(params) => {
5 | const response = await axios
6 | .get(`https://api.themoviedb.org/3/movie/${params.id}?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182&language=en-US`);
7 | return await response.data;
8 | })
9 |
10 | export const fetchSingleMovieCredits = createAsyncThunk("singleMovie/fetchSingleMovieCredits", async(params) => {
11 | const response = await axios
12 | .get(`https://api.themoviedb.org/3/movie/${params.id}/credits?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182&language=en-US`);
13 | return await response.data;
14 | })
15 |
16 | export const fetchSingleMovieSimilar = createAsyncThunk("singleMovie/fetchSingleMovieSimilar", async(params) => {
17 | const response = await axios
18 | .get(`https://api.themoviedb.org/3/movie/${params.id}/similar?api_key=81e6c83ddfd9e06a0fb3cf0012fcf182&language=en-US&page=1`);
19 | return await response.data.results;
20 | })
21 |
22 | const initialState = {
23 | movie: [],
24 | credits: [],
25 | similar: []
26 | }
27 |
28 | const singleMovieSlice = createSlice({
29 | name: 'singleMovie',
30 | initialState,
31 | extraReducers:{
32 | [fetchSingleMovie.fulfilled]: (state,action) => {
33 | state.movie = action.payload;
34 | },
35 | [fetchSingleMovieCredits.fulfilled]: (state,action) => {
36 | state.credits = action.payload;
37 | },
38 | [fetchSingleMovieSimilar.fulfilled]: (state,action) => {
39 | state.similar = action.payload;
40 | }
41 | }
42 | })
43 |
44 | export default singleMovieSlice.reducer;
45 |
46 | // selectors
47 | export const getSingleMovie = (state) => state.singleMovie.movie;
48 | export const getSingleMovieCredits = (state) => state.singleMovie.credits;
49 | export const getSingleMovieSimilar = (state) => state.singleMovie.similar;
50 |
--------------------------------------------------------------------------------
/src/components/Cards/OverlayCard/OverlayCard.module.css:
--------------------------------------------------------------------------------
1 | .overlay_card_main div {
2 | position: relative;
3 | background-position: center;
4 | background-size: cover;
5 | z-index: 2;
6 | width: 100%;
7 | height: 500px;
8 | }
9 | .overlay_card_img {
10 | position: absolute;
11 | z-index: 4;
12 | width: 70px;
13 | left: 70px;
14 | top: 20px;
15 | }
16 | .overlay_card_content {
17 | position: relative;
18 | width: 410px !important;
19 | padding-left: 70px;
20 | padding-top: 100px;
21 | }
22 | .overlay_card_content::before {
23 | content: "";
24 | width: 90%;
25 | height: 100%;
26 | position: absolute;
27 | top: 0;
28 | left: 0;
29 | background: linear-gradient(
30 | 90deg,
31 | rgb(39 40 41) 0%,
32 | rgb(39 40 41) 55%,
33 | rgba(83, 100, 141, 0) 100%
34 | );
35 | z-index: -1;
36 | }
37 | .overlay_card_content h2 {
38 | font-size: 45px;
39 | color: #fff;
40 | text-transform: uppercase;
41 | font-weight: 900;
42 | }
43 | .overlay_card_content p {
44 | font-size: 14px;
45 | color: #fff;
46 | }
47 | .overlay_card_btn button {
48 | color: #fff;
49 | margin-top: 10px;
50 | background-color: red;
51 | outline: none;
52 | border: none;
53 | padding: 10px 25px;
54 | margin-right: 20px;
55 | border-radius: 8px;
56 | }
57 | .overlay_card_btn button a {
58 | color: #fff;
59 | }
60 | .overlay_card_btn button i {
61 | margin-right: 10px;
62 | }
63 | .overlay_card_btn button:hover {
64 | color: #fff;
65 | background-color: #333;
66 | transition: all 0.2s ease-in;
67 | }
68 | @media screen and (max-width: 900px) {
69 | .overlay_card_content {
70 | width: 360px !important;
71 | padding-left: 50px;
72 | padding-top: 120px;
73 | }
74 | .overlay_card_img {
75 | width: 65px;
76 | left: 50px;
77 | top: 30px;
78 | }
79 | .overlay_card_content h2 {
80 | font-size: 50px;
81 | }
82 | .overlay_card_content p {
83 | font-size: 12px;
84 | width: 260px;
85 | }
86 | .overlay_card_btn button {
87 | padding: 10px 15px;
88 | font-size: 15px;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/components/Sliders/MainHomeSlider/MainSlider.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import { useDispatch,useSelector } from "react-redux";
3 | import {fetchDiscoverMovies, getMoviesList} from '../../../Redux/discoverSlice';
4 | import Slider from "react-slick";
5 | import OverlayMainCard from '../../Cards/OverlayMainCard/OverlayMainCard';
6 |
7 | export default function MainSlider() {
8 |
9 | const moviesList = useSelector(getMoviesList);
10 | const dispatch = useDispatch();
11 |
12 | useEffect(() => {
13 | dispatch(fetchDiscoverMovies({
14 | path: "now_playing"
15 | }));
16 | }, [dispatch])
17 |
18 | const showMovies = moviesList?.slice(13,19).map(movie => (
19 |
29 | ))
30 |
31 |
32 | const settings = {
33 | infinite: true,
34 | speed: 500,
35 | slidesToShow: 1,
36 | slidesToScroll: 1,
37 | nextArrow: ,
38 | prevArrow:
39 | };
40 |
41 | return (
42 |
43 |
44 | {showMovies}
45 |
46 |
47 | )
48 | }
49 |
50 |
51 | function SampleNextArrow(props) {
52 | const { className, style, onClick } = props;
53 | return (
54 |
60 | );
61 | }
62 |
63 | function SamplePrevArrow(props) {
64 | const { className, style, onClick } = props;
65 | return (
66 |
71 | );
72 | }
--------------------------------------------------------------------------------
/src/Layout/Footer/Footer.js:
--------------------------------------------------------------------------------
1 | import styles from './Footer.module.css';
2 |
3 | export default function Footer() {
4 | return (
5 |
6 |
7 |
8 |
9 |
Lorem ipsum dolor sit amet consectetur adipisicing elit. Sunt voluptate accusantium voluptatibus soluta.
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
Display Type
19 |
20 | Action
21 | Comedy
22 | Drama
23 | Horror
24 |
25 |
26 |
27 |
Production
28 |
29 | 2018 Year
30 | 2019 Year
31 | 2020 Year
32 | 2021 Year
33 |
34 |
35 |
36 |
Display Quality
37 |
38 | 720p HDTV
39 | 1080p BluRay
40 | 720p BluRay
41 | 1080p WEB-DL
42 |
43 |
44 |
45 |
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/src/Pages/Discover/MovieBySearch.js:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect, useState } from "react";
2 | import { useDispatch, useSelector } from "react-redux";
3 | import { useParams } from "react-router";
4 | import Card from "../../components/Cards/Card/Card";
5 | import Pagination from "../../components/Pagination/Pagination";
6 | import {
7 | fetchSearch,
8 | getSearchMovie,
9 | getSerachTotalResult,
10 | } from "../../Redux/searchSlice";
11 | import styles from "./Discover.module.css";
12 |
13 | export default function MovieBySearch() {
14 | const [currentPage, setCurrentPage] = useState(1);
15 | const { movie } = useParams();
16 | const dispatch = useDispatch();
17 | const searchList = useSelector(getSearchMovie);
18 | const totalResults = useSelector(getSerachTotalResult);
19 |
20 | useEffect(() => {
21 | dispatch(
22 | fetchSearch({
23 | query: movie,
24 | pageNumber: currentPage,
25 | })
26 | );
27 | }, [currentPage,dispatch,movie]);
28 |
29 | // change the page number
30 | const changePage = useCallback(
31 | (page) => {
32 | setCurrentPage(page);
33 | },
34 | [setCurrentPage]
35 | );
36 |
37 | const showSearchList = searchList?.map((movie) => (
38 |
39 |
51 |
52 | ));
53 | return (
54 |
57 |
58 |
59 | Search Keywords: {movie}
60 |
61 | {showSearchList}
62 |
63 |
68 |
69 | );
70 | }
71 |
--------------------------------------------------------------------------------
/src/components/RelatedMovies/RelatedMovies.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import styles from "./RelatedMovies.module.css";
3 | import MovieCard from "../Cards/MovieCard/MovieCard";
4 | import ResponsiveSlider from "../Sliders/ResponsiveSlider/ResponsiveSlider";
5 | import { useDispatch, useSelector } from "react-redux";
6 | import {
7 | fetchSingleMovieSimilar,
8 | getSingleMovieSimilar,
9 | } from "../../Redux/singleMovieSlice";
10 | import {
11 | fetchSingleTvSimilar,
12 | getSingleTvSimilar,
13 | } from "../../Redux/singleTvSlice";
14 | import {
15 | getSinglePersonSimilar,
16 | fetchPersonSimilar,
17 | } from "../../Redux/personSlice";
18 |
19 | export default function RelatedMovies({ Id, type }) {
20 | const movieSimilar = useSelector(getSingleMovieSimilar);
21 | const tvSimilar = useSelector(getSingleTvSimilar);
22 | const personSimilar = useSelector(getSinglePersonSimilar);
23 | const dispatch = useDispatch();
24 |
25 | useEffect(() => {
26 | switch (type) {
27 | case "movie":
28 | dispatch(
29 | fetchSingleMovieSimilar({
30 | id: Id,
31 | })
32 | );
33 | break;
34 | case "tv":
35 | dispatch(
36 | fetchSingleTvSimilar({
37 | id: Id,
38 | })
39 | );
40 | break;
41 | case "person":
42 | dispatch(
43 | fetchPersonSimilar({
44 | id: Id,
45 | })
46 | );
47 | break;
48 | default:
49 | break;
50 | }
51 | }, [Id, type, dispatch]);
52 |
53 | const similarType =
54 | (type === "movie" && movieSimilar) ||
55 | (type === "tv" && tvSimilar) ||
56 | (type === "person" && personSimilar);
57 |
58 | const showRelatedMovies = similarType.slice(0, 12).map((movie) => (
59 |
60 |
73 |
74 | ));
75 |
76 | return (
77 | <>
78 | RELATED MOVIES
79 | {showRelatedMovies}
80 | >
81 | );
82 | }
83 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, {Suspense} from 'react';
2 | import Layout from './Layout/Layout';
3 | // import NotFoundPage from './Pages/NotFoundPage/NotFoundPage';
4 | import {
5 | BrowserRouter as Router,
6 | Switch,
7 | Route,
8 | } from "react-router-dom";
9 | import LazyLoader from './components/Loader/LazyLoader';
10 |
11 | // React Lazy
12 | const Home = React.lazy(() => import('./Pages/Home/Home'));
13 | const Dashboard = React.lazy(() => import('./Pages/Dashboard/Dashboard'));
14 | const TopRated = React.lazy(() => import('./Pages/Discover/TopRated'));
15 | const NowPlaying = React.lazy(() => import('./Pages/Discover/NowPlaying'));
16 | const Popular = React.lazy(() => import('./Pages/Discover/Popular'));
17 | const Upcoming = React.lazy(() => import('./Pages/Discover/Upcoming'));
18 | const MovieBySearch = React.lazy(() => import('./Pages/Discover/MovieBySearch'));
19 | const PricingPlans = React.lazy(() => import('./Pages/PricingPlans/PricingPlans'));
20 | const SingleMoviePage = React.lazy(() => import('./Pages/SingleMoviePage/SingleMoviePage'));
21 | const Person = React.lazy(() => import('./Pages/Person/Person'));
22 | const SingleTvPage = React.lazy(() => import('./Pages/SingleTvPage/SingleTvPage'));
23 | const MovieByGenrePage = React.lazy(() => import('./Pages/MovieByGenrePage/MovieByGenrePage'));
24 |
25 | function App() {
26 | return (
27 |
28 |
29 |
30 | }>
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | {/* */}
44 |
45 |
46 |
47 |
48 | );
49 | }
50 |
51 | export default App;
52 |
--------------------------------------------------------------------------------
/src/components/BestSeries/BestSeries.js:
--------------------------------------------------------------------------------
1 | import { useCallback, useState } from "react";
2 | import styles from "./BestSeries.module.css";
3 | import NewSeries from "./NewSeries";
4 | import PopularSeries from "./PopularSeries";
5 | import TopRatedSeries from "./TopRatedSeries";
6 | import bestSeriesBg from "../../img/best-series-bg.jpg";
7 |
8 | export default function BestSeries() {
9 | const [newSeries, setNewSeries] = useState(true);
10 | const [popularSeries, setPopularSeries] = useState(false);
11 | const [topRatedSeries, setTopRatedSeries] = useState(false);
12 |
13 | const handleShowNewSeries = useCallback(() => {
14 | setPopularSeries(false);
15 | setTopRatedSeries(false);
16 | setNewSeries(true);
17 | }, []);
18 |
19 | const handleShowPopularSeries = useCallback(() => {
20 | setNewSeries(false);
21 | setTopRatedSeries(false);
22 | setPopularSeries(true);
23 | }, []);
24 |
25 | const handleTopRatedSeries = useCallback(() => {
26 | setTopRatedSeries(true);
27 | setPopularSeries(false);
28 | setNewSeries(false);
29 | }, []);
30 |
31 | return (
32 |
35 |
36 |
37 |
featured
38 |
Best Series
39 |
News Season 5 Just flown in Watch and debate.
40 |
41 |
42 |
45 | New
46 |
47 |
52 | Popular
53 |
54 |
59 | Top Rated
60 |
61 |
62 |
63 |
64 | {newSeries &&
}
65 | {popularSeries &&
}
66 | {topRatedSeries &&
}
67 |
68 |
69 | );
70 | }
71 |
--------------------------------------------------------------------------------
/src/components/Cards/OverlayMainCard/OverlayMainCard.module.css:
--------------------------------------------------------------------------------
1 | .slider_item {
2 | height: 700px;
3 | display: flex !important;
4 | flex-wrap: wrap;
5 | }
6 | .slider_content {
7 | position: relative;
8 | background-position: center;
9 | background-size: cover;
10 | background-color: #fff;
11 | z-index: 2;
12 | width: 100%;
13 | height: 100%;
14 | }
15 | .slider_body {
16 | width: 500px;
17 | margin-left: 90px;
18 | margin-top: 60px;
19 | }
20 | .slider_content::before {
21 | content: "";
22 | width: 80%;
23 | height: 100%;
24 | position: absolute;
25 | top: 0;
26 | left: 0;
27 | background: linear-gradient(
28 | 90deg,
29 | rgba(39, 40, 41, 0.994) 0%,
30 | rgb(39, 40, 41, 0.994) 35%,
31 | rgba(39, 40, 41, 0) 100%
32 | );
33 | z-index: -1;
34 | }
35 | .slider_body_badge {
36 | width: 60px;
37 | padding: 0 5px;
38 | text-align: center;
39 | border-radius: 5px;
40 | font-weight: 700;
41 | color: #343a40;
42 | background-color: #ffc107;
43 | margin-bottom: 30px;
44 | }
45 | .slider_body h2 {
46 | font-size: 65px;
47 | color: #fff;
48 | text-transform: uppercase;
49 | font-weight: 900;
50 | }
51 | .slider_body_date {
52 | color: #fff;
53 | margin: 20px 0;
54 | }
55 | .slider_body_date span {
56 | font-size: 14px;
57 | padding-right: 20px;
58 | }
59 | .slider_body_date span:nth-child(2) {
60 | font-size: 13px;
61 | border: 2px solid red;
62 | border-radius: 20px;
63 | padding: 3px 10px;
64 | margin-right: 20px;
65 | }
66 | .slider_body p {
67 | margin-top: 40px;
68 | font-size: 14px;
69 | color: #fff;
70 | }
71 | .slider_body_btn button {
72 | color: #fff;
73 | background-color: red;
74 | outline: none;
75 | border: none;
76 | padding: 10px 40px;
77 | margin-right: 20px;
78 | border-radius: 8px;
79 | }
80 | .slider_body_btn button a {
81 | color: #fff;
82 | }
83 | .slider_body_btn button i {
84 | margin-right: 10px;
85 | }
86 | .slider_body_btn button:hover {
87 | color: #fff;
88 | background-color: #333;
89 | transition: all 0.2s ease-in;
90 | }
91 |
92 | @media screen and (max-width: 982px) {
93 | .slider_body {
94 | width: 400px;
95 | margin-left: 60px;
96 | margin-top: 80px;
97 | }
98 | .slider_body h2 {
99 | font-size: 35px;
100 | width: 280px;
101 | }
102 | .slider_body p {
103 | font-size: 13px;
104 | width: 280px;
105 | }
106 | .slider_body_btn button {
107 | padding: 10px 30px;
108 | font-size: 16px;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/components/Cards/MovieCard/MovieCard.js:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect, useState } from "react";
2 | import { useDispatch } from "react-redux";
3 | import { Link } from "react-router-dom";
4 | import { addToWatchList } from "../../../Redux/watchListSlice";
5 | import styles from "./MovieCard.module.css";
6 | import noImage from "../../../img/ava.jpg";
7 | import Loader from "../../Loader/Loader";
8 |
9 | export default function MovieCard({
10 | responsive = true,
11 | id,
12 | img,
13 | year,
14 | firstAirDate,
15 | genres,
16 | title,
17 | name,
18 | age,
19 | type,
20 | allInformation,
21 | }) {
22 | const [loading, setLoading] = useState(false);
23 |
24 | useEffect(() => {
25 | return () => setLoading(false);
26 | }, []);
27 |
28 | const onLoading = useCallback(() => {
29 | setLoading(true);
30 | }, [setLoading]);
31 |
32 | const noPoster = img === "" || img === null || img === undefined;
33 |
34 | let imageUrl = "";
35 | if (noPoster) {
36 | imageUrl = noImage;
37 | } else {
38 | imageUrl = `https://image.tmdb.org/t/p/w342/${img}`;
39 | }
40 |
41 | const dispatch = useDispatch();
42 |
43 | const handleAddToWatchList = () => {
44 | dispatch(addToWatchList(allInformation));
45 | };
46 |
47 |
48 | return (
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | {noPoster ? (
63 |
64 | ) : (
65 | <>
66 | {!loading ?
: null}
67 |
73 | )
74 | >
75 | )}
76 |
77 |
78 |
{(title || name) || "Unknown"}
79 |
80 | {(firstAirDate || year) || "Not Added"}
81 | {age}
82 | {type}
83 |
84 |
85 |
86 |
87 | );
88 | }
89 |
--------------------------------------------------------------------------------
/src/components/Cards/WatchListCard/WatchListCard.module.css:
--------------------------------------------------------------------------------
1 | .watch_list_card {
2 | color: #fff;
3 | margin-top: 40px;
4 | }
5 | /* watch_list_card_details */
6 | .watch_list_card_details {
7 | display: flex;
8 | flex-direction: column;
9 | align-items: center;
10 | }
11 | .watch_list_card_details img {
12 | padding-top: 10px;
13 | width: 180px;
14 | }
15 | .watch_list_card_details p:nth-child(2) {
16 | font-size: 25px;
17 | font-weight: 600;
18 | margin-top: 15px;
19 | margin-bottom: -1px;
20 | text-transform: uppercase;
21 | }
22 | .watch_list_card_details p:last-child {
23 | text-align: center;
24 | font-size: 13px;
25 | margin-top: 15px;
26 | height: 150px;
27 | color: #959595;
28 | }
29 | .watch_list_card_details span {
30 | font-size: 13px;
31 | color: #c3c3c3;
32 | margin: 0 8px;
33 | }
34 | /* watch_list_card_btns */
35 | .watch_list_card_btns {
36 | text-align: center;
37 | margin-bottom: 20px;
38 | border-top: 1px solid #959595;
39 | border-bottom: 1px solid #959595;
40 | padding: 20px 0;
41 | }
42 | .watch_list_card_btns p {
43 | margin-bottom: 0;
44 | color: rgb(240, 240, 240);
45 | }
46 | .watch_list_card_btns button {
47 | width: 45%;
48 | margin: 10px 10px 0 0;
49 | outline: none;
50 | border: none;
51 | color: #fff;
52 | padding: 10px 30px;
53 | border-radius: 16px;
54 | }
55 | .watch_list_card_btns button:nth-child(1) {
56 | background-color: #0076ca;
57 | }
58 | .watch_list_card_btns button:nth-child(2) {
59 | background-color: #224f37;
60 | }
61 | .watch_list_card_btns button:nth-child(3) {
62 | background-color: #ff8321;
63 | }
64 | .watch_list_card_btns button:nth-child(4) {
65 | background-color: #ca0000;
66 | }
67 | .watch_list_card_btns img {
68 | width: 25px;
69 | margin-right: 10px;
70 | }
71 | .watch_list_card_btns img[alt="netflix"] {
72 | width: 20px;
73 | vertical-align: -3px;
74 | }
75 | .watch_list_card_btns i {
76 | margin-right: 10px;
77 | }
78 | /* watch_list_card_actions */
79 | .watch_list_card_actions {
80 | display: flex;
81 | flex-direction: row;
82 | justify-content: space-around;
83 | padding-bottom: 20px;
84 | border-bottom: 1px solid rgb(121, 0, 0);
85 | }
86 | .watch_list_card_actions button {
87 | display: flex;
88 | flex-direction: column;
89 | background-color: transparent;
90 | border: none;
91 | color: rgb(233, 233, 233);
92 | align-items: center;
93 | font-size: 15px;
94 | }
95 | .watch_list_card_actions button:hover {
96 | text-shadow: 0px 0px 5px rgb(184 184 184);
97 | }
98 | .watch_list_card_actions button i {
99 | font-size: 26px;
100 | margin-bottom: 5px;
101 | }
102 |
--------------------------------------------------------------------------------
/src/Layout/Header/Navbar/Navbar.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import { useDispatch, useSelector } from 'react-redux';
3 | import { Link } from 'react-router-dom';
4 | import { fetchGenresType, getGenresType } from '../../../Redux/genreSlice';
5 | import styles from './Navbar.module.css';
6 |
7 | export default function Navbar() {
8 |
9 | const genresType = useSelector(getGenresType);
10 | const dispatch = useDispatch();
11 |
12 | useEffect(() => {
13 | dispatch(fetchGenresType());
14 | }, [dispatch])
15 |
16 | const showGenresList = genresType?.map(genre => (
17 | {genre.name}
18 | ))
19 |
20 | return (
21 |
22 |
23 | HOME
24 |
25 |
26 | Pricing Plans
27 |
28 |
29 |
36 | DISCOVER
37 |
38 |
39 | NOW PLAYING
40 | TOP RATED
41 | POPULAR
42 | UPCOMING
43 |
44 |
45 |
46 |
53 | GENRES
54 |
55 |
60 |
61 |
62 | )
63 | }
64 |
--------------------------------------------------------------------------------
/src/components/Pagination/Pagination.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import styles from './Pagination.module.css';
3 |
4 | const Pagination = ({totalResults,changePage,currentPage}) => {
5 |
6 | const [itemsPerPage] = useState(20);
7 | const [pageNumberLimit] = useState(5);
8 | const [maxPageNumberLimit, setmaxPageNumberLimit] = useState(5);
9 | const [minPageNumberLimit, setminPageNumberLimit] = useState(0);
10 |
11 | const handleClick = (e) => {
12 | e.preventDefault();
13 | changePage(e.target.id);
14 | window.scrollTo(0,0);
15 | };
16 |
17 | const pages = [];
18 | for (let i = 1; i <= Math.ceil(totalResults / itemsPerPage); i++) {
19 | pages.push(i);
20 | }
21 |
22 | const renderPageNumbers = pages.map((number) => {
23 | if (number < maxPageNumberLimit + 1 && number > minPageNumberLimit) {
24 | return (
25 |
31 | {number}
32 |
33 | );
34 | } else {
35 | return null;
36 | }
37 | });
38 |
39 | const handleNextbtn = () => {
40 | changePage(currentPage + 1);
41 |
42 | if (currentPage + 1 > maxPageNumberLimit) {
43 | setmaxPageNumberLimit(maxPageNumberLimit + pageNumberLimit);
44 | setminPageNumberLimit(minPageNumberLimit + pageNumberLimit);
45 | }
46 | };
47 |
48 | const handlePrevbtn = () => {
49 | changePage(currentPage - 1);
50 |
51 | if ((currentPage - 1) % pageNumberLimit === 0) {
52 | setmaxPageNumberLimit(maxPageNumberLimit - pageNumberLimit);
53 | setminPageNumberLimit(minPageNumberLimit - pageNumberLimit);
54 | }
55 | };
56 |
57 | let pageIncrementBtn = null;
58 | if (pages.length > maxPageNumberLimit) {
59 | pageIncrementBtn = … ;
60 | }
61 |
62 | let pageDecrementBtn = null;
63 | if (minPageNumberLimit >= 1) {
64 | pageDecrementBtn = … ;
65 | }
66 |
67 | return (
68 | <>
69 |
70 |
71 |
75 | Prev
76 |
77 |
78 | {pageDecrementBtn}
79 | {renderPageNumbers}
80 | {pageIncrementBtn}
81 |
82 |
86 | Next
87 |
88 |
89 |
90 | >
91 | );
92 | }
93 |
94 | export default React.memo(Pagination);
--------------------------------------------------------------------------------
/src/components/DashboardComponents/DashboardComponents.module.css:
--------------------------------------------------------------------------------
1 | /* main style */
2 | .main_movies_tabs {
3 | margin-top: 60px;
4 | }
5 | .main_movies_tabs button {
6 | font-size: 14px;
7 | color: #fff;
8 | background-color: transparent;
9 | outline: none;
10 | border: none;
11 | padding: 7px 15px;
12 | margin-right: 10px;
13 | border-radius: 4px;
14 | }
15 | button.active_tabs {
16 | background-color: red;
17 | transition: all 0.4s linear;
18 | }
19 | button.not_active_tab:hover {
20 | color: red;
21 | transition: all 0.3s linear;
22 | }
23 |
24 | @media screen and (min-width: 982px) {
25 | .main_movies_tabs {
26 | padding-left: 45px;
27 | }
28 | }
29 | /* ------------------------------- Profile Settings ------------------------------- */
30 | /* profile_settings */
31 | .profile_settings {
32 | color: #fff;
33 | padding: 30px 45px;
34 | }
35 | .profile_settings_details {
36 | display: flex;
37 | /* flex-wrap: wrap; */
38 | }
39 | .profile_settings_details div:first-child img {
40 | width: 150px;
41 | }
42 | .profile_settings_details div:last-child {
43 | margin-left: 30px;
44 | }
45 | .profile_settings_details div:last-child p:nth-child(1) {
46 | font-weight: 700;
47 | font-size: 33px;
48 | }
49 | .profile_settings_details div:last-child p:nth-child(2) {
50 | font-size: 14px;
51 | margin-top: -10px;
52 | }
53 |
54 | /* profile_settings_form */
55 | .profile_settings_form {
56 | display: flex;
57 | flex-direction: column;
58 | }
59 | .profile_settings_form h5 {
60 | font-weight: 600;
61 | font-size: 19px;
62 | border-bottom: 1px solid #fff;
63 | margin-top: 40px;
64 | padding-bottom: 20px;
65 | }
66 | .profile_settings_input {
67 | display: flex;
68 | flex-direction: column;
69 | }
70 | .profile_settings_input input,
71 | .profile_settings_input select {
72 | border-radius: 0 !important;
73 | background-color: transparent;
74 | color: #959595;
75 | border: 1px solid #fff;
76 | padding: 13px 20px;
77 | font-size: 13px;
78 | }
79 | .profile_settings_input input:focus,
80 | .profile_settings_input select:focus {
81 | background-color: #fff;
82 | border: 1px solid red;
83 | outline: none;
84 | }
85 | .profile_settings_input span {
86 | padding: 20px 0 8px 0;
87 | font-size: 15px;
88 | }
89 | .profile_settings_form button {
90 | color: #fff;
91 | margin-top: 30px;
92 | background-color: red;
93 | outline: none;
94 | border: none;
95 | padding: 10px 25px;
96 | margin-right: 20px;
97 | }
98 | .profile_settings_form button:hover {
99 | color: #fff;
100 | background-color: #333;
101 | transition: all 0.2s ease-in;
102 | }
103 |
104 | /* ------------------------------- Watch List ------------------------------- */
105 | .empty_watch_list {
106 | display: flex;
107 | justify-content: center;
108 | flex-direction: column;
109 | align-items: center;
110 | color: #fff;
111 | }
112 | .empty_watch_list i {
113 | font-size: 60px;
114 | color: rgb(255, 0, 0);
115 | padding-bottom: 20px;
116 | }
117 |
--------------------------------------------------------------------------------
/src/components/Cards/PricingPlansCard/PricingPlansCard.js:
--------------------------------------------------------------------------------
1 | import styles from './PricingPlansCard.module.css';
2 |
3 | export default function PricingPlansCard({type}) {
4 |
5 | return (
6 | <>
7 | {type === 'standard' &&
8 |
9 |
STANDARD
10 |
$0 /month
11 |
12 |
13 |
New Movies
14 | Streamit Special
15 | American Tv Shows
16 | Hollywood Movies
17 | FHD (1080p) Video Quality
18 | Ad Free Entertainment
19 |
20 |
PURCHASE
21 |
}
22 | {type === 'platinum' &&
23 |
24 |
PLATINUM
25 |
$79 /month
26 |
27 |
28 |
New Movies
29 | Streamit Special
30 | American Tv Shows
31 | Hollywood Movies
32 | FHD (1080p) Video Quality
33 | Ad Free Entertainment
34 |
35 |
PURCHASE
36 |
}
37 | {type === 'premium' &&
38 |
39 |
PREMIUM
40 |
$120 /month
41 |
42 |
43 |
New Movies
44 | Streamit Special
45 | American Tv Shows
46 | Hollywood Movies
47 | FHD (1080p) Video Quality
48 | Ad Free Entertainment
49 |
50 |
PURCHASE
51 |
}
52 | >
53 | )
54 | }
55 |
--------------------------------------------------------------------------------
/src/components/Cards/WatchListCard/WatchListCard.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { useDispatch } from "react-redux";
3 | import { removeFromWatchList } from "../../../Redux/watchListSlice";
4 | import styles from "./WatchListCard.module.css";
5 | import noImage from '../../../img/ava.jpg';
6 |
7 | export default function WatchListCard({ id, img, name, year, time, detail }) {
8 | const [favorite, setFavorite] = useState(false);
9 | const [watched, setWatched] = useState(false);
10 |
11 | const noPoster =
12 | img === "" ||
13 | img === null ||
14 | img === undefined;
15 | let imageUrl = "";
16 | if (noPoster) {
17 | imageUrl = noImage;
18 | } else {
19 | imageUrl = `https://image.tmdb.org/t/p/w342/${img}`;
20 | }
21 |
22 | const dispatch = useDispatch();
23 |
24 | const handleRemoveFromWatchList = () => {
25 | dispatch(removeFromWatchList(id));
26 | };
27 |
28 | const handleFavorite = () => {
29 | setFavorite((prevFavorite) => !prevFavorite);
30 | };
31 | const handleWatched = () => {
32 | setWatched((prevWatched) => !prevWatched);
33 | };
34 |
35 | return (
36 |
37 |
38 |
39 |
{name}
40 |
41 | {year}
42 | {time}
43 |
44 |
{detail}
45 |
46 |
47 |
Where to Watch
48 |
49 |
50 | iTunes
51 |
52 |
53 | {" "}
54 | Hulu
55 |
56 |
57 | Amazon
58 |
59 |
60 |
61 | Netflix
62 |
63 |
64 |
65 |
66 |
67 |
68 | Remove
69 |
70 |
71 | {watched ? (
72 | <>
73 |
74 | Watched
75 | >
76 | ) : (
77 | <>
78 |
79 | Watched
80 | >
81 | )}
82 |
83 |
84 | {favorite ? (
85 | <>
86 |
87 | Favorite
88 | >
89 | ) : (
90 | <>
91 |
92 | Favorite
93 | >
94 | )}
95 |
96 |
97 |
98 | );
99 | }
100 |
--------------------------------------------------------------------------------
/src/components/Cards/PersonCard/PersonCard.js:
--------------------------------------------------------------------------------
1 | import { useState,useEffect,useCallback } from "react";
2 | import { useDispatch, useSelector } from "react-redux";
3 | import { fetchPerson, getPerson } from "../../../Redux/personSlice";
4 | import styles from "./PersonCard.module.css";
5 | import noImage from '../../../img/ava.jpg';
6 | import Loader from "../../Loader/Loader";
7 |
8 | export default function PersonCard({ personId }) {
9 | const dispatch = useDispatch();
10 | const personInfo = useSelector(getPerson);
11 |
12 | useEffect(() => {
13 | window.scrollTo(0,0);
14 | dispatch(
15 | fetchPerson({
16 | id: personId,
17 | })
18 | );
19 | }, [personId, dispatch]);
20 |
21 | const noPoster =
22 | personInfo.profile_path === "" ||
23 | personInfo.profile_path === null ||
24 | personInfo.profile_path === undefined;
25 |
26 | let imageUrl = "";
27 | if (noPoster) {
28 | imageUrl = noImage;
29 | } else {
30 | imageUrl = `https://image.tmdb.org/t/p/w342/${personInfo.profile_path}`;
31 | }
32 |
33 | // show spinner before loading poster/image
34 | const [loading, setLoading] = useState(false);
35 |
36 | useEffect(() => {
37 | return () => setLoading(false);
38 | }, []);
39 |
40 | const onLoading = useCallback(() => {
41 | setLoading(true);
42 | }, [setLoading]);
43 |
44 | return (
45 |
46 |
47 |
48 |
49 | {noPoster ? (
50 |
51 | ) : (
52 | <>
53 | {!loading ?
: null}
54 |
60 | >
61 | )}
62 |
63 |
64 |
65 |
66 | Popularity:{" "}
67 | {personInfo.popularity}
68 |
69 |
73 |
74 |
75 |
76 |
77 |
78 |
{personInfo.name}
79 |
80 |
81 | Career:{" "}
82 | {personInfo.known_for_department}
83 |
84 |
85 | Date of birth:{" "}
86 | {personInfo.birthday}
87 |
88 |
89 | Popularity:{" "}
90 | {personInfo.popularity}
91 |
92 |
93 | Born in:{" "}
94 | {personInfo.place_of_birth}
95 |
96 |
97 |
98 |
Biography:
99 |
{personInfo.biography}
100 |
101 |
102 |
103 | );
104 | }
105 |
--------------------------------------------------------------------------------
/src/img/spinner.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/components/Cards/SinglePageCard/SinglePageCard.module.css:
--------------------------------------------------------------------------------
1 | .movie_page_card_wrapper {
2 | display: flex;
3 | /* flex-wrap: wrap; */
4 | color: #fff;
5 | }
6 | /* LEFT */
7 | .movie_page_card_left {
8 | margin-right: 40px;
9 | display: flex;
10 | flex-direction: column;
11 | align-items: center;
12 | margin-left: 30px;
13 | }
14 | .movie_page_card_left img {
15 | width: 400px;
16 | border-radius: 20px;
17 | box-shadow: 6px 6px 32px -12px rgb(0 0 0 / 75%);
18 | }
19 | .movie_page_card_left_content {
20 | margin: 15px;
21 | }
22 | .movie_page_card_left_content span {
23 | margin: 0 15px;
24 | color: #a3a3a3;
25 | font-size: 16px;
26 | }
27 | .movie_page_card_left_content a i {
28 | color: #ffc107;
29 | font-size: 23px;
30 | vertical-align: -2px;
31 | padding-right: 3px;
32 | }
33 | .movie_page_card_left_content a i:hover {
34 | transform: scale(1.1);
35 | }
36 | .movie_page_card_left_content span:nth-child(4) i {
37 | color: #ffc107;
38 | font-size: 12px;
39 | vertical-align: 1px;
40 | padding-right: 3px;
41 | }
42 | /* RIGHT */
43 | .movie_page_card_right {
44 | margin-left: 30px;
45 | width: 90%;
46 | }
47 | .movie_page_card_right h2 {
48 | font-weight: 600;
49 | margin: 20px 0;
50 | text-transform: uppercase;
51 | }
52 | .movie_page_card_right_info {
53 | color: #a3a3a3;
54 | display: flex;
55 | flex-direction: row;
56 | }
57 | .movie_page_card_right_info div {
58 | display: flex;
59 | flex-direction: column;
60 | }
61 | .movie_page_card_right_info p {
62 | margin-right: 35px;
63 | font-size: 16px;
64 | }
65 | .movie_page_card_right_info i {
66 | margin-right: 5px;
67 | }
68 | .movie_page_card_right_info p span {
69 | color: #dddddd;
70 | }
71 | .movie_page_card_right_desc {
72 | font-size: 14px;
73 | margin: 40px 0;
74 | width: 90%;
75 | }
76 | .movie_page_card_right_cast {
77 | display: flex;
78 | flex-wrap: wrap;
79 | }
80 | .movie_page_card_right_cast div:first-child {
81 | margin-right: 230px;
82 | }
83 | .movie_page_card_right_cast div:first-child p:last-child {
84 | white-space: nowrap;
85 | }
86 | .movie_page_card_right_cast div p:first-child {
87 | font-weight: 600;
88 | margin-bottom: 0;
89 | font-size: 18px;
90 | }
91 | .movie_page_card_right_cast div a {
92 | color: #fff;
93 | font-size: 14px;
94 | width: 60%;
95 | }
96 | .movie_page_card_right_cast div a:hover {
97 | text-shadow: 0px 0px 5px rgb(184 184 184);
98 | }
99 | .movie_page_card_right_btns button {
100 | color: #fff;
101 | margin-top: 40px;
102 | background-color: red;
103 | outline: none;
104 | border: none;
105 | padding: 10px 0px;
106 | width: 280px;
107 | margin-right: 20px;
108 | border-radius: 8px;
109 | }
110 | .movie_page_card_right_btns button i {
111 | padding-right: 10px;
112 | }
113 | .movie_page_card_right_btns button:hover {
114 | color: #fff;
115 | background-color: #333;
116 | transition: all 0.2s ease-in;
117 | }
118 |
119 | @media screen and (max-width: 1146px) {
120 | .movie_page_card_wrapper {
121 | flex-wrap: wrap;
122 | }
123 | .movie_page_card_left {
124 | margin-left: 0px;
125 | margin-top: -150px;
126 | }
127 | .movie_page_card_left img {
128 | max-width: 100%;
129 | text-align: center;
130 | margin-left: 20px;
131 | }
132 | .movie_page_card_right {
133 | margin-left: 0px;
134 | }
135 | .movie_page_card_right_info {
136 | flex-wrap: wrap;
137 | }
138 | .movie_page_card_right_btns button {
139 | width: 43%;
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
48 | ### Code Splitting
49 |
50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
51 |
52 | ### Analyzing the Bundle Size
53 |
54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
55 |
56 | ### Making a Progressive Web App
57 |
58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
59 |
60 | ### Advanced Configuration
61 |
62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
63 |
64 | ### Deployment
65 |
66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
67 |
68 | ### `npm run build` fails to minify
69 |
70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
71 |
--------------------------------------------------------------------------------
/src/components/DashboardComponents/ProfileSettings.js:
--------------------------------------------------------------------------------
1 | import styles from './DashboardComponents.module.css';
2 |
3 | export default function ProfileSettings() {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
Mehdi H
12 |
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Iure ratione neque consequuntur eius, unde animi temporibus excepturi exercitationem accusantium deleniti mollitia
13 |
14 |
15 |
16 |
General Information
17 |
35 |
Change Password
36 |
46 |
Personal Information
47 |
48 |
49 | Date of Birth
50 |
51 |
52 |
53 | Gender
54 |
55 | Male
56 | Female
57 |
58 |
59 |
60 | Language
61 |
62 | English
63 | Persian
64 | Arabic
65 | German
66 | French
67 | Italian
68 | Russian
69 | Spanish
70 |
71 |
72 |
73 |
74 | Save
75 | Cancel
76 |
77 |
78 |
79 | )
80 | }
81 |
--------------------------------------------------------------------------------
/src/components/Cards/SinglePageCard/MoviePageCard/MoviePageCard.js:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect, useState } from "react";
2 | import { useDispatch, useSelector } from "react-redux";
3 | import { Link } from "react-router-dom";
4 | import {
5 | fetchSingleMovie,
6 | fetchSingleMovieCredits,
7 | getSingleMovie,
8 | getSingleMovieCredits,
9 | } from "../../../../Redux/singleMovieSlice";
10 | import { addToWatchList } from "../../../../Redux/watchListSlice";
11 | import styles from "../SinglePageCard.module.css";
12 | import noImage from "../../../../img/ava.jpg";
13 | import Loader from "../../../Loader/Loader";
14 | import ModalVideo from "react-modal-video";
15 | import { fetchVideoMovie, getVideo } from "../../../../Redux/videoSlice";
16 |
17 | export default function MoviePageCard({ movieId }) {
18 | const movieData = useSelector(getSingleMovie);
19 | const movieCredits = useSelector(getSingleMovieCredits);
20 | const videoTrailer = useSelector(getVideo);
21 | const dispatch = useDispatch();
22 |
23 | useEffect(() => {
24 | window.scrollTo(0,0);
25 | dispatch(
26 | fetchSingleMovie({
27 | id: movieId,
28 | })
29 | );
30 | dispatch(
31 | fetchSingleMovieCredits({
32 | id: movieId,
33 | })
34 | );
35 | dispatch(
36 | fetchVideoMovie({
37 | id: movieId,
38 | })
39 | );
40 | }, [movieId, dispatch]);
41 |
42 | const handleAddToWatchList = () => {
43 | dispatch(addToWatchList(movieData));
44 | };
45 |
46 | // movie data
47 | const noPoster =
48 | movieData.poster_path === "" ||
49 | movieData.poster_path === null ||
50 | movieData.poster_path === undefined;
51 | let imageUrl = "";
52 | if (noPoster) {
53 | imageUrl = noImage;
54 | } else {
55 | imageUrl = `https://image.tmdb.org/t/p/w780/${movieData.poster_path}`;
56 | }
57 | const movieLang = movieData.spoken_languages
58 | ?.map((lang) => `${lang.name}`)
59 | .join(" , ");
60 | const movieGenre = movieData.genres
61 | ?.map((genre) => `${genre.name}`)
62 | .join(" , ");
63 | const movieCompanies = movieData.production_companies
64 | ?.map((companies) => `${companies.name}`)
65 | .join(" , ");
66 | const movieCountries = movieData.production_countries
67 | ?.map((countries) => `${countries.name}`)
68 | .join(" , ");
69 | const director = movieCredits.crew?.filter((c) => c.job === "Director");
70 | const castName = movieCredits.cast;
71 |
72 | // show spinner before loading poster/image
73 | const [loading, setLoading] = useState(false);
74 |
75 | useEffect(() => {
76 | return () => setLoading(false);
77 | }, []);
78 |
79 | const onLoading = useCallback(() => {
80 | setLoading(true);
81 | }, [setLoading]);
82 |
83 | // video Modal
84 | const [isOpen, setOpen] = useState(false);
85 | let videoId = "";
86 | if (videoTrailer[0] === undefined) {
87 | videoId = null;
88 | } else {
89 | videoId = videoTrailer[0].key;
90 | }
91 | const handleOpenModal = useCallback(
92 | (e) => {
93 | e.preventDefault();
94 | setOpen(true);
95 | },
96 | [setOpen]
97 | );
98 |
99 | return (
100 |
101 |
102 |
103 | {noPoster ? (
104 |
105 | ) : (
106 | <>
107 | {!loading ?
: null}
108 |
114 | >
115 | )}
116 |
117 |
118 |
1080p
119 |
24p
120 |
121 |
122 |
123 |
124 |
125 | {movieData.vote_average}
126 |
127 |
128 |
129 |
130 |
{movieData.title}
131 |
132 |
133 |
134 | Duration:{" "}
135 | {movieData.runtime}m
136 |
137 |
138 | Director:{" "}
139 | {movieCredits.crew && director[0]?.name}
140 |
141 |
142 | Release Date:{" "}
143 | {movieData.release_date}
144 |
145 |
146 | Language:{" "}
147 | {movieData && movieLang}
148 |
149 |
150 |
151 |
152 | Genres: {movieGenre}
153 |
154 |
155 | Budget:{" "}
156 | ${movieData.budget}
157 |
158 |
159 | Company:{" "}
160 | {movieCompanies}
161 |
162 |
163 | Country:{" "}
164 | {movieCountries}
165 |
166 |
167 |
168 |
169 | {movieData.overview}
170 |
171 |
172 |
173 |
Director
174 |
175 | {movieCredits.crew && director[0]?.name}
176 |
177 |
178 |
179 |
Cast
180 |
181 | {castName?.slice(0, 7).map((n) => (
182 |
183 | {" "}
184 | {`${n.name}`} ,{" "}
185 |
186 | ))}
187 |
188 |
189 |
190 |
191 |
192 | Play
193 |
194 |
195 | My List
196 |
197 |
198 | Trailer
199 |
200 | setOpen(false)}
206 | />
207 |
208 | Share
209 |
210 |
211 |
212 |
213 | );
214 | }
215 |
--------------------------------------------------------------------------------
/src/components/Cards/SinglePageCard/TvPageCard/TvPageCard.js:
--------------------------------------------------------------------------------
1 | import { useCallback, useEffect, useState } from "react";
2 | import { useDispatch, useSelector } from "react-redux";
3 | import { Link } from "react-router-dom";
4 | import {
5 | fetchSingleTv,
6 | fetchSingleTvCredits,
7 | getSingleTv,
8 | getSingleTvCredits,
9 | } from "../../../../Redux/singleTvSlice";
10 | import { addToWatchList } from "../../../../Redux/watchListSlice";
11 | import styles from "../SinglePageCard.module.css";
12 | import noImage from "../../../../img/ava.jpg";
13 | import Loader from "../../../Loader/Loader";
14 | import { fetchVideoTv, getVideo } from "../../../../Redux/videoSlice";
15 | import ModalVideo from "react-modal-video";
16 |
17 | export default function TvPageCard({ tvId }) {
18 | const tvData = useSelector(getSingleTv);
19 | const tvCredits = useSelector(getSingleTvCredits);
20 | const videoTrailer = useSelector(getVideo);
21 |
22 | const dispatch = useDispatch();
23 |
24 | useEffect(() => {
25 | window.scrollTo(0,0);
26 | dispatch(
27 | fetchSingleTv({
28 | id: tvId,
29 | })
30 | );
31 | dispatch(
32 | fetchSingleTvCredits({
33 | id: tvId,
34 | })
35 | );
36 | dispatch(
37 | fetchVideoTv({
38 | id: tvId,
39 | })
40 | );
41 | }, [tvId, dispatch]);
42 |
43 | const handleAddToWatchList = () => {
44 | dispatch(addToWatchList(tvData));
45 | };
46 |
47 | // tv data
48 | const noPoster =
49 | tvData?.poster_path === "" ||
50 | tvData?.poster_path === null ||
51 | tvData?.poster_path === undefined;
52 | let imageUrl = "";
53 | if (noPoster) {
54 | imageUrl = noImage;
55 | } else {
56 | imageUrl = `https://image.tmdb.org/t/p/w780/${tvData?.poster_path}`;
57 | }
58 | const movieLang = tvData.spoken_languages
59 | ?.map((lang) => `${lang.name}`)
60 | .join(" , ");
61 | const movieGenre = tvData.genres?.map((genre) => `${genre.name}`).join(" , ");
62 | const tvNetworks = tvData.networks
63 | ?.map((network) => `${network.name}`)
64 | .join(" , ");
65 | const tvCompany = tvData.production_companies
66 | ?.map((company) => `${company.name}`)
67 | .join(" , ");
68 | const movieCountries = tvData.production_countries
69 | ?.map((countries) => `${countries.name}`)
70 | .join(" , ");
71 | const director = tvCredits.crew?.filter((c) => c.job === "Director");
72 | const director2 = tvData.created_by?.map((d) => d.name).join(" , ");
73 | const director2id = tvData.created_by?.map((d) => d.id);
74 | const castName = tvCredits.cast;
75 |
76 | // show spinner before loading poster/image
77 | const [loading, setLoading] = useState(false);
78 |
79 | useEffect(() => {
80 | return () => setLoading(false);
81 | }, []);
82 |
83 | const onLoading = useCallback(() => {
84 | setLoading(true);
85 | }, [setLoading]);
86 |
87 | // video Modal
88 | const [isOpen, setOpen] = useState(false);
89 | let videoId = "";
90 | if (videoTrailer[0] === undefined) {
91 | videoId = null;
92 | } else {
93 | videoId = videoTrailer[0].key;
94 | }
95 | const handleOpenModal = useCallback(
96 | (e) => {
97 | e.preventDefault();
98 | setOpen(true);
99 | },
100 | [setOpen]
101 | );
102 |
103 | return (
104 |
105 |
106 |
107 | {noPoster ? (
108 |
109 | ) : (
110 | <>
111 | {!loading ?
: null}
112 |
118 | >
119 | )}
120 |
121 |
122 |
1080p
123 |
24p
124 |
125 |
126 |
127 |
128 |
129 | {tvData.vote_average}
130 |
131 |
132 |
133 |
134 |
{tvData.title}
135 |
136 |
137 |
138 | Episode Duration:{" "}
139 | {tvData.episode_run_time?.join().slice(0, 2)}m
140 |
141 |
142 | Director:{" "}
143 | {(tvCredits.crew && director[0]?.name) || director2}
144 |
145 |
146 | Genres: {movieGenre}
147 |
148 |
149 | Release Date:{" "}
150 | {tvData.first_air_date}
151 |
152 |
153 | Status:{" "}
154 | {tvData.status}
155 |
156 |
157 | Language:{" "}
158 | {tvData && movieLang}
159 |
160 |
161 |
162 |
163 | Type:{" "}
164 | {tvData.type}
165 |
166 |
167 | Seasons:{" "}
168 | {tvData.number_of_seasons}
169 |
170 |
171 | Episodes:{" "}
172 | {tvData.number_of_episodes}
173 |
174 |
175 | Network:{" "}
176 | {tvNetworks}
177 |
178 |
179 | Company:{" "}
180 | {tvCompany}
181 |
182 |
183 | Country:{" "}
184 | {movieCountries}
185 |
186 |
187 |
188 |
{tvData.overview}
189 |
190 |
191 |
Director
192 |
193 | {" "}
194 | {(tvCredits.crew && director[0]?.name) || director2}
195 |
196 |
197 |
198 |
Cast
199 |
200 | {castName?.slice(0, 7).map((n) => (
201 | {`${n.name}`} ,
202 | ))}
203 |
204 |
205 |
206 |
207 |
208 | Play
209 |
210 |
211 | My List
212 |
213 |
214 | Trailer
215 |
216 | setOpen(false)}
222 | />
223 |
224 | Share
225 |
226 |
227 |
228 |
229 | );
230 | }
231 |
--------------------------------------------------------------------------------