From d2c578ef8c3bf17ef995e3120949c7c86c597757 Mon Sep 17 00:00:00 2001 From: hai0z Date: Sat, 10 Jun 2023 08:11:27 +0700 Subject: [PATCH] ok --- .github/workflows/nextjs.yml | 190 ++--- app/(store)/store.ts | 58 +- app/PageWraper.tsx | 88 +-- app/components/CardSlide.tsx | 108 +-- app/components/Header.tsx | 276 ++++---- .../MediaList/ChangeMediaListBtn.tsx | 100 +-- app/components/MediaList/MediaList.tsx | 54 +- app/components/Modal.tsx | 92 +-- app/components/MovieCard.tsx | 312 ++++---- app/components/MovieDetailTab.tsx | 126 ++-- app/components/Pagination.tsx | 222 +++--- app/components/PeopleCard.tsx | 72 +- app/components/ShadowImg.tsx | 116 +-- app/components/Sidebar.tsx | 670 +++++++++--------- app/components/Slider.tsx | 666 ++++++++--------- app/components/StickyTab.tsx | 84 +-- app/components/WatchTrailerButton.tsx | 36 +- app/components/mobile/MobileTab.tsx | 310 ++++---- app/components/search/Search.tsx | 52 +- app/discover/movies/page.tsx | 78 +- app/discover/page.tsx | 240 +++---- app/genres/layout.tsx | 44 +- app/genres/movie/page.tsx | 54 +- app/genres/tv-show/page.tsx | 14 +- app/globals.css | 74 +- app/loading.tsx | 40 +- app/movie/(discover)/layout.tsx | 14 +- app/movie/(discover)/popular/page.tsx | 14 +- app/movie/[movieId]/cast/page.tsx | 48 +- app/movie/[movieId]/error.tsx | 24 +- app/movie/[movieId]/layout.tsx | 280 ++++---- app/movie/[movieId]/loading.tsx | 40 +- app/movie/[movieId]/page.tsx | 298 ++++---- app/movie/[movieId]/photos/page.tsx | 172 ++--- app/movie/[movieId]/recommendations/page.tsx | 42 +- app/movie/[movieId]/videos/page.tsx | 180 ++--- app/page.tsx | 138 ++-- app/people/[peopleId]/credits/page.tsx | 49 +- app/people/[peopleId]/layout.tsx | 154 ++-- app/people/[peopleId]/media/page.tsx | 100 +-- app/people/[peopleId]/page.tsx | 74 +- app/people/page.tsx | 66 +- app/search/layout.tsx | 46 +- app/search/movie/page.tsx | 134 ++-- app/search/tv/page.tsx | 130 ++-- app/setting/page.tsx | 494 ++++++------- app/trending/layout.tsx | 52 +- app/trending/today/page.tsx | 82 +-- app/trending/week/page.tsx | 66 +- app/tv-show/[tvId]/cast/page.tsx | 72 +- app/tv-show/[tvId]/error.tsx | 24 +- app/tv-show/[tvId]/layout.tsx | 230 +++--- app/tv-show/[tvId]/loading.tsx | 40 +- app/tv-show/[tvId]/page.tsx | 294 ++++---- app/tv-show/[tvId]/recommendations/page.tsx | 74 +- context/AppProvider.tsx | 112 +-- hooks/useWindowDemensions.tsx | 84 +-- lib/prisma/index.ts | 10 +- lib/prisma/task.ts | 42 +- package.json | 2 +- service/TMDB.ts | 282 ++++---- service/TMDB.type.ts | 352 ++++----- 62 files changed, 4248 insertions(+), 4243 deletions(-) diff --git a/.github/workflows/nextjs.yml b/.github/workflows/nextjs.yml index 17c5fcc..82d4109 100644 --- a/.github/workflows/nextjs.yml +++ b/.github/workflows/nextjs.yml @@ -1,95 +1,95 @@ -# Sample workflow for building and deploying a Next.js site to GitHub Pages -# -# To get started with Next.js see: https://nextjs.org/docs/getting-started -# -name: Deploy Next.js site to Pages - -on: - # Runs on pushes targeting the default branch - push: - branches: ["main"] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - # Build job - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Detect package manager - id: detect-package-manager - run: | - if [ -f "${{ github.workspace }}/yarn.lock" ]; then - echo "manager=yarn" >> $GITHUB_OUTPUT - echo "command=install" >> $GITHUB_OUTPUT - echo "runner=yarn" >> $GITHUB_OUTPUT - exit 0 - elif [ -f "${{ github.workspace }}/package.json" ]; then - echo "manager=npm" >> $GITHUB_OUTPUT - echo "command=ci" >> $GITHUB_OUTPUT - echo "runner=npx --no-install" >> $GITHUB_OUTPUT - exit 0 - else - echo "Unable to determine package manager" - exit 1 - fi - - name: Setup Node - uses: actions/setup-node@v3 - with: - node-version: "16" - cache: ${{ steps.detect-package-manager.outputs.manager }} - - name: Setup Pages - uses: actions/configure-pages@v3 - with: - # Automatically inject basePath in your Next.js configuration file and disable - # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized). - # - # You may remove this line if you want to manage the configuration yourself. - static_site_generator: next - - name: Restore cache - uses: actions/cache@v3 - with: - path: | - .next/cache - # Generate a new cache whenever packages or source files change. - key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} - # If source files changed but packages didn't, rebuild from a prior cache. - restore-keys: | - ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- - - name: Install dependencies - run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} - - name: Build with Next.js - run: ${{ steps.detect-package-manager.outputs.runner }} next build - - name: Static HTML export with Next.js - run: ${{ steps.detect-package-manager.outputs.runner }} next export - - name: Upload artifact - uses: actions/upload-pages-artifact@v1 - with: - path: ./out - - # Deployment job - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v2 +# Sample workflow for building and deploying a Next.js site to GitHub Pages +# +# To get started with Next.js see: https://nextjs.org/docs/getting-started +# +name: Deploy Next.js site to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Detect package manager + id: detect-package-manager + run: | + if [ -f "${{ github.workspace }}/yarn.lock" ]; then + echo "manager=yarn" >> $GITHUB_OUTPUT + echo "command=install" >> $GITHUB_OUTPUT + echo "runner=yarn" >> $GITHUB_OUTPUT + exit 0 + elif [ -f "${{ github.workspace }}/package.json" ]; then + echo "manager=npm" >> $GITHUB_OUTPUT + echo "command=ci" >> $GITHUB_OUTPUT + echo "runner=npx --no-install" >> $GITHUB_OUTPUT + exit 0 + else + echo "Unable to determine package manager" + exit 1 + fi + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: "16" + cache: ${{ steps.detect-package-manager.outputs.manager }} + - name: Setup Pages + uses: actions/configure-pages@v3 + with: + # Automatically inject basePath in your Next.js configuration file and disable + # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized). + # + # You may remove this line if you want to manage the configuration yourself. + static_site_generator: next + - name: Restore cache + uses: actions/cache@v3 + with: + path: | + .next/cache + # Generate a new cache whenever packages or source files change. + key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} + # If source files changed but packages didn't, rebuild from a prior cache. + restore-keys: | + ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- + - name: Install dependencies + run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} + - name: Build with Next.js + run: ${{ steps.detect-package-manager.outputs.runner }} next build + - name: Static HTML export with Next.js + run: ${{ steps.detect-package-manager.outputs.runner }} next export + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + path: ./out + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v2 diff --git a/app/(store)/store.ts b/app/(store)/store.ts index 10bda85..ffeeabf 100644 --- a/app/(store)/store.ts +++ b/app/(store)/store.ts @@ -1,29 +1,29 @@ -import { create } from "zustand"; - -interface Store { - videoId: string; - setVideoId: (id: string) => void; - theme: string; - setTheme: (theme: string) => void; - currentSlideIndex: number; - setCurrentSlideIndex: (index: number) => void; - mediaType: "grid" | "compact" | "list"; - setMediaType: (type: "grid" | "compact" | "list") => void; - expandedSideBar: boolean; - setExpandedSideBar: (open: boolean) => void; -} -const useStore = create((set) => ({ - videoId: "", - theme: "", - currentSlideIndex: 0, - mediaType: "grid", - expandedSideBar: true, - setVideoId: (param) => set({ videoId: param }), - setTheme: (param) => set({ theme: param }), - setCurrentSlideIndex: (param) => set({ currentSlideIndex: param }), - setMediaType: (type: "grid" | "compact" | "list") => - set({ mediaType: type }), - setExpandedSideBar: (open: boolean) => - set(() => ({ expandedSideBar: !open })), -})); -export default useStore; +import { create } from "zustand"; + +interface Store { + videoId: string; + setVideoId: (id: string) => void; + theme: string; + setTheme: (theme: string) => void; + currentSlideIndex: number; + setCurrentSlideIndex: (index: number) => void; + mediaType: "grid" | "compact" | "list"; + setMediaType: (type: "grid" | "compact" | "list") => void; + expandedSideBar: boolean; + setExpandedSideBar: (open: boolean) => void; +} +const useStore = create((set) => ({ + videoId: "", + theme: "", + currentSlideIndex: 0, + mediaType: "grid", + expandedSideBar: true, + setVideoId: (param) => set({ videoId: param }), + setTheme: (param) => set({ theme: param }), + setCurrentSlideIndex: (param) => set({ currentSlideIndex: param }), + setMediaType: (type: "grid" | "compact" | "list") => + set({ mediaType: type }), + setExpandedSideBar: (open: boolean) => + set(() => ({ expandedSideBar: !open })), +})); +export default useStore; diff --git a/app/PageWraper.tsx b/app/PageWraper.tsx index 210f935..a6fe46e 100644 --- a/app/PageWraper.tsx +++ b/app/PageWraper.tsx @@ -1,44 +1,44 @@ -"use client"; -import React from "react"; -import { motion } from "framer-motion"; -import NextTopLoader from "nextjs-toploader"; -import Header from "./components/Header"; -import useStore from "./(store)/store"; -import useWindowDimensions from "@/hooks/useWindowDemensions"; -import AppProvider from "@/context/AppProvider"; -import Sidebar from "./components/Sidebar"; -import MobileTab from "./components/mobile/MobileTab"; - -function PageWraper({ children }: { children: React.ReactNode }) { - const expandedSideBar = useStore((state) => state.expandedSideBar); - const { width } = useWindowDimensions(); - - return ( - - -
- - -
-
768 ? (expandedSideBar ? 250 : 80) : 0, - }} - > - -
- - {children} - -
-
-
- ); -} - -export default PageWraper; +"use client"; +import React from "react"; +import { motion } from "framer-motion"; +import NextTopLoader from "nextjs-toploader"; +import Header from "./components/Header"; +import useStore from "./(store)/store"; +import useWindowDimensions from "@/hooks/useWindowDemensions"; +import AppProvider from "@/context/AppProvider"; +import Sidebar from "./components/Sidebar"; +import MobileTab from "./components/mobile/MobileTab"; + +function PageWraper({ children }: { children: React.ReactNode }) { + const expandedSideBar = useStore((state) => state.expandedSideBar); + const { width } = useWindowDimensions(); + + return ( + + +
+ + +
+
768 ? (expandedSideBar ? 250 : 80) : 0, + }} + > + +
+ + {children} + +
+
+
+ ); +} + +export default PageWraper; diff --git a/app/components/CardSlide.tsx b/app/components/CardSlide.tsx index 5260557..a70bfef 100644 --- a/app/components/CardSlide.tsx +++ b/app/components/CardSlide.tsx @@ -1,53 +1,55 @@ -"use client"; - -import React from "react"; -import { Movie, TrendingPeople } from "@/service/TMDB.type"; -import { Swiper, SwiperSlide } from "swiper/react"; -import { Navigation } from "swiper"; -import MovieCard from "./MovieCard"; -import "swiper/css"; -import "swiper/css/navigation"; -import useWindowDimensions from "@/hooks/useWindowDemensions"; -import PeopleCard from "./PeopleCard"; - -interface ICardPropsMovie { - type: "movie"; - data: { results: Movie[] }; -} - -interface ICardPropsPeople { - type: "people"; - data: { results: TrendingPeople[] }; -} - -type CardProps = ICardPropsMovie | ICardPropsPeople; - -function CardSlide({ data, type }: CardProps) { - const { width } = useWindowDimensions(); - const isMovie = type === "movie"; - const isPeople = type === "people"; - return ( - 1700 ? 1700 / 275 : width / 275} - > - {data.results.slice(0, 10).map((m) => ( - - {isMovie && } - {isPeople && } - - ))} - - ); -} - -export default CardSlide; +"use client"; + +import React from "react"; +import { Movie, TrendingPeople } from "@/service/TMDB.type"; +import { Swiper, SwiperSlide } from "swiper/react"; +import { Navigation } from "swiper"; +import MovieCard from "./MovieCard"; +import "swiper/css"; +import "swiper/css/navigation"; +import useWindowDimensions from "@/hooks/useWindowDemensions"; +import PeopleCard from "./PeopleCard"; + +interface ICardPropsMovie { + type: "movie"; + data: { results: Movie[] }; + sildePerView?: number | undefined; +} + +interface ICardPropsPeople { + type: "people"; + data: { results: TrendingPeople[] }; + sildePerView?: number | undefined; +} + +type CardProps = ICardPropsMovie | ICardPropsPeople; + +function CardSlide({ data, type, sildePerView }: CardProps) { + const { width } = useWindowDimensions(); + const isMovie = type === "movie"; + const isPeople = type === "people"; + return ( + 1700 ? 1700 / 275 : width / 275} + > + {data.results.slice(0, 10).map((m) => ( + + {isMovie && } + {isPeople && } + + ))} + + ); +} + +export default CardSlide; diff --git a/app/components/Header.tsx b/app/components/Header.tsx index faf0304..c14b247 100644 --- a/app/components/Header.tsx +++ b/app/components/Header.tsx @@ -1,138 +1,138 @@ -"use client"; -import React, { useEffect, useState } from "react"; -import Image from "next/image"; -import { usePathname, useParams } from "next/navigation"; -import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/24/solid"; -import { useRouter } from "next/navigation"; -import useStore from "../(store)/store"; -import { Movie } from "@/service/TMDB.type"; -import useWindowDimensions from "@/hooks/useWindowDemensions"; -import { motion } from "framer-motion"; -function Header() { - const [data, setData] = useState({} as Movie); - const pathname = usePathname(); - const params = useParams(); - const ImagePath = "https://image.tmdb.org/t/p/"; - const [isScroll, setIsScroll] = useState(false); - - const { width } = useWindowDimensions(); - - useEffect(() => { - const onScrollTop = () => { - if (window.scrollY > 90) { - setIsScroll(true); - } else { - setIsScroll(false); - } - }; - window.addEventListener("scroll", onScrollTop); - return () => window.removeEventListener("scroll", onScrollTop); - }, []); - useEffect(() => { - const getData = async () => { - const respone = await fetch( - `https://api.themoviedb.org/3/movie/${params.movieId}?api_key=${process.env.TMDB}&language=vi-VN` - ); - const data = await respone.json(); - setData(data); - }; - if (params.movieId !== undefined) { - getData(); - } - }, [params.movieId]); - const router = useRouter(); - const expandedSizeBar = useStore((state) => state.expandedSideBar); - return ( - 768 ? (expandedSizeBar ? 250 : 80) : 0 }} - className={`w-full px-4 ${ - isScroll && "bg-base-100 backdrop-blur-lg bg-opacity-80" - } ${ - pathname == "/" && width < 768 && "shadow-lg bg-base-100" - } flex flex-row items-center z-50 transition-all duration-150 h-16 md:h-16 fixed navbar `} - > -
-
- router.back()} - /> -
-
- router.forward()} - /> -
-
-
-

The Movies

-
-
- {isScroll && - pathname.includes("movie") && - !pathname.includes("search") && - !pathname.includes("discover") && - !pathname.includes("genres") && ( -
- film -
-

- {data?.title} -

-

- {pathname.split("/").at(-1) === - params.movieId - ? "overview" - : pathname.split("/").at(-1)} -

-
-
- )} - {isScroll && pathname == "/" && width >= 768 && ( -
-

- Trang chủ -

-
- )} - {isScroll && pathname.includes("trending") && ( -
-

- Xu hướng -

-
- )} - {isScroll && pathname.includes("discover") && ( -
-

- Khám phá -

-
- )} - {isScroll && pathname.includes("genres") && ( -
-

- Khám phá -

-
- )} -
-
- ); -} - -export default Header; +"use client"; +import React, { useEffect, useState } from "react"; +import Image from "next/image"; +import { usePathname, useParams } from "next/navigation"; +import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/24/solid"; +import { useRouter } from "next/navigation"; +import useStore from "../(store)/store"; +import { Movie } from "@/service/TMDB.type"; +import useWindowDimensions from "@/hooks/useWindowDemensions"; +import { motion } from "framer-motion"; +function Header() { + const [data, setData] = useState({} as Movie); + const pathname = usePathname(); + const params = useParams(); + const ImagePath = "https://image.tmdb.org/t/p/"; + const [isScroll, setIsScroll] = useState(false); + + const { width } = useWindowDimensions(); + + useEffect(() => { + const onScrollTop = () => { + if (window.scrollY > 90) { + setIsScroll(true); + } else { + setIsScroll(false); + } + }; + window.addEventListener("scroll", onScrollTop); + return () => window.removeEventListener("scroll", onScrollTop); + }, []); + useEffect(() => { + const getData = async () => { + const respone = await fetch( + `https://api.themoviedb.org/3/movie/${params.movieId}?api_key=${process.env.TMDB}&language=vi-VN` + ); + const data = await respone.json(); + setData(data); + }; + if (params.movieId !== undefined) { + getData(); + } + }, [params.movieId]); + const router = useRouter(); + const expandedSizeBar = useStore((state) => state.expandedSideBar); + return ( + 768 ? (expandedSizeBar ? 250 : 80) : 0 }} + className={`w-full px-4 ${ + isScroll && "bg-base-100 backdrop-blur-lg bg-opacity-80" + } ${ + pathname == "/" && width < 768 && "shadow-lg bg-base-100" + } flex flex-row items-center z-50 transition-all duration-150 h-16 md:h-16 fixed navbar `} + > +
+
+ router.back()} + /> +
+
+ router.forward()} + /> +
+
+
+

The Movies

+
+
+ {isScroll && + pathname.includes("movie") && + !pathname.includes("search") && + !pathname.includes("discover") && + !pathname.includes("genres") && ( +
+ film +
+

+ {data?.title} +

+

+ {pathname.split("/").at(-1) === + params.movieId + ? "overview" + : pathname.split("/").at(-1)} +

+
+
+ )} + {isScroll && pathname == "/" && width >= 768 && ( +
+

+ Trang chủ +

+
+ )} + {isScroll && pathname.includes("trending") && ( +
+

+ Xu hướng +

+
+ )} + {isScroll && pathname.includes("discover") && ( +
+

+ Khám phá +

+
+ )} + {isScroll && pathname.includes("genres") && ( +
+

+ Khám phá +

+
+ )} +
+
+ ); +} + +export default Header; diff --git a/app/components/MediaList/ChangeMediaListBtn.tsx b/app/components/MediaList/ChangeMediaListBtn.tsx index a2af129..53de071 100644 --- a/app/components/MediaList/ChangeMediaListBtn.tsx +++ b/app/components/MediaList/ChangeMediaListBtn.tsx @@ -1,50 +1,50 @@ -"use client"; -import useStore from "@/app/(store)/store"; -import React from "react"; - -function ChangeMediaListBtn() { - const mediaType = useStore((state) => state.mediaType); - const setMediaType = useStore((state) => state.setMediaType); - return ( -
- - - -
- ); -} - -export default ChangeMediaListBtn; +"use client"; +import useStore from "@/app/(store)/store"; +import React from "react"; + +function ChangeMediaListBtn() { + const mediaType = useStore((state) => state.mediaType); + const setMediaType = useStore((state) => state.setMediaType); + return ( +
+ + + +
+ ); +} + +export default ChangeMediaListBtn; diff --git a/app/components/MediaList/MediaList.tsx b/app/components/MediaList/MediaList.tsx index 5fe2c54..6b7bc2e 100644 --- a/app/components/MediaList/MediaList.tsx +++ b/app/components/MediaList/MediaList.tsx @@ -1,27 +1,27 @@ -"use client"; -import { Movie } from "@/service/TMDB.type"; -import React from "react"; -import { MovieCard1, MovieCard2, MovieCard3 } from "../MovieCard"; -import useStore from "../../(store)/store"; - -interface IPageProps { - movie: { results: Movie[] }; -} -function MediaList({ movie }: IPageProps) { - const mediaType = useStore((state) => state.mediaType); - return ( -
- {movie?.results.map((m, index) => { - return mediaType === "list" ? ( - - ) : mediaType == "compact" ? ( - - ) : ( - - ); - })} -
- ); -} - -export default MediaList; +"use client"; +import { Movie } from "@/service/TMDB.type"; +import React from "react"; +import { MovieCard1, MovieCard2, MovieCard3 } from "../MovieCard"; +import useStore from "../../(store)/store"; + +interface IPageProps { + movie: { results: Movie[] }; +} +function MediaList({ movie }: IPageProps) { + const mediaType = useStore((state) => state.mediaType); + return ( +
+ {movie?.results.map((m, index) => { + return mediaType === "list" ? ( + + ) : mediaType == "compact" ? ( + + ) : ( + + ); + })} +
+ ); +} + +export default MediaList; diff --git a/app/components/Modal.tsx b/app/components/Modal.tsx index 390898f..6b3b1c2 100644 --- a/app/components/Modal.tsx +++ b/app/components/Modal.tsx @@ -1,46 +1,46 @@ -"use client"; -import React, { useEffect, useRef, useState } from "react"; -import useStore from "../(store)/store"; - -function Modal() { - const videoId = useStore((state) => state.videoId); - const [video, setVideo] = useState(); - const videoRef = useRef(null); - - useEffect(() => { - const getTrailer = async () => { - const respone = await fetch( - `https://api.themoviedb.org/3/movie/${videoId}/videos?api_key=${process.env.TMDB}&language=en-US` - ); - const data = await respone.json(); - setVideo(data); - }; - getTrailer(); - return () => { - console.log("modal unmount"); - }; - }, [videoId]); - console.log(videoId); - return ( -
- -
-
- - -
-
-
- ); -} - -export default Modal; +"use client"; +import React, { useEffect, useRef, useState } from "react"; +import useStore from "../(store)/store"; + +function Modal() { + const videoId = useStore((state) => state.videoId); + const [video, setVideo] = useState(); + const videoRef = useRef(null); + + useEffect(() => { + const getTrailer = async () => { + const respone = await fetch( + `https://api.themoviedb.org/3/movie/${videoId}/videos?api_key=${process.env.TMDB}&language=en-US` + ); + const data = await respone.json(); + setVideo(data); + }; + getTrailer(); + return () => { + console.log("modal unmount"); + }; + }, [videoId]); + console.log(videoId); + return ( +
+ +
+
+ + +
+
+
+ ); +} + +export default Modal; diff --git a/app/components/MovieCard.tsx b/app/components/MovieCard.tsx index 53999ce..29ae1a3 100644 --- a/app/components/MovieCard.tsx +++ b/app/components/MovieCard.tsx @@ -1,156 +1,156 @@ -"use client"; -import React from "react"; -import Image from "next/image"; -import Link from "next/link"; -import { Movie, MovieCredits } from "@/service/TMDB.type"; -import tmdb from "@/service/TMDB"; -import { motion } from "framer-motion"; -function MovieCard2({ m, index }: { m: Movie; index: number }) { - return ( - -
- - cast - -
-
- - {m.name ?? m.title} - -
-

- TMDB {m.vote_average.toFixed(1)} -

-

- {m.overview} -

-
-
-
- ); -} -function MovieCard3({ m, index }: { m: Movie; index: number }) { - return ( - -
- - - cast - - -
-
- - {m.name ?? m.title} - -

- {m.overview} -

-
-
- ); -} -function MovieCard1({ m }: { m: Movie }) { - return ( - -
- - cast - -
-
- -

- {m.name ?? m.title} -

- -
-
- ); -} -function MovieCard({ m }: { m: Movie }) { - return ( - -
- - cast - -
-
- -

- {" "} - {m.name ?? m.title} -

- -
-
- ); -} -export { MovieCard2, MovieCard3, MovieCard1 }; -export default MovieCard; +"use client"; +import React from "react"; +import Image from "next/image"; +import Link from "next/link"; +import { Movie, MovieCredits } from "@/service/TMDB.type"; +import tmdb from "@/service/TMDB"; +import { motion } from "framer-motion"; +function MovieCard2({ m, index }: { m: Movie; index: number }) { + return ( + +
+ + cast + +
+
+ + {m.name ?? m.title} + +
+

+ TMDB {m.vote_average.toFixed(1)} +

+

+ {m.overview} +

+
+
+
+ ); +} +function MovieCard3({ m, index }: { m: Movie; index: number }) { + return ( + +
+ + + cast + + +
+
+ + {m.name ?? m.title} + +

+ {m.overview} +

+
+
+ ); +} +function MovieCard1({ m }: { m: Movie }) { + return ( + +
+ + cast + +
+
+ +

+ {m.name ?? m.title} +

+ +
+
+ ); +} +function MovieCard({ m }: { m: Movie }) { + return ( + +
+ + cast + +
+
+ +

+ {" "} + {m.name ?? m.title} +

+ +
+
+ ); +} +export { MovieCard2, MovieCard3, MovieCard1 }; +export default MovieCard; diff --git a/app/components/MovieDetailTab.tsx b/app/components/MovieDetailTab.tsx index da49640..7279126 100644 --- a/app/components/MovieDetailTab.tsx +++ b/app/components/MovieDetailTab.tsx @@ -1,63 +1,63 @@ -"use client"; -import React from "react"; -import Link from "next/link"; -import { useParams, usePathname } from "next/navigation"; -function MovieDetailTab() { - const params = useParams(); - const path = usePathname(); - - return ( -
- - Overview - - - Cast - - - Photos - - - Videos - - - Recommendations - -
- ); -} - -export default MovieDetailTab; +"use client"; +import React from "react"; +import Link from "next/link"; +import { useParams, usePathname } from "next/navigation"; +function MovieDetailTab() { + const params = useParams(); + const path = usePathname(); + + return ( +
+ + Overview + + + Cast + + + Photos + + + Videos + + + Recommendations + +
+ ); +} + +export default MovieDetailTab; diff --git a/app/components/Pagination.tsx b/app/components/Pagination.tsx index 8829ba2..4a8f785 100644 --- a/app/components/Pagination.tsx +++ b/app/components/Pagination.tsx @@ -1,111 +1,111 @@ -"use client"; - -import Link from "next/link"; -import React, { useState } from "react"; -import { motion } from "framer-motion"; -function Pagination({ - totalPages, - href, -}: { - totalPages: number; - href?: string; -}) { - const [currentPage, setCurrentPage] = useState(1); - - const handlePageClick = (pageNumber: number) => { - setCurrentPage(pageNumber); - }; - - const renderPageNumbers = () => { - const pageNumbers = []; - const maxDisplayedPages = 3; - - let startPage = Math.max( - 1, - currentPage - Math.floor(maxDisplayedPages / 2) - ); - let endPage = Math.min(totalPages, startPage + maxDisplayedPages - 1); - startPage = Math.max(1, endPage - maxDisplayedPages + 1); - - for (let i = startPage; i <= endPage; i++) { - pageNumbers.push( - handlePageClick(i)} - > - {i} - - ); - } - - pageNumbers.push( -
-

{"..."}

-
- ); - if (currentPage < totalPages) { - pageNumbers.push( - handlePageClick(totalPages)} - > - - - ); - } - - if (currentPage >= maxDisplayedPages) { - pageNumbers.unshift( -

- -

- ); - } - if (currentPage >= maxDisplayedPages) { - pageNumbers.unshift( - handlePageClick(1)} - > - - - ); - } - - return pageNumbers; - }; - - return ( -
- setCurrentPage(currentPage - 1)} - > - {"<"} - - {renderPageNumbers()} - setCurrentPage(currentPage + 1)} - > - {">"} - -
- ); -} - -export default Pagination; +"use client"; + +import Link from "next/link"; +import React, { useState } from "react"; +import { motion } from "framer-motion"; +function Pagination({ + totalPages, + href, +}: { + totalPages: number; + href?: string; +}) { + const [currentPage, setCurrentPage] = useState(1); + + const handlePageClick = (pageNumber: number) => { + setCurrentPage(pageNumber); + }; + + const renderPageNumbers = () => { + const pageNumbers = []; + const maxDisplayedPages = 3; + + let startPage = Math.max( + 1, + currentPage - Math.floor(maxDisplayedPages / 2) + ); + let endPage = Math.min(totalPages, startPage + maxDisplayedPages - 1); + startPage = Math.max(1, endPage - maxDisplayedPages + 1); + + for (let i = startPage; i <= endPage; i++) { + pageNumbers.push( + handlePageClick(i)} + > + {i} + + ); + } + + pageNumbers.push( +
+

{"..."}

+
+ ); + if (currentPage < totalPages) { + pageNumbers.push( + handlePageClick(totalPages)} + > + + + ); + } + + if (currentPage >= maxDisplayedPages) { + pageNumbers.unshift( +

+ +

+ ); + } + if (currentPage >= maxDisplayedPages) { + pageNumbers.unshift( + handlePageClick(1)} + > + + + ); + } + + return pageNumbers; + }; + + return ( +
+ setCurrentPage(currentPage - 1)} + > + {"<"} + + {renderPageNumbers()} + setCurrentPage(currentPage + 1)} + > + {">"} + +
+ ); +} + +export default Pagination; diff --git a/app/components/PeopleCard.tsx b/app/components/PeopleCard.tsx index bb5d858..39520a9 100644 --- a/app/components/PeopleCard.tsx +++ b/app/components/PeopleCard.tsx @@ -1,36 +1,36 @@ -import { TrendingPeople } from "@/service/TMDB.type"; -import Link from "next/link"; -import React from "react"; -import Image from "next/image"; -import tmdb from "@/service/TMDB"; -interface IPeopleCardProps { - people: TrendingPeople; -} -function PeopleCard({ people }: IPeopleCardProps) { - return ( -
-
- - cast - -
-
- -

- {" "} - {people.name ?? people.original_name} -

- -
-
- ); -} - -export default PeopleCard; +import { TrendingPeople } from "@/service/TMDB.type"; +import Link from "next/link"; +import React from "react"; +import Image from "next/image"; +import tmdb from "@/service/TMDB"; +interface IPeopleCardProps { + people: TrendingPeople; +} +function PeopleCard({ people }: IPeopleCardProps) { + return ( +
+
+ + cast + +
+
+ +

+ {" "} + {people.name ?? people.original_name} +

+ +
+
+ ); +} + +export default PeopleCard; diff --git a/app/components/ShadowImg.tsx b/app/components/ShadowImg.tsx index ed62390..c2b1b3c 100644 --- a/app/components/ShadowImg.tsx +++ b/app/components/ShadowImg.tsx @@ -1,58 +1,58 @@ -import React from "react"; - -function ShadowImg() { - return ( -
-
-
- ); -} - -export default ShadowImg; -// "use client"; -// import React from "react"; -// import Color, { Palette } from "color-thief-react"; -// function ShadowImg() { -// const imgSrc = `https://corsproxy.io/?${encodeURIComponent( -// "https://image.tmdb.org/t/p/original/nLBRD7UPR6GjmWQp6ASAfCTaWKX.jpg" -// )}`; - -// return ( -//
-// - -// -// {({ data, loading }) => { -// return ( -//
-// Predominant color: {data} -//
-// ); -// }} -//
-// -// {({ data, loading }) => { -// return ( -//
-// Palette: -//
    -// {data?.map((color, index) => ( -//
  • -// {color} -//
  • -// ))} -//
-//
-// ); -// }} -//
-//
-// ); -// } - -// export default ShadowImg; +import React from "react"; + +function ShadowImg() { + return ( +
+
+
+ ); +} + +export default ShadowImg; +// "use client"; +// import React from "react"; +// import Color, { Palette } from "color-thief-react"; +// function ShadowImg() { +// const imgSrc = `https://corsproxy.io/?${encodeURIComponent( +// "https://image.tmdb.org/t/p/original/nLBRD7UPR6GjmWQp6ASAfCTaWKX.jpg" +// )}`; + +// return ( +//
+// + +// +// {({ data, loading }) => { +// return ( +//
+// Predominant color: {data} +//
+// ); +// }} +//
+// +// {({ data, loading }) => { +// return ( +//
+// Palette: +//
    +// {data?.map((color, index) => ( +//
  • +// {color} +//
  • +// ))} +//
+//
+// ); +// }} +//
+//
+// ); +// } + +// export default ShadowImg; diff --git a/app/components/Sidebar.tsx b/app/components/Sidebar.tsx index 2313751..8f896f6 100644 --- a/app/components/Sidebar.tsx +++ b/app/components/Sidebar.tsx @@ -1,335 +1,335 @@ -"use client"; -import React from "react"; -import { HomeIcon } from "@heroicons/react/24/outline"; -import Link from "next/link"; -import { motion } from "framer-motion"; -import useStore from "../(store)/store"; -import { usePathname } from "next/navigation"; -function Sidebar() { - const expandedSizeBar = useStore((state) => state.expandedSideBar); - const setExpandedSideBar = useStore((state) => state.setExpandedSideBar); - const pathname = usePathname(); - - return ( - -
- - - - {expandedSizeBar && ( - -

- The Movies -

-
- )} - -
- -
    -
  • - - - {expandedSizeBar && ( - - Trang chủ - - )} - -
  • -
  • - - - - - {expandedSizeBar && ( - - Xu hướng - - )} - -
  • -
  • - - - - - {expandedSizeBar && ( - - Khám phá - - )} - -
  • -
  • - - - - - {expandedSizeBar && ( - - Tìm kiếm - - )} - -
  • -
  • - - - - - - {expandedSizeBar && ( - - Người nổi tiếng - - )} - -
  • -
  • - - - - - {expandedSizeBar && ( - - Cài đặt - - )} - -
  • -
-
- ); -} - -export default Sidebar; +"use client"; +import React from "react"; +import { HomeIcon } from "@heroicons/react/24/outline"; +import Link from "next/link"; +import { motion } from "framer-motion"; +import useStore from "../(store)/store"; +import { usePathname } from "next/navigation"; +function Sidebar() { + const expandedSizeBar = useStore((state) => state.expandedSideBar); + const setExpandedSideBar = useStore((state) => state.setExpandedSideBar); + const pathname = usePathname(); + + return ( + +
+ + + + {expandedSizeBar && ( + +

+ The Movies +

+
+ )} + +
+ +
    +
  • + + + {expandedSizeBar && ( + + Trang chủ + + )} + +
  • +
  • + + + + + {expandedSizeBar && ( + + Xu hướng + + )} + +
  • +
  • + + + + + {expandedSizeBar && ( + + Khám phá + + )} + +
  • +
  • + + + + + {expandedSizeBar && ( + + Tìm kiếm + + )} + +
  • +
  • + + + + + + {expandedSizeBar && ( + + Người nổi tiếng + + )} + +
  • +
  • + + + + + {expandedSizeBar && ( + + Cài đặt + + )} + +
  • +
+
+ ); +} + +export default Sidebar; diff --git a/app/components/Slider.tsx b/app/components/Slider.tsx index 65b7527..41f7c65 100644 --- a/app/components/Slider.tsx +++ b/app/components/Slider.tsx @@ -1,333 +1,333 @@ -"use client"; -import React, { - useState, - useEffect, - Fragment, - useRef, - useLayoutEffect, -} from "react"; -import { Swiper, SwiperClass, SwiperSlide } from "swiper/react"; -import "swiper/css"; -import "swiper/css/effect-creative"; -import "swiper/css/thumbs"; -import "swiper/css/pagination"; -import "swiper/css/navigation"; -import "swiper/css/free-mode"; - -import Image from "next/image"; -import { motion, AnimatePresence } from "framer-motion"; -import SwiperCore, { - Autoplay, - Pagination, - Thumbs, - Controller, - FreeMode, -} from "swiper"; -import Link from "next/link"; -import { Genre, TrendingMovie } from "@/service/TMDB.type"; -import tmdb from "@/service/TMDB"; -import ShadowImg from "./ShadowImg"; -import useWindowDimensions from "@/hooks/useWindowDemensions"; -import { useRouter } from "next/navigation"; -import useStore from "../(store)/store"; - -function Slider({ movie }: { movie: { results: TrendingMovie[] } }) { - const router = useRouter(); - const [thumbsSwiper, setThumbsSwiper] = useState(); - const [firstSwiper, setFirstSwiper] = useState(); - const swiper1Ref = useRef(null); - const swiper2Ref = useRef(); - const [currentIndex, setCurrentIndex] = useState(0); - const expandedSideBar = useStore((state) => state.expandedSideBar); - const [listLogo, setListLogo] = useState([]); - - const [listGenres, setListGenres] = useState([]); - - useEffect(() => { - const getListGenres = async () => { - const data = await tmdb.getListGenres("movie"); - setListGenres(data.genres); - }; - const promises = []; - for (const m of movie.results) { - promises.push(tmdb.getPhotos("movie", m.id)); - } - Promise.all(promises) - .then((data) => { - setListLogo(data); - }) - .catch((error) => { - console.error(error); - }); - getListGenres(); - }, [movie.results]); - const getGenresOfMovie = (m: any) => { - const genres = listGenres.filter((g) => m.genre_ids.includes(g.id)); - return genres; - }; - const { width } = useWindowDimensions(); - useLayoutEffect(() => { - if (swiper1Ref.current !== null) { - swiper1Ref.current.controller.control = swiper2Ref.current; - } - }, []); - return ( - - { - setCurrentIndex(e.realIndex); - }} - autoplay={{ - delay: 10000, - }} - pagination={width < 768 ? true : false} - slidesPerView={width < 768 ? 1.25 : 1} - grabCursor={true} - modules={[Autoplay, Pagination, Thumbs, Controller]} - className="mb-6" - spaceBetween={15} - centeredSlides - loop - > - - {movie.results.slice(0, 10).map((m, index) => ( - - router.push("/movie/" + m.id)} - className="w-full z-10" - initial={{ - scale: 1.05, - }} - animate={{ scale: 1 }} - exit={{ scale: 1.05 }} - transition={{ duration: 1 }} - key={currentIndex} - > - - 640 - ? tmdb.getImage( - m.backdrop_path - ) - : tmdb.getImage( - m.poster_path - ) - : tmdb.getImage(m.backdrop_path) - } - alt="film" - className="md:w-full w-full md:brightness-50 object-cover h-[60vh] lg:h-[95vh] md:rounded-tl-none rounded-xl md:rounded-tr-none" - width={1920} - height={1080} - loading="lazy" - /> - - - -
-
- - - {listLogo?.[index]?.logos[0] - ?.file_path && width > 768 ? ( - img - ) : ( -

- {m.title} -

- )} -
-
-

- TMDB -

- - {m.vote_average.toFixed(1)} - -
- {getGenresOfMovie(m).map( - (x) => ( -
- {x.name} -
- ) - )} -
-
- - - {m.overview} - - - - Xem chi tiết - - -
-
-
- -
-
-
- ))} -
-
- -
- 1700 - ? 1700 / 270 - : expandedSideBar - ? (width - 200) / 270 - : width / 270 - } - > - {movie.results.slice(0, 10).map((m, index) => { - return ( - -
- film - - -

- {m.title} -

-
-
- ); - })} -
-
-
- ); -} - -export default Slider; +"use client"; +import React, { + useState, + useEffect, + Fragment, + useRef, + useLayoutEffect, +} from "react"; +import { Swiper, SwiperClass, SwiperSlide } from "swiper/react"; +import "swiper/css"; +import "swiper/css/effect-creative"; +import "swiper/css/thumbs"; +import "swiper/css/pagination"; +import "swiper/css/navigation"; +import "swiper/css/free-mode"; + +import Image from "next/image"; +import { motion, AnimatePresence } from "framer-motion"; +import SwiperCore, { + Autoplay, + Pagination, + Thumbs, + Controller, + FreeMode, +} from "swiper"; +import Link from "next/link"; +import { Genre, TrendingMovie } from "@/service/TMDB.type"; +import tmdb from "@/service/TMDB"; +import ShadowImg from "./ShadowImg"; +import useWindowDimensions from "@/hooks/useWindowDemensions"; +import { useRouter } from "next/navigation"; +import useStore from "../(store)/store"; + +function Slider({ movie }: { movie: { results: TrendingMovie[] } }) { + const router = useRouter(); + const [thumbsSwiper, setThumbsSwiper] = useState(); + const [firstSwiper, setFirstSwiper] = useState(); + const swiper1Ref = useRef(null); + const swiper2Ref = useRef(); + const [currentIndex, setCurrentIndex] = useState(0); + const expandedSideBar = useStore((state) => state.expandedSideBar); + const [listLogo, setListLogo] = useState([]); + + const [listGenres, setListGenres] = useState([]); + + useEffect(() => { + const getListGenres = async () => { + const data = await tmdb.getListGenres("movie"); + setListGenres(data.genres); + }; + const promises = []; + for (const m of movie.results) { + promises.push(tmdb.getPhotos("movie", m.id)); + } + Promise.all(promises) + .then((data) => { + setListLogo(data); + }) + .catch((error) => { + console.error(error); + }); + getListGenres(); + }, [movie.results]); + const getGenresOfMovie = (m: any) => { + const genres = listGenres.filter((g) => m.genre_ids.includes(g.id)); + return genres; + }; + const { width } = useWindowDimensions(); + useLayoutEffect(() => { + if (swiper1Ref.current !== null) { + swiper1Ref.current.controller.control = swiper2Ref.current; + } + }, []); + return ( + + { + setCurrentIndex(e.realIndex); + }} + autoplay={{ + delay: 10000, + }} + pagination={width < 768 ? true : false} + slidesPerView={width < 768 ? 1.25 : 1} + grabCursor={true} + modules={[Autoplay, Pagination, Thumbs, Controller]} + className="mb-6" + spaceBetween={15} + centeredSlides + loop + > + + {movie.results.slice(0, 10).map((m, index) => ( + + router.push("/movie/" + m.id)} + className="w-full z-10" + initial={{ + scale: 1.05, + }} + animate={{ scale: 1 }} + exit={{ scale: 1.05 }} + transition={{ duration: 1 }} + key={currentIndex} + > + + 640 + ? tmdb.getImage( + m.backdrop_path + ) + : tmdb.getImage( + m.poster_path + ) + : tmdb.getImage(m.backdrop_path) + } + alt="film" + className="md:w-full w-full md:brightness-50 object-cover h-[60vh] lg:h-[95vh] md:rounded-tl-none rounded-xl md:rounded-tr-none" + width={1920} + height={1080} + loading="lazy" + /> + + + +
+
+ + + {listLogo?.[index]?.logos[0] + ?.file_path && width > 768 ? ( + img + ) : ( +

+ {m.title} +

+ )} +
+
+

+ TMDB +

+ + {m.vote_average.toFixed(1)} + +
+ {getGenresOfMovie(m).map( + (x) => ( +
+ {x.name} +
+ ) + )} +
+
+ + + {m.overview} + + + + Xem chi tiết + + +
+
+
+ +
+
+
+ ))} +
+
+ +
+ 1700 + ? 1700 / 270 + : expandedSideBar + ? (width - 200) / 270 + : width / 270 + } + > + {movie.results.slice(0, 10).map((m, index) => { + return ( + +
+ film + + +

+ {m.title} +

+
+
+ ); + })} +
+
+
+ ); +} + +export default Slider; diff --git a/app/components/StickyTab.tsx b/app/components/StickyTab.tsx index 6238dcb..7c4f8cd 100644 --- a/app/components/StickyTab.tsx +++ b/app/components/StickyTab.tsx @@ -1,42 +1,42 @@ -"use client"; -import { motion } from "framer-motion"; -import Link from "next/link"; -import { usePathname } from "next/navigation"; -import React from "react"; - -type Tab = { - tabName: string; - href: string; -}; -interface ITabProps { - tabs: Tab[]; -} -function StickyTab({ tabs }: ITabProps) { - const pathName = usePathname(); - - return ( -
-
- {tabs.map((tab, index) => ( - - {tab.tabName} - {pathName == tab.href && ( - - )} - - ))} -
-
- ); -} - -export default StickyTab; +"use client"; +import { motion } from "framer-motion"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import React from "react"; + +type Tab = { + tabName: string; + href: string; +}; +interface ITabProps { + tabs: Tab[]; +} +function StickyTab({ tabs }: ITabProps) { + const pathName = usePathname(); + + return ( +
+
+ {tabs.map((tab, index) => ( + + {tab.tabName} + {pathName == tab.href && ( + + )} + + ))} +
+
+ ); +} + +export default StickyTab; diff --git a/app/components/WatchTrailerButton.tsx b/app/components/WatchTrailerButton.tsx index 4afd774..b58c95e 100644 --- a/app/components/WatchTrailerButton.tsx +++ b/app/components/WatchTrailerButton.tsx @@ -1,18 +1,18 @@ -"use client"; -import React from "react"; -import useStore from "../(store)/store"; - -function WatchTrailerButton({ videoId }: { videoId: string }) { - const setVideoId = useStore((state) => state.setVideoId); - return ( - - ); -} - -export default WatchTrailerButton; +"use client"; +import React from "react"; +import useStore from "../(store)/store"; + +function WatchTrailerButton({ videoId }: { videoId: string }) { + const setVideoId = useStore((state) => state.setVideoId); + return ( + + ); +} + +export default WatchTrailerButton; diff --git a/app/components/mobile/MobileTab.tsx b/app/components/mobile/MobileTab.tsx index 8730bba..fbc4bf5 100644 --- a/app/components/mobile/MobileTab.tsx +++ b/app/components/mobile/MobileTab.tsx @@ -1,155 +1,155 @@ -"use client"; -import React, { useState, useEffect } from "react"; -import Link from "next/link"; -import { HomeIcon } from "@heroicons/react/24/outline"; -function MobileTab() { - const [isScroll, setIsScroll] = useState(false); - useEffect(() => { - const onScrollTop = () => { - if (window.scrollY > 90) { - setIsScroll(true); - } else { - setIsScroll(false); - } - }; - window.addEventListener("scroll", onScrollTop); - return () => window.removeEventListener("scroll", onScrollTop); - }, []); - return ( -
-
- - -

Trang chủ

- -
-
- - - - -

Trending

- -
-
- -
    -
  • -
    - - - - -

    Cài đặt

    - -
    -
  • -
  • -
    - - - - -

    Tìm kiếm

    - -
    -
  • -
  • -
    - - - - - Khám phá - -
    -
  • -
-
-
- ); -} - -export default MobileTab; +"use client"; +import React, { useState, useEffect } from "react"; +import Link from "next/link"; +import { HomeIcon } from "@heroicons/react/24/outline"; +function MobileTab() { + const [isScroll, setIsScroll] = useState(false); + useEffect(() => { + const onScrollTop = () => { + if (window.scrollY > 90) { + setIsScroll(true); + } else { + setIsScroll(false); + } + }; + window.addEventListener("scroll", onScrollTop); + return () => window.removeEventListener("scroll", onScrollTop); + }, []); + return ( +
+
+ + +

Trang chủ

+ +
+
+ + + + +

Trending

+ +
+
+ +
    +
  • +
    + + + + +

    Cài đặt

    + +
    +
  • +
  • +
    + + + + +

    Tìm kiếm

    + +
    +
  • +
  • +
    + + + + + Khám phá + +
    +
  • +
+
+
+ ); +} + +export default MobileTab; diff --git a/app/components/search/Search.tsx b/app/components/search/Search.tsx index 67eb4ce..1c3c3e4 100644 --- a/app/components/search/Search.tsx +++ b/app/components/search/Search.tsx @@ -1,26 +1,26 @@ -"use client"; -import React, { useState } from "react"; -import Link from "next/link"; -import { usePathname } from "next/navigation"; -function Search({}) { - const pathName = usePathname(); - const [searchValue, setSearchValue] = useState(""); - return ( -
- setSearchValue(e.target.value)} - /> - - Tìm kiếm - -
- ); -} - -export default Search; +"use client"; +import React, { useState } from "react"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +function Search({}) { + const pathName = usePathname(); + const [searchValue, setSearchValue] = useState(""); + return ( +
+ setSearchValue(e.target.value)} + /> + + Tìm kiếm + +
+ ); +} + +export default Search; diff --git a/app/discover/movies/page.tsx b/app/discover/movies/page.tsx index 4db44a0..226778d 100644 --- a/app/discover/movies/page.tsx +++ b/app/discover/movies/page.tsx @@ -1,39 +1,39 @@ -import ChangeMediaListBtn from "@/app/components/MediaList/ChangeMediaListBtn"; -import MediaList from "@/app/components/MediaList/MediaList"; -import Pagination from "@/app/components/Pagination"; -import tmdb from "@/service/TMDB"; -import { Movie } from "@/service/TMDB.type"; -import React from "react"; -interface PageProp { - searchParams: { - with_genres: string; - page: number; - }; -} -async function page({ searchParams }: PageProp) { - const listMovie: { results: Movie[]; total_pages: number } = - await tmdb.discover( - "movie", - +searchParams.with_genres, - +searchParams.page ?? 1 - ); - - return ( -
-
- -
-
- -
-
- -
-
- ); -} - -export default page; +import ChangeMediaListBtn from "@/app/components/MediaList/ChangeMediaListBtn"; +import MediaList from "@/app/components/MediaList/MediaList"; +import Pagination from "@/app/components/Pagination"; +import tmdb from "@/service/TMDB"; +import { Movie } from "@/service/TMDB.type"; +import React from "react"; +interface PageProp { + searchParams: { + with_genres: string; + page: number; + }; +} +async function page({ searchParams }: PageProp) { + const listMovie: { results: Movie[]; total_pages: number } = + await tmdb.discover( + "movie", + +searchParams.with_genres, + +searchParams.page ?? 1 + ); + + return ( +
+
+ +
+
+ +
+
+ +
+
+ ); +} + +export default page; diff --git a/app/discover/page.tsx b/app/discover/page.tsx index 480a06e..68d953d 100644 --- a/app/discover/page.tsx +++ b/app/discover/page.tsx @@ -1,120 +1,120 @@ -import React from "react"; -import Link from "next/link"; -function page() { - return ( -
-

Khám phá

- - - - - Tìm kiếm - -
-

Danh mục

-
- - - - -
-
-
-

Tổng quan

-
- - Thể loại phim lẻ - - -
-
-
- ); -} - -export default page; +import React from "react"; +import Link from "next/link"; +function page() { + return ( +
+

Khám phá

+ + + + + Tìm kiếm + +
+

Danh mục

+
+ + + + +
+
+
+

Tổng quan

+
+ + Thể loại phim lẻ + + +
+
+
+ ); +} + +export default page; diff --git a/app/genres/layout.tsx b/app/genres/layout.tsx index ab19706..cf98429 100644 --- a/app/genres/layout.tsx +++ b/app/genres/layout.tsx @@ -1,22 +1,22 @@ -import React from "react"; -import StickyTab from "../components/StickyTab"; -function layout({ children }: { children: React.ReactNode }) { - const tabs = [ - { - tabName: "Thể loại phim lẻ", - href: "/genres/movie", - }, - { - tabName: "Thể loại phim bộ", - href: "/genres/tv-show", - }, - ]; - return ( -
- -
{children}
-
- ); -} - -export default layout; +import React from "react"; +import StickyTab from "../components/StickyTab"; +function layout({ children }: { children: React.ReactNode }) { + const tabs = [ + { + tabName: "Thể loại phim lẻ", + href: "/genres/movie", + }, + { + tabName: "Thể loại phim bộ", + href: "/genres/tv-show", + }, + ]; + return ( +
+ +
{children}
+
+ ); +} + +export default layout; diff --git a/app/genres/movie/page.tsx b/app/genres/movie/page.tsx index 823dfd2..f42d9c8 100644 --- a/app/genres/movie/page.tsx +++ b/app/genres/movie/page.tsx @@ -1,27 +1,27 @@ -import tmdb from "@/service/TMDB"; -import { Genre } from "@/service/TMDB.type"; -import Link from "next/link"; -import React from "react"; - -async function page() { - const listGenres: { genres: Genre[] } = await tmdb.getListGenres("movie"); - - return ( -
-

Thể loại phim lẻ

-
- {listGenres.genres.map((genres) => ( - - {genres.name} - - ))} -
-
- ); -} - -export default page; +import tmdb from "@/service/TMDB"; +import { Genre } from "@/service/TMDB.type"; +import Link from "next/link"; +import React from "react"; + +async function page() { + const listGenres: { genres: Genre[] } = await tmdb.getListGenres("movie"); + + return ( +
+

Thể loại phim lẻ

+
+ {listGenres.genres.map((genres) => ( + + {genres.name} + + ))} +
+
+ ); +} + +export default page; diff --git a/app/genres/tv-show/page.tsx b/app/genres/tv-show/page.tsx index 5eb93b8..a5c851c 100644 --- a/app/genres/tv-show/page.tsx +++ b/app/genres/tv-show/page.tsx @@ -1,7 +1,7 @@ -import React from "react"; - -function page() { - return
page
; -} - -export default page; +import React from "react"; + +function page() { + return
page
; +} + +export default page; diff --git a/app/globals.css b/app/globals.css index 6e51ace..e1cd7f8 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,37 +1,37 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer components { - .text-gradient { - @apply [&::selection]:text-base-content relative col-start-1 row-start-1 bg-[linear-gradient(90deg,hsl(var(--s))_0%,hsl(var(--sf))_9%,hsl(var(--pf))_42%,hsl(var(--p))_47%,hsl(var(--a))_100%)] bg-clip-text [-webkit-text-fill-color:transparent] [&::selection]:bg-blue-700/20 [@supports(color:oklch(0_0_0))]:bg-[linear-gradient(90deg,hsl(var(--s))_4%,color-mix(in_oklch,hsl(var(--sf)),hsl(var(--pf)))_22%,hsl(var(--p))_45%,color-mix(in_oklch,hsl(var(--p)),hsl(var(--a)))_67%,hsl(var(--a))_100.2%)]; - } -} - -.img-shadow::after { - position: fixed; - content: ""; - background-image: linear-gradient( - 180deg, - rgba(24, 133, 69, 0) 20.77%, - hsl(var(--b3, var(--b2)) / var(--tw-bg-opacity)) 100% - ); - top: 0; - left: 80px; - bottom: 0; - right: 0; -} -.img-shadow2::after { - --tw-bg-opacity: 1; - position: absolute; - content: ""; - background-image: linear-gradient( - 180deg, - rgba(24, 133, 69, 0) 30.77%, - hsl(var(--b1) / var(--tw-bg-opacity)) 100% - ); - top: 0; - left: 0; - bottom: 0; - right: 0; -} +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer components { + .text-gradient { + @apply [&::selection]:text-base-content relative col-start-1 row-start-1 bg-[linear-gradient(90deg,hsl(var(--s))_0%,hsl(var(--sf))_9%,hsl(var(--pf))_42%,hsl(var(--p))_47%,hsl(var(--a))_100%)] bg-clip-text [-webkit-text-fill-color:transparent] [&::selection]:bg-blue-700/20 [@supports(color:oklch(0_0_0))]:bg-[linear-gradient(90deg,hsl(var(--s))_4%,color-mix(in_oklch,hsl(var(--sf)),hsl(var(--pf)))_22%,hsl(var(--p))_45%,color-mix(in_oklch,hsl(var(--p)),hsl(var(--a)))_67%,hsl(var(--a))_100.2%)]; + } +} + +.img-shadow::after { + position: fixed; + content: ""; + background-image: linear-gradient( + 180deg, + rgba(24, 133, 69, 0) 20.77%, + hsl(var(--b3, var(--b2)) / var(--tw-bg-opacity)) 100% + ); + top: 0; + left: 80px; + bottom: 0; + right: 0; +} +.img-shadow2::after { + --tw-bg-opacity: 1; + position: absolute; + content: ""; + background-image: linear-gradient( + 180deg, + rgba(24, 133, 69, 0) 30.77%, + hsl(var(--b1) / var(--tw-bg-opacity)) 100% + ); + top: 0; + left: 0; + bottom: 0; + right: 0; +} diff --git a/app/loading.tsx b/app/loading.tsx index dbe3baa..3fd9f46 100644 --- a/app/loading.tsx +++ b/app/loading.tsx @@ -1,20 +1,20 @@ -import React from "react"; -import Image from "next/image"; -function Loading() { - return ( -
- logo - -
- ); -} - -export default Loading; +import React from "react"; +import Image from "next/image"; +function Loading() { + return ( +
+ logo + +
+ ); +} + +export default Loading; diff --git a/app/movie/(discover)/layout.tsx b/app/movie/(discover)/layout.tsx index e5cca75..31f04e1 100644 --- a/app/movie/(discover)/layout.tsx +++ b/app/movie/(discover)/layout.tsx @@ -1,7 +1,7 @@ -import React from "react"; - -function layout() { - return
layout
; -} - -export default layout; +import React from "react"; + +function layout() { + return
layout
; +} + +export default layout; diff --git a/app/movie/(discover)/popular/page.tsx b/app/movie/(discover)/popular/page.tsx index 5eb93b8..a5c851c 100644 --- a/app/movie/(discover)/popular/page.tsx +++ b/app/movie/(discover)/popular/page.tsx @@ -1,7 +1,7 @@ -import React from "react"; - -function page() { - return
page
; -} - -export default page; +import React from "react"; + +function page() { + return
page
; +} + +export default page; diff --git a/app/movie/[movieId]/cast/page.tsx b/app/movie/[movieId]/cast/page.tsx index 541a208..2769b98 100644 --- a/app/movie/[movieId]/cast/page.tsx +++ b/app/movie/[movieId]/cast/page.tsx @@ -1,24 +1,24 @@ -import React from "react"; -import { Cast } from "../page"; - -import PeopleCard from "@/app/components/PeopleCard"; -async function page({ params }: { params: any }) { - const getCast = async () => { - const respone = await fetch( - `https://api.themoviedb.org/3/movie/${params.movieId}/credits?api_key=${process.env.TMDB}&language=vi-VN` - ); - const data = await respone.json(); - return data; - }; - const cast: { cast: Cast[] } = await getCast(); - - return ( -
- {cast.cast.map((c) => ( - - ))} -
- ); -} - -export default page; +import React from "react"; +import { Cast } from "../page"; + +import PeopleCard from "@/app/components/PeopleCard"; +async function page({ params }: { params: any }) { + const getCast = async () => { + const respone = await fetch( + `https://api.themoviedb.org/3/movie/${params.movieId}/credits?api_key=${process.env.TMDB}&language=vi-VN` + ); + const data = await respone.json(); + return data; + }; + const cast: { cast: Cast[] } = await getCast(); + + return ( +
+ {cast.cast.map((c) => ( + + ))} +
+ ); +} + +export default page; diff --git a/app/movie/[movieId]/error.tsx b/app/movie/[movieId]/error.tsx index 6d7587b..e9b609d 100644 --- a/app/movie/[movieId]/error.tsx +++ b/app/movie/[movieId]/error.tsx @@ -1,12 +1,12 @@ -"use client"; -import React from "react"; - -function error() { - return ( -
- error rồi thằng ngu -
- ); -} - -export default error; +"use client"; +import React from "react"; + +function error() { + return ( +
+ error rồi thằng ngu +
+ ); +} + +export default error; diff --git a/app/movie/[movieId]/layout.tsx b/app/movie/[movieId]/layout.tsx index 513cbd7..4991988 100644 --- a/app/movie/[movieId]/layout.tsx +++ b/app/movie/[movieId]/layout.tsx @@ -1,140 +1,140 @@ -import { Movie } from "@/service/TMDB.type"; -import React from "react"; -import Image from "next/image"; -import MovieDetailTab from "@/app/components/MovieDetailTab"; -import WatchTrailerButton from "@/app/components/WatchTrailerButton"; -import Modal from "@/app/components/Modal"; -import tmdb from "@/service/TMDB"; -import ShadowImg from "@/app/components/ShadowImg"; -import Link from "next/link"; -import { Metadata } from "next"; -interface MovieDetailProp { - params: { - movieId: string; - }; - children: React.ReactNode; -} - -export async function generateMetadata({ - params, -}: { - params: { movieId: string }; -}): Promise { - const movie: Movie = await tmdb.getMovieOrTV(+params.movieId, "movie"); - return { - title: movie.title, - }; -} - -async function Layout({ params, children }: MovieDetailProp) { - const movie: Movie = await tmdb.getMovieOrTV(+params.movieId, "movie"); - function convertToHourMinute(minutes: number) { - const hours = Math.floor(minutes / 60); - const remainingMinutes = minutes % 60; - const formattedTime = `${hours}h ${remainingMinutes}m`; - return formattedTime; - } - return ( -
-
- - {movie.title} - -
-
-
-
- {movie.title} -
-
-
-

- {movie.title} -

-

- {movie?.tagline} -

-
- - -
-
- {movie.genres.map((genres) => { - return ( - - {genres.name} - - ); - })} -
-
- -
-
-
-
- -
-
-
-
- - -
-
- {movie.genres.map((genres) => { - return ( -
- {genres.name} -
- ); - })} -
-
- -
-
- - {children} -
- {/* */} -
- ); -} - -export default Layout; +import { Movie } from "@/service/TMDB.type"; +import React from "react"; +import Image from "next/image"; +import MovieDetailTab from "@/app/components/MovieDetailTab"; +import WatchTrailerButton from "@/app/components/WatchTrailerButton"; +import Modal from "@/app/components/Modal"; +import tmdb from "@/service/TMDB"; +import ShadowImg from "@/app/components/ShadowImg"; +import Link from "next/link"; +import { Metadata } from "next"; +interface MovieDetailProp { + params: { + movieId: string; + }; + children: React.ReactNode; +} + +export async function generateMetadata({ + params, +}: { + params: { movieId: string }; +}): Promise { + const movie: Movie = await tmdb.getMovieOrTV(+params.movieId, "movie"); + return { + title: movie.title, + }; +} + +async function Layout({ params, children }: MovieDetailProp) { + const movie: Movie = await tmdb.getMovieOrTV(+params.movieId, "movie"); + function convertToHourMinute(minutes: number) { + const hours = Math.floor(minutes / 60); + const remainingMinutes = minutes % 60; + const formattedTime = `${hours}h ${remainingMinutes}m`; + return formattedTime; + } + return ( +
+
+ + {movie.title} + +
+
+
+
+ {movie.title} +
+
+
+

+ {movie.title} +

+

+ {movie?.tagline} +

+
+ + +
+
+ {movie.genres.map((genres) => { + return ( + + {genres.name} + + ); + })} +
+
+ +
+
+
+
+ +
+
+
+
+ + +
+
+ {movie.genres.map((genres) => { + return ( +
+ {genres.name} +
+ ); + })} +
+
+ +
+
+ + {children} +
+ {/* */} +
+ ); +} + +export default Layout; diff --git a/app/movie/[movieId]/loading.tsx b/app/movie/[movieId]/loading.tsx index c8462da..0cfff64 100644 --- a/app/movie/[movieId]/loading.tsx +++ b/app/movie/[movieId]/loading.tsx @@ -1,20 +1,20 @@ -import React from "react"; -import Image from "next/image"; -function Loading() { - return ( -
- logo - -
- ); -} - -export default Loading; +import React from "react"; +import Image from "next/image"; +function Loading() { + return ( +
+ logo + +
+ ); +} + +export default Loading; diff --git a/app/movie/[movieId]/page.tsx b/app/movie/[movieId]/page.tsx index 0ba864e..5732486 100644 --- a/app/movie/[movieId]/page.tsx +++ b/app/movie/[movieId]/page.tsx @@ -1,147 +1,151 @@ -import React from "react"; -import { Movie } from "@/service/TMDB.type"; -import Link from "next/link"; -import tmdb from "@/service/TMDB"; -import { Metadata } from "next"; - -import ListMovie from "@/app/components/CardSlide"; -import PeopleCard from "@/app/components/PeopleCard"; -export interface Cast { - id: number; - profile_path: string; - name: string; -} -export async function generateMetadata({ - params, -}: { - params: { movieId: string }; -}): Promise { - const movie: Movie = await tmdb.getMovieOrTV(+params.movieId, "movie"); - return { - title: movie.title, - }; -} -async function Page({ - params, -}: { - params: { - movieId: number; - }; -}) { - const movie: Movie = await tmdb.getMovieOrTV(params.movieId, "movie"); - const { cast }: { cast: Cast[] } = await tmdb.getCast( - params.movieId, - "movie" - ); - const listRecommendations: { results: Movie[] } = - await tmdb.getRecomendations(params.movieId, "movie"); - const slicedRecommendations = { - results: listRecommendations.results.slice(0, 9), - }; - return ( -
-
-
-
-

Original Title

-

- {movie.original_title} -

-
-
-

Status

-

- {movie.status} -

-
-
-

Production Companies

-
- {movie.production_companies.map((company) => ( -

- {company.name} -

- ))} -
-
-
-

Budget

-

- {movie.budget.toLocaleString("en-US", { - style: "currency", - currency: "USD", - })} -

-
-
-

Revenue

-

- {movie.revenue.toLocaleString("en-US", { - style: "currency", - currency: "USD", - })} -

-
-
-
-
-
-

{movie.overview}

-
-
-

Production Countries

- {movie.production_countries.map((country) => ( -

{country.name}

- ))} -
-
- Spoken Languages - {movie.spoken_languages.map((l) => ( -

{l.name}

- ))} -
-
-
-
-

- Top Diễn Viên -

- - Xem Thêm - -
- {cast?.slice(0, 5).map((cast) => ( -
- -
- ))} -
-
-
-
-

Phim Đề Xuất

-
- {!!slicedRecommendations.results.length && ( - - Xem Thêm - - )} -
- -
-
-
-
- ); -} - -export default Page; +import React from "react"; +import { Movie } from "@/service/TMDB.type"; +import Link from "next/link"; +import tmdb from "@/service/TMDB"; +import { Metadata } from "next"; + +import ListMovie from "@/app/components/CardSlide"; +import PeopleCard from "@/app/components/PeopleCard"; +export interface Cast { + id: number; + profile_path: string; + name: string; +} +export async function generateMetadata({ + params, +}: { + params: { movieId: string }; +}): Promise { + const movie: Movie = await tmdb.getMovieOrTV(+params.movieId, "movie"); + return { + title: movie.title, + }; +} +async function Page({ + params, +}: { + params: { + movieId: number; + }; +}) { + const movie: Movie = await tmdb.getMovieOrTV(params.movieId, "movie"); + const { cast }: { cast: Cast[] } = await tmdb.getCast( + params.movieId, + "movie" + ); + const listRecommendations: { results: Movie[] } = + await tmdb.getRecomendations(params.movieId, "movie"); + const slicedRecommendations = { + results: listRecommendations.results.slice(0, 9), + }; + return ( +
+
+
+
+

Original Title

+

+ {movie.original_title} +

+
+
+

Status

+

+ {movie.status} +

+
+
+

Production Companies

+
+ {movie.production_companies.map((company) => ( +

+ {company.name} +

+ ))} +
+
+
+

Budget

+

+ {movie.budget.toLocaleString("en-US", { + style: "currency", + currency: "USD", + })} +

+
+
+

Revenue

+

+ {movie.revenue.toLocaleString("en-US", { + style: "currency", + currency: "USD", + })} +

+
+
+
+
+
+

{movie.overview}

+
+
+

Production Countries

+ {movie.production_countries.map((country) => ( +

{country.name}

+ ))} +
+
+ Spoken Languages + {movie.spoken_languages.map((l) => ( +

{l.name}

+ ))} +
+
+
+
+

+ Top Diễn Viên +

+ + Xem Thêm + +
+ {cast?.slice(0, 5).map((cast) => ( +
+ +
+ ))} +
+
+
+
+

Phim Đề Xuất

+
+ {!!slicedRecommendations.results.length && ( + + Xem Thêm + + )} +
+ +
+
+
+
+ ); +} + +export default Page; diff --git a/app/movie/[movieId]/photos/page.tsx b/app/movie/[movieId]/photos/page.tsx index 26fe9a3..608d317 100644 --- a/app/movie/[movieId]/photos/page.tsx +++ b/app/movie/[movieId]/photos/page.tsx @@ -1,86 +1,86 @@ -"use client"; -import tmdb from "@/service/TMDB"; -import { ListPhotos } from "@/service/TMDB.type"; -import React from "react"; -import Image from "next/image"; -import "react-photo-view/dist/react-photo-view.css"; -import { PhotoProvider, PhotoView } from "react-photo-view"; -import useSwr from "swr"; -async function Page({ params }: { params: { movieId: number } }) { - const { data: listPhoto } = useSwr( - `/photos/${params.movieId}`, - () => tmdb.getPhotos("movie", params.movieId), - { - revalidateOnMount: true, - } - ); - return ( - 800} - easing={(type) => - type === 2 - ? "cubic-bezier(0.36, 0, 0.66, -0.56)" - : "cubic-bezier(0.34, 1.56, 0.64, 1)" - } - > -
-

Backdrops

-
- {listPhoto?.backdrops.slice(0, 8).map((backdrop, index) => ( - - img - - ))} -
-

Logos

-
- {listPhoto?.logos.slice(0, 4).map((backdrop, index) => ( - - img - - ))} -
-

Posters

-
- {listPhoto?.posters.slice(0, 4).map((backdrop, index) => ( - - img - - ))} -
-
-
- ); -} - -export default Page; +"use client"; +import tmdb from "@/service/TMDB"; +import { ListPhotos } from "@/service/TMDB.type"; +import React from "react"; +import Image from "next/image"; +import "react-photo-view/dist/react-photo-view.css"; +import { PhotoProvider, PhotoView } from "react-photo-view"; +import useSwr from "swr"; +async function Page({ params }: { params: { movieId: number } }) { + const { data: listPhoto } = useSwr( + `/photos/${params.movieId}`, + () => tmdb.getPhotos("movie", params.movieId), + { + revalidateOnMount: true, + } + ); + return ( + 800} + easing={(type) => + type === 2 + ? "cubic-bezier(0.36, 0, 0.66, -0.56)" + : "cubic-bezier(0.34, 1.56, 0.64, 1)" + } + > +
+

Backdrops

+
+ {listPhoto?.backdrops.slice(0, 8).map((backdrop, index) => ( + + img + + ))} +
+

Logos

+
+ {listPhoto?.logos.slice(0, 4).map((backdrop, index) => ( + + img + + ))} +
+

Posters

+
+ {listPhoto?.posters.slice(0, 4).map((backdrop, index) => ( + + img + + ))} +
+
+
+ ); +} + +export default Page; diff --git a/app/movie/[movieId]/recommendations/page.tsx b/app/movie/[movieId]/recommendations/page.tsx index e93fbed..132ecd4 100644 --- a/app/movie/[movieId]/recommendations/page.tsx +++ b/app/movie/[movieId]/recommendations/page.tsx @@ -1,21 +1,21 @@ -import React from "react"; -import { Movie } from "@/service/TMDB.type"; -import tmdb from "@/service/TMDB"; -import MediaList from "@/app/components/MediaList/MediaList"; -import ChangeMediaListBtn from "@/app/components/MediaList/ChangeMediaListBtn"; -async function page({ params }: { params: any }) { - const listRecommendations: { results: Movie[] } = - await tmdb.getRecomendations(params.movieId, "movie"); - return ( -
-
- -
-
- -
-
- ); -} - -export default page; +import React from "react"; +import { Movie } from "@/service/TMDB.type"; +import tmdb from "@/service/TMDB"; +import MediaList from "@/app/components/MediaList/MediaList"; +import ChangeMediaListBtn from "@/app/components/MediaList/ChangeMediaListBtn"; +async function page({ params }: { params: any }) { + const listRecommendations: { results: Movie[] } = + await tmdb.getRecomendations(params.movieId, "movie"); + return ( +
+
+ +
+
+ +
+
+ ); +} + +export default page; diff --git a/app/movie/[movieId]/videos/page.tsx b/app/movie/[movieId]/videos/page.tsx index 0ad3b0c..2d3c585 100644 --- a/app/movie/[movieId]/videos/page.tsx +++ b/app/movie/[movieId]/videos/page.tsx @@ -1,90 +1,90 @@ -"use client"; -import React, { useState } from "react"; -import useSwr from "swr"; -import { ListPhotos, Videos, videoTypes } from "@/service/TMDB.type"; -import tmdb from "@/service/TMDB"; -import Image from "next/image"; -interface IPageProps { - params: { - movieId: number; - }; -} -const menu = [ - { - name: videoTypes.Clip, - }, - { - name: videoTypes.Trailer, - }, - { - name: videoTypes.Teaser, - }, - { - name: videoTypes.Featurette, - }, - { - name: videoTypes.BehindTheScenes, - }, -]; -function Page({ params }: IPageProps) { - const [type, setType] = useState(videoTypes.Clip); - const { data: listVideos } = useSwr<{ results: Videos[] }>( - `/videos/${params.movieId}`, - () => tmdb.getVideos("movie", params.movieId) - ); - const { data: listPhoto } = useSwr( - `/photos/${params.movieId}`, - () => tmdb.getPhotos("movie", params.movieId), - { - revalidateOnMount: true, - } - ); - return ( -
-
- -
-
- {listVideos?.results - .filter((v) => v.type == type) - .map((v, index) => ( -
-
- img -
- -
-

{v.name}

-
-
- ))} -
-
- ); -} - -export default Page; +"use client"; +import React, { useState } from "react"; +import useSwr from "swr"; +import { ListPhotos, Videos, videoTypes } from "@/service/TMDB.type"; +import tmdb from "@/service/TMDB"; +import Image from "next/image"; +interface IPageProps { + params: { + movieId: number; + }; +} +const menu = [ + { + name: videoTypes.Clip, + }, + { + name: videoTypes.Trailer, + }, + { + name: videoTypes.Teaser, + }, + { + name: videoTypes.Featurette, + }, + { + name: videoTypes.BehindTheScenes, + }, +]; +function Page({ params }: IPageProps) { + const [type, setType] = useState(videoTypes.Clip); + const { data: listVideos } = useSwr<{ results: Videos[] }>( + `/videos/${params.movieId}`, + () => tmdb.getVideos("movie", params.movieId) + ); + const { data: listPhoto } = useSwr( + `/photos/${params.movieId}`, + () => tmdb.getPhotos("movie", params.movieId), + { + revalidateOnMount: true, + } + ); + return ( +
+
+ +
+
+ {listVideos?.results + .filter((v) => v.type == type) + .map((v, index) => ( +
+
+ img +
+ +
+

{v.name}

+
+
+ ))} +
+
+ ); +} + +export default Page; diff --git a/app/page.tsx b/app/page.tsx index 906ca7a..02466ec 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,69 +1,69 @@ -import Link from "next/link"; -import Slider from "./components/Slider"; -import tmdb from "@/service/TMDB"; -import CardSlide from "./components/CardSlide"; - -export default async function Home() { - const [trendingList, popularList, topRateList, popularPeople] = - await Promise.all([ - tmdb.getTrending("movie", "day"), - tmdb.getPopular("movie"), - tmdb.getTopRate("movie"), - tmdb.getTrending("person", "day"), - ]); - - return ( -
-
- -
- -
-
-

- Popular Movies -

- - View more - - -
- -
-
- -
-

- Top Rate -

- - View more - -
- -
-
-
-

- Popular People -

- - View more - -
- -
-
-
-
- ); -} +import Link from "next/link"; +import Slider from "./components/Slider"; +import tmdb from "@/service/TMDB"; +import CardSlide from "./components/CardSlide"; + +export default async function Home() { + const [trendingList, popularList, topRateList, popularPeople] = + await Promise.all([ + tmdb.getTrending("movie", "day"), + tmdb.getPopular("movie"), + tmdb.getTopRate("movie"), + tmdb.getTrending("person", "day"), + ]); + + return ( +
+
+ +
+ +
+
+

+ Popular Movies +

+ + View more + + +
+ +
+
+ +
+

+ Top Rate +

+ + View more + +
+ +
+
+
+

+ Popular People +

+ + View more + +
+ +
+
+
+
+ ); +} diff --git a/app/people/[peopleId]/credits/page.tsx b/app/people/[peopleId]/credits/page.tsx index 89eef0b..7b85df9 100644 --- a/app/people/[peopleId]/credits/page.tsx +++ b/app/people/[peopleId]/credits/page.tsx @@ -1,25 +1,24 @@ -import ChangeMediaListBtn from "@/app/components/MediaList/ChangeMediaListBtn"; -import { MovieCard2 } from "@/app/components/MovieCard"; -import tmdb from "@/service/TMDB"; -import React from "react"; -interface IPageProps { - params: { - peopleId: number; - }; -} -async function page({ params }: IPageProps) { - const listOfMoview = await tmdb.getMovieCredits(params.peopleId); - return ( -
-

- Movie Credits {listOfMoview.cast.length} -

- - {listOfMoview.cast.map((m, index) => ( - - ))} -
- ); -} - -export default page; +import { MovieCard2 } from "@/app/components/MovieCard"; +import tmdb from "@/service/TMDB"; +import React from "react"; +interface IPageProps { + params: { + peopleId: number; + }; +} +async function page({ params }: IPageProps) { + const listOfMoview = await tmdb.getMovieCredits(params.peopleId); + return ( +
+

+ Movie Credits {listOfMoview.cast.length} +

+ + {listOfMoview.cast.map((m, index) => ( + + ))} +
+ ); +} + +export default page; diff --git a/app/people/[peopleId]/layout.tsx b/app/people/[peopleId]/layout.tsx index 85aa280..6836f26 100644 --- a/app/people/[peopleId]/layout.tsx +++ b/app/people/[peopleId]/layout.tsx @@ -1,77 +1,77 @@ -import StickyTab from "@/app/components/StickyTab"; -import tmdb from "@/service/TMDB"; -import React from "react"; -import Image from "next/image"; -interface IPageProps { - params: { - peopleId: number; - }; - children: React.ReactNode; -} -async function layout({ params, children }: IPageProps) { - const tabs = [ - { - tabName: "Overview", - href: `/people/${params.peopleId}`, - }, - { - tabName: "Credit", - href: `/people/${params.peopleId}/credits`, - }, - { - tabName: "Media", - href: `/people/${params.peopleId}/media`, - }, - ]; - const people = await tmdb.getPeople(params.peopleId); - return ( -
- -
-
- people avatar -

- {people.name} -

-
-

Personal Info

-
-

Know for

-

{people.known_for_department}

-
-
-

Gender

-

{people.gender === 1 ? "Female" : "Male"}

-
-
-

Birthday

-

{people.birthday}

-
-
-

Place of Birth

-

{people.place_of_birth}

-
-
-

Also know as

-
- {people.also_known_as.map((x) => ( -

{x}

- ))} -
-
-
-
-
{children}
-
-
- ); -} - -export default layout; +import StickyTab from "@/app/components/StickyTab"; +import tmdb from "@/service/TMDB"; +import React from "react"; +import Image from "next/image"; +interface IPageProps { + params: { + peopleId: number; + }; + children: React.ReactNode; +} +async function layout({ params, children }: IPageProps) { + const tabs = [ + { + tabName: "Overview", + href: `/people/${params.peopleId}`, + }, + { + tabName: "Credit", + href: `/people/${params.peopleId}/credits`, + }, + { + tabName: "Media", + href: `/people/${params.peopleId}/media`, + }, + ]; + const people = await tmdb.getPeople(params.peopleId); + return ( +
+ +
+
+ people avatar +

+ {people.name} +

+
+

Personal Info

+
+

Know for

+

{people.known_for_department}

+
+
+

Gender

+

{people.gender === 1 ? "Female" : "Male"}

+
+
+

Birthday

+

{people.birthday}

+
+
+

Place of Birth

+

{people.place_of_birth}

+
+
+

Also know as

+
+ {people.also_known_as.map((x) => ( +

{x}

+ ))} +
+
+
+
+
{children}
+
+
+ ); +} + +export default layout; diff --git a/app/people/[peopleId]/media/page.tsx b/app/people/[peopleId]/media/page.tsx index 4fa1045..0508ba1 100644 --- a/app/people/[peopleId]/media/page.tsx +++ b/app/people/[peopleId]/media/page.tsx @@ -1,50 +1,50 @@ -"use client"; -import tmdb from "@/service/TMDB"; -import React from "react"; -import useSwr from "swr"; -import { PhotoProvider, PhotoView } from "react-photo-view"; -import Image from "next/image"; -import "react-photo-view/dist/react-photo-view.css"; -interface IPageProps { - params: { - peopleId: number; - }; -} -function Page({ params }: IPageProps) { - const { data: listPhoto } = useSwr(`/photos2/${params.peopleId}`, () => - tmdb.getPeoplePhotos(params.peopleId) - ); - return ( - 800} - easing={(type) => - type === 2 - ? "cubic-bezier(0.36, 0, 0.66, -0.56)" - : "cubic-bezier(0.34, 1.56, 0.64, 1)" - } - > -
-

Profile

-
- {listPhoto?.profiles.map((profile, index) => ( - - img_people - - ))} -
-
-
- ); -} - -export default Page; +"use client"; +import tmdb from "@/service/TMDB"; +import React from "react"; +import useSwr from "swr"; +import { PhotoProvider, PhotoView } from "react-photo-view"; +import Image from "next/image"; +import "react-photo-view/dist/react-photo-view.css"; +interface IPageProps { + params: { + peopleId: number; + }; +} +function Page({ params }: IPageProps) { + const { data: listPhoto } = useSwr(`/photos2/${params.peopleId}`, () => + tmdb.getPeoplePhotos(params.peopleId) + ); + return ( + 800} + easing={(type) => + type === 2 + ? "cubic-bezier(0.36, 0, 0.66, -0.56)" + : "cubic-bezier(0.34, 1.56, 0.64, 1)" + } + > +
+

Profile

+
+ {listPhoto?.profiles.map((profile, index) => ( + + img_people + + ))} +
+
+
+ ); +} + +export default Page; diff --git a/app/people/[peopleId]/page.tsx b/app/people/[peopleId]/page.tsx index d34a2ed..e12aabb 100644 --- a/app/people/[peopleId]/page.tsx +++ b/app/people/[peopleId]/page.tsx @@ -1,37 +1,37 @@ -import MovieCard from "@/app/components/MovieCard"; -import tmdb from "@/service/TMDB"; -import { TrendingPeople } from "@/service/TMDB.type"; -import React from "react"; - -interface IPageProps { - params: { - peopleId: number; - }; -} -async function page({ params }: IPageProps) { - const people = await tmdb.getPeople(params.peopleId); - const knowFor: { results: TrendingPeople[] } = await tmdb.search( - people.name, - "person" - ); - return ( -
-

Biography

-
- {people.biography.split("\n\n").map((content, index) => ( -

- {content}

-

- ))} -
-

Know For

-
- {knowFor?.results?.[0].known_for.map((k) => ( - - ))} -
-
- ); -} - -export default page; +import MovieCard from "@/app/components/MovieCard"; +import tmdb from "@/service/TMDB"; +import { TrendingPeople } from "@/service/TMDB.type"; +import React from "react"; + +interface IPageProps { + params: { + peopleId: number; + }; +} +async function page({ params }: IPageProps) { + const people = await tmdb.getPeople(params.peopleId); + const knowFor: { results: TrendingPeople[] } = await tmdb.search( + people.name, + "person" + ); + return ( +
+

Biography

+
+ {people.biography.split("\n\n").map((content, index) => ( +

+ {content}

+

+ ))} +
+

Know For

+
+ {knowFor?.results?.[0].known_for.map((k) => ( + + ))} +
+
+ ); +} + +export default page; diff --git a/app/people/page.tsx b/app/people/page.tsx index 2c61922..f93cf00 100644 --- a/app/people/page.tsx +++ b/app/people/page.tsx @@ -1,33 +1,33 @@ -import tmdb from "@/service/TMDB"; -import { TrendingPeople } from "@/service/TMDB.type"; -import React from "react"; -import PeopleCard from "../components/PeopleCard"; -import Pagination from "../components/Pagination"; - -interface IPageProps { - searchParams: { - page: number; - }; -} -async function page({ searchParams }: IPageProps) { - const popularPeople: { results: TrendingPeople[] } = await tmdb.getTrending( - "person", - "day", - searchParams.page ?? 1 - ); - return ( -
-

Popular People

-
- -
-
- {popularPeople.results.map((p) => ( - - ))} -
-
- ); -} - -export default page; +import tmdb from "@/service/TMDB"; +import { TrendingPeople } from "@/service/TMDB.type"; +import React from "react"; +import PeopleCard from "../components/PeopleCard"; +import Pagination from "../components/Pagination"; + +interface IPageProps { + searchParams: { + page: number; + }; +} +async function page({ searchParams }: IPageProps) { + const popularPeople: { results: TrendingPeople[] } = await tmdb.getTrending( + "person", + "day", + searchParams.page ?? 1 + ); + return ( +
+

Popular People

+
+ +
+
+ {popularPeople.results.map((p) => ( + + ))} +
+
+ ); +} + +export default page; diff --git a/app/search/layout.tsx b/app/search/layout.tsx index d29e798..b429184 100644 --- a/app/search/layout.tsx +++ b/app/search/layout.tsx @@ -1,23 +1,23 @@ -import React from "react"; -import StickyTab from "../components/StickyTab"; - -function Page({ children }: { children: React.ReactNode }) { - const tabs = [ - { - tabName: "Tìm kiếm phim lẻ", - href: "/search/movie", - }, - { - tabName: "Tìm kiếm phim bộ", - href: "/search/tv", - }, - ]; - return ( -
- -
{children}
-
- ); -} - -export default Page; +import React from "react"; +import StickyTab from "../components/StickyTab"; + +function Page({ children }: { children: React.ReactNode }) { + const tabs = [ + { + tabName: "Tìm kiếm phim lẻ", + href: "/search/movie", + }, + { + tabName: "Tìm kiếm phim bộ", + href: "/search/tv", + }, + ]; + return ( +
+ +
{children}
+
+ ); +} + +export default Page; diff --git a/app/search/movie/page.tsx b/app/search/movie/page.tsx index 1a04e7e..3256790 100644 --- a/app/search/movie/page.tsx +++ b/app/search/movie/page.tsx @@ -1,67 +1,67 @@ -import React from "react"; -import tmdb from "@/service/TMDB"; -import { Movie, MovieList } from "@/service/TMDB.type"; -import MovieCard from "@/app/components/MovieCard"; -import Pagination from "@/app/components/Pagination"; -import Search from "@/app/components/search/Search"; -import MediaList from "@/app/components/MediaList/MediaList"; -import ChangeMediaListBtn from "@/app/components/MediaList/ChangeMediaListBtn"; -interface IPageProps { - searchParams: { - page: number; - q: string; - }; -} -async function page({ searchParams }: IPageProps) { - const movie: { results: Movie[] } = await tmdb.getTrending( - "movie", - "day", - searchParams.page ?? 1 - ); - const searchMovie: { results: Movie[]; total_pages: number } = - await tmdb.search(searchParams.q, "movie", searchParams.page); - return ( -
- - -
- -
-
- -
- {searchParams.q === undefined ? ( -
-

- Xu hướng hôm nay -

-
- -
-
- ) : ( -
-

- Kết quả tìm kiếm cho {`"${searchParams.q}"`} -

-
- -
-
- )} -
- ); -} - -export default page; +import React from "react"; +import tmdb from "@/service/TMDB"; +import { Movie, MovieList } from "@/service/TMDB.type"; +import MovieCard from "@/app/components/MovieCard"; +import Pagination from "@/app/components/Pagination"; +import Search from "@/app/components/search/Search"; +import MediaList from "@/app/components/MediaList/MediaList"; +import ChangeMediaListBtn from "@/app/components/MediaList/ChangeMediaListBtn"; +interface IPageProps { + searchParams: { + page: number; + q: string; + }; +} +async function page({ searchParams }: IPageProps) { + const movie: { results: Movie[] } = await tmdb.getTrending( + "movie", + "day", + searchParams.page ?? 1 + ); + const searchMovie: { results: Movie[]; total_pages: number } = + await tmdb.search(searchParams.q, "movie", searchParams.page); + return ( +
+ + +
+ +
+
+ +
+ {searchParams.q === undefined ? ( +
+

+ Xu hướng hôm nay +

+
+ +
+
+ ) : ( +
+

+ Kết quả tìm kiếm cho {`"${searchParams.q}"`} +

+
+ +
+
+ )} +
+ ); +} + +export default page; diff --git a/app/search/tv/page.tsx b/app/search/tv/page.tsx index 85e79d2..993a081 100644 --- a/app/search/tv/page.tsx +++ b/app/search/tv/page.tsx @@ -1,65 +1,65 @@ -import React from "react"; -import tmdb from "@/service/TMDB"; -import { Movie } from "@/service/TMDB.type"; -import Pagination from "@/app/components/Pagination"; -import Search from "@/app/components/search/Search"; -import MediaList from "@/app/components/MediaList/MediaList"; -import ChangeMediaListBtn from "@/app/components/MediaList/ChangeMediaListBtn"; -interface IPageProps { - searchParams: { - page: number; - q: string; - }; -} -async function page({ searchParams }: IPageProps) { - const movie: { results: Movie[] } = await tmdb.getTrending( - "tv", - "day", - searchParams.page ?? 1 - ); - const searchMovie: { results: Movie[]; total_pages: number } = - await tmdb.search(searchParams.q, "tv"); - return ( -
- -
- -
-
- -
- {searchParams.q === undefined ? ( -
-

- Xu hướng hôm nay -

-
- -
-
- ) : ( -
-

- Kết quả tìm kiếm cho {`"${searchParams.q}"`} -

-
- -
-
- )} -
- ); -} - -export default page; +import React from "react"; +import tmdb from "@/service/TMDB"; +import { Movie } from "@/service/TMDB.type"; +import Pagination from "@/app/components/Pagination"; +import Search from "@/app/components/search/Search"; +import MediaList from "@/app/components/MediaList/MediaList"; +import ChangeMediaListBtn from "@/app/components/MediaList/ChangeMediaListBtn"; +interface IPageProps { + searchParams: { + page: number; + q: string; + }; +} +async function page({ searchParams }: IPageProps) { + const movie: { results: Movie[] } = await tmdb.getTrending( + "tv", + "day", + searchParams.page ?? 1 + ); + const searchMovie: { results: Movie[]; total_pages: number } = + await tmdb.search(searchParams.q, "tv"); + return ( +
+ +
+ +
+
+ +
+ {searchParams.q === undefined ? ( +
+

+ Xu hướng hôm nay +

+
+ +
+
+ ) : ( +
+

+ Kết quả tìm kiếm cho {`"${searchParams.q}"`} +

+
+ +
+
+ )} +
+ ); +} + +export default page; diff --git a/app/setting/page.tsx b/app/setting/page.tsx index f29729b..e621a15 100644 --- a/app/setting/page.tsx +++ b/app/setting/page.tsx @@ -1,247 +1,247 @@ -"use client"; -import React from "react"; -import useStore from "../(store)/store"; -function Page() { - const theme = useStore((state) => state.theme); - const setTheme = useStore((state) => state.setTheme); - const expandedSideBar = useStore((state) => state.expandedSideBar); - const setExpandedSideBar = useStore((state) => state.setExpandedSideBar); - function handleChangeTheme(themeName: string) { - setTheme(themeName); - localStorage.setItem("theme", themeName); - document.getElementById("html")?.setAttribute("data-theme", themeName); - } - - return ( -
-

Cài đặt

-
- -
-
- -
-

Giao diện

-

- Tuỳ chình giao diện cho ứng dụng -

-
-
-
-
- - handleChangeTheme("black") - } - type="radio" - name="radio-1" - className="radio radio-primary md:tooltip hover:bg-primary" - data-tip="dark" - checked={theme === "black"} - /> -

dark

-
-
- - handleChangeTheme("light") - } - type="radio" - name="radio-1" - className="radio radio-primary md:tooltip hover:bg-primary" - data-tip="light" - checked={theme === "light"} - /> -

light

-
-
- - handleChangeTheme("luxury") - } - type="radio" - name="radio-1" - className="radio radio-primary md:tooltip hover:bg-primary" - data-tip="luxury" - checked={theme === "luxury"} - /> -

luxury

-
-
- - handleChangeTheme("dracula") - } - type="radio" - name="radio-1" - className="radio radio-primary md:tooltip hover:bg-primary" - data-tip="dracula" - checked={theme === "dracula"} - /> -

dracula

-
-
- - handleChangeTheme("night") - } - type="radio" - name="radio-1" - className="radio radio-primary md:tooltip hover:bg-primary" - data-tip="night" - checked={theme === "night"} - /> -

night

-
-
- - handleChangeTheme("retro") - } - type="radio" - name="radio-1" - className="radio radio-primary md:tooltip hover:bg-primary" - data-tip="retro" - checked={theme === "retro"} - /> -

retro

-
-
- - handleChangeTheme("business") - } - type="radio" - name="radio-1" - className="radio radio-primary md:tooltip hover:bg-primary" - data-tip="business" - checked={theme === "business"} - /> -

- business -

-
- -
- - handleChangeTheme("synthwave") - } - type="radio" - name="radio-1" - className="radio radio-primary md:tooltip hover:bg-primary" - data-tip="synthwave" - checked={theme === "synthwave"} - /> -

- sythwave -

-
-
- - handleChangeTheme("forest") - } - type="radio" - name="radio-1" - className="radio radio-primary md:tooltip hover:bg-primary" - data-tip="forest" - checked={theme === "forest"} - /> -

forest

-
-
- - handleChangeTheme("halloween") - } - type="radio" - name="radio-1" - className="radio radio-primary md:tooltip hover:bg-primary" - data-tip="halloween" - checked={theme === "halloween"} - /> -

- halloween -

-
-
-
-
-
- -
-

Thanh bên

-

Cài đặt thanh bên

-
-
-
-

Mini

- setExpandedSideBar(true)} - type="checkbox" - className="toggle toggle-primary" - /> -
-
-

Expanded

- - setExpandedSideBar(expandedSideBar) - } - type="checkbox" - className="toggle toggle-primary" - /> -
-
-
-
-
-
- ); -} - -export default Page; +"use client"; +import React from "react"; +import useStore from "../(store)/store"; +function Page() { + const theme = useStore((state) => state.theme); + const setTheme = useStore((state) => state.setTheme); + const expandedSideBar = useStore((state) => state.expandedSideBar); + const setExpandedSideBar = useStore((state) => state.setExpandedSideBar); + function handleChangeTheme(themeName: string) { + setTheme(themeName); + localStorage.setItem("theme", themeName); + document.getElementById("html")?.setAttribute("data-theme", themeName); + } + + return ( +
+

Cài đặt

+
+ +
+
+ +
+

Giao diện

+

+ Tuỳ chình giao diện cho ứng dụng +

+
+
+
+
+ + handleChangeTheme("black") + } + type="radio" + name="radio-1" + className="radio radio-primary md:tooltip hover:bg-primary" + data-tip="dark" + checked={theme === "black"} + /> +

dark

+
+
+ + handleChangeTheme("light") + } + type="radio" + name="radio-1" + className="radio radio-primary md:tooltip hover:bg-primary" + data-tip="light" + checked={theme === "light"} + /> +

light

+
+
+ + handleChangeTheme("luxury") + } + type="radio" + name="radio-1" + className="radio radio-primary md:tooltip hover:bg-primary" + data-tip="luxury" + checked={theme === "luxury"} + /> +

luxury

+
+
+ + handleChangeTheme("dracula") + } + type="radio" + name="radio-1" + className="radio radio-primary md:tooltip hover:bg-primary" + data-tip="dracula" + checked={theme === "dracula"} + /> +

dracula

+
+
+ + handleChangeTheme("night") + } + type="radio" + name="radio-1" + className="radio radio-primary md:tooltip hover:bg-primary" + data-tip="night" + checked={theme === "night"} + /> +

night

+
+
+ + handleChangeTheme("retro") + } + type="radio" + name="radio-1" + className="radio radio-primary md:tooltip hover:bg-primary" + data-tip="retro" + checked={theme === "retro"} + /> +

retro

+
+
+ + handleChangeTheme("business") + } + type="radio" + name="radio-1" + className="radio radio-primary md:tooltip hover:bg-primary" + data-tip="business" + checked={theme === "business"} + /> +

+ business +

+
+ +
+ + handleChangeTheme("synthwave") + } + type="radio" + name="radio-1" + className="radio radio-primary md:tooltip hover:bg-primary" + data-tip="synthwave" + checked={theme === "synthwave"} + /> +

+ sythwave +

+
+
+ + handleChangeTheme("forest") + } + type="radio" + name="radio-1" + className="radio radio-primary md:tooltip hover:bg-primary" + data-tip="forest" + checked={theme === "forest"} + /> +

forest

+
+
+ + handleChangeTheme("halloween") + } + type="radio" + name="radio-1" + className="radio radio-primary md:tooltip hover:bg-primary" + data-tip="halloween" + checked={theme === "halloween"} + /> +

+ halloween +

+
+
+
+
+
+ +
+

Thanh bên

+

Cài đặt thanh bên

+
+
+
+

Mini

+ setExpandedSideBar(true)} + type="checkbox" + className="toggle toggle-primary" + /> +
+
+

Expanded

+ + setExpandedSideBar(expandedSideBar) + } + type="checkbox" + className="toggle toggle-primary" + /> +
+
+
+
+
+
+ ); +} + +export default Page; diff --git a/app/trending/layout.tsx b/app/trending/layout.tsx index 3660156..f29d417 100644 --- a/app/trending/layout.tsx +++ b/app/trending/layout.tsx @@ -1,26 +1,26 @@ -import React from "react"; -import StickyTab from "../components/StickyTab"; - -interface PageProps { - children: React.ReactNode; -} -function Page({ children }: PageProps) { - const tabs = [ - { - tabName: "Xu hướng hôm nay", - href: "/trending/today", - }, - { - tabName: "Xu hướng tuần này", - href: "/trending/week", - }, - ]; - return ( -
- -
{children}
-
- ); -} - -export default Page; +import React from "react"; +import StickyTab from "../components/StickyTab"; + +interface PageProps { + children: React.ReactNode; +} +function Page({ children }: PageProps) { + const tabs = [ + { + tabName: "Xu hướng hôm nay", + href: "/trending/today", + }, + { + tabName: "Xu hướng tuần này", + href: "/trending/week", + }, + ]; + return ( +
+ +
{children}
+
+ ); +} + +export default Page; diff --git a/app/trending/today/page.tsx b/app/trending/today/page.tsx index 105a00d..f0d262d 100644 --- a/app/trending/today/page.tsx +++ b/app/trending/today/page.tsx @@ -1,41 +1,41 @@ -import Pagination from "@/app/components/Pagination"; -import React from "react"; -import tmdb from "@/service/TMDB"; -import { Movie } from "@/service/TMDB.type"; - -import MediaList from "@/app/components/MediaList/MediaList"; -import ChangeMediaListBtn from "@/app/components/MediaList/ChangeMediaListBtn"; -interface IPageProps { - searchParams: { - page: number; - }; -} -async function Page({ searchParams }: IPageProps) { - const movie: { results: Movie[] } = await tmdb.getTrending( - "movie", - "day", - searchParams.page ?? 1 - ); - - return ( -
-
- -
-
-

- Xu hướng hôm nay -

-
-
- -
- -
- -
-
- ); -} - -export default Page; +import Pagination from "@/app/components/Pagination"; +import React from "react"; +import tmdb from "@/service/TMDB"; +import { Movie } from "@/service/TMDB.type"; + +import MediaList from "@/app/components/MediaList/MediaList"; +import ChangeMediaListBtn from "@/app/components/MediaList/ChangeMediaListBtn"; +interface IPageProps { + searchParams: { + page: number; + }; +} +async function Page({ searchParams }: IPageProps) { + const movie: { results: Movie[] } = await tmdb.getTrending( + "movie", + "day", + searchParams.page ?? 1 + ); + + return ( +
+
+ +
+
+

+ Xu hướng hôm nay +

+
+
+ +
+ +
+ +
+
+ ); +} + +export default Page; diff --git a/app/trending/week/page.tsx b/app/trending/week/page.tsx index 8d9e145..b0a9fd8 100644 --- a/app/trending/week/page.tsx +++ b/app/trending/week/page.tsx @@ -1,33 +1,33 @@ -import React from "react"; -import tmdb from "@/service/TMDB"; -import { Movie } from "@/service/TMDB.type"; -import Pagination from "@/app/components/Pagination"; -import MediaList from "@/app/components/MediaList/MediaList"; -import ChangeMediaListBtn from "@/app/components/MediaList/ChangeMediaListBtn"; -async function page({ searchParams }: { searchParams: { page: number } }) { - const movie: { results: Movie[] } = await tmdb.getTrending( - "movie", - "week", - searchParams.page ?? 1 - ); - return ( -
-
- -
-
-

- Xu hướng tuần này -

-
-
- -
-
- -
-
- ); -} - -export default page; +import React from "react"; +import tmdb from "@/service/TMDB"; +import { Movie } from "@/service/TMDB.type"; +import Pagination from "@/app/components/Pagination"; +import MediaList from "@/app/components/MediaList/MediaList"; +import ChangeMediaListBtn from "@/app/components/MediaList/ChangeMediaListBtn"; +async function page({ searchParams }: { searchParams: { page: number } }) { + const movie: { results: Movie[] } = await tmdb.getTrending( + "movie", + "week", + searchParams.page ?? 1 + ); + return ( +
+
+ +
+
+

+ Xu hướng tuần này +

+
+
+ +
+
+ +
+
+ ); +} + +export default page; diff --git a/app/tv-show/[tvId]/cast/page.tsx b/app/tv-show/[tvId]/cast/page.tsx index 7ca8851..682ed1e 100644 --- a/app/tv-show/[tvId]/cast/page.tsx +++ b/app/tv-show/[tvId]/cast/page.tsx @@ -1,36 +1,36 @@ -import React from "react"; -import { Cast } from "../page"; -import Image from "next/image"; -import tmdb from "@/service/TMDB"; -async function page({ params }: { params: any }) { - const getCast = async () => { - const respone = await fetch( - `https://api.themoviedb.org/3/movie/${params.tvId}/credits?api_key=${process.env.TMDB}&language=vi-VN` - ); - const data = await respone.json(); - return data; - }; - const { cast }: Cast = await getCast(); - - return ( -
- {cast.map((c) => ( -
- cast - - {c.name} - -
- ))} -
- ); -} - -export default page; +import React from "react"; +import { Cast } from "../page"; +import Image from "next/image"; +import tmdb from "@/service/TMDB"; +async function page({ params }: { params: any }) { + const getCast = async () => { + const respone = await fetch( + `https://api.themoviedb.org/3/movie/${params.tvId}/credits?api_key=${process.env.TMDB}&language=vi-VN` + ); + const data = await respone.json(); + return data; + }; + const { cast }: Cast = await getCast(); + + return ( +
+ {cast.map((c) => ( +
+ cast + + {c.name} + +
+ ))} +
+ ); +} + +export default page; diff --git a/app/tv-show/[tvId]/error.tsx b/app/tv-show/[tvId]/error.tsx index 6d7587b..e9b609d 100644 --- a/app/tv-show/[tvId]/error.tsx +++ b/app/tv-show/[tvId]/error.tsx @@ -1,12 +1,12 @@ -"use client"; -import React from "react"; - -function error() { - return ( -
- error rồi thằng ngu -
- ); -} - -export default error; +"use client"; +import React from "react"; + +function error() { + return ( +
+ error rồi thằng ngu +
+ ); +} + +export default error; diff --git a/app/tv-show/[tvId]/layout.tsx b/app/tv-show/[tvId]/layout.tsx index e691cd0..9415d6e 100644 --- a/app/tv-show/[tvId]/layout.tsx +++ b/app/tv-show/[tvId]/layout.tsx @@ -1,115 +1,115 @@ -import { Movie } from "@/service/TMDB.type"; -import React from "react"; -import Image from "next/image"; -import MovieDetailTab from "@/app/components/MovieDetailTab"; -import WatchTrailerButton from "@/app/components/WatchTrailerButton"; -import Modal from "@/app/components/Modal"; -import tmdb from "@/service/TMDB"; -import ShadowImg from "@/app/components/ShadowImg"; -interface MovieDetailProp { - params: { - tvId: string; - }; - children: React.ReactNode; -} - -export const metadata = { - title: "Hai0z Movie", -}; - -async function Layout({ params, children }: MovieDetailProp) { - const movie: Movie = await tmdb.getMovieOrTV(+params.tvId, "tv"); - function convertToHourMinute(minutes: number) { - const hours = Math.floor(minutes / 60); - const remainingMinutes = minutes % 60; - const formattedTime = `${hours}h ${remainingMinutes}m`; - return formattedTime; - } - return ( -
-
- {movie.title} - -
-
-
-
- {movie.title} -
-
-
-

- {movie.title} -

-

- {movie?.tagline} -

- -
- {movie.genres.map((genres) => { - return ( -
- {genres.name} -
- ); - })} -
-
- -
-
-
-
- -
-
-
- -
- {movie.genres.map((genres) => { - return ( -
- {genres.name} -
- ); - })} -
-
- -
-
- - {children} -
- {/* */} -
- ); -} - -export default Layout; +import { Movie } from "@/service/TMDB.type"; +import React from "react"; +import Image from "next/image"; +import MovieDetailTab from "@/app/components/MovieDetailTab"; +import WatchTrailerButton from "@/app/components/WatchTrailerButton"; +import Modal from "@/app/components/Modal"; +import tmdb from "@/service/TMDB"; +import ShadowImg from "@/app/components/ShadowImg"; +interface MovieDetailProp { + params: { + tvId: string; + }; + children: React.ReactNode; +} + +export const metadata = { + title: "Hai0z Movie", +}; + +async function Layout({ params, children }: MovieDetailProp) { + const movie: Movie = await tmdb.getMovieOrTV(+params.tvId, "tv"); + function convertToHourMinute(minutes: number) { + const hours = Math.floor(minutes / 60); + const remainingMinutes = minutes % 60; + const formattedTime = `${hours}h ${remainingMinutes}m`; + return formattedTime; + } + return ( +
+
+ {movie.title} + +
+
+
+
+ {movie.title} +
+
+
+

+ {movie.title} +

+

+ {movie?.tagline} +

+ +
+ {movie.genres.map((genres) => { + return ( +
+ {genres.name} +
+ ); + })} +
+
+ +
+
+
+
+ +
+
+
+ +
+ {movie.genres.map((genres) => { + return ( +
+ {genres.name} +
+ ); + })} +
+
+ +
+
+ + {children} +
+ {/* */} +
+ ); +} + +export default Layout; diff --git a/app/tv-show/[tvId]/loading.tsx b/app/tv-show/[tvId]/loading.tsx index c8462da..0cfff64 100644 --- a/app/tv-show/[tvId]/loading.tsx +++ b/app/tv-show/[tvId]/loading.tsx @@ -1,20 +1,20 @@ -import React from "react"; -import Image from "next/image"; -function Loading() { - return ( -
- logo - -
- ); -} - -export default Loading; +import React from "react"; +import Image from "next/image"; +function Loading() { + return ( +
+ logo + +
+ ); +} + +export default Loading; diff --git a/app/tv-show/[tvId]/page.tsx b/app/tv-show/[tvId]/page.tsx index cf17090..3be955f 100644 --- a/app/tv-show/[tvId]/page.tsx +++ b/app/tv-show/[tvId]/page.tsx @@ -1,147 +1,147 @@ -import React from "react"; -import Image from "next/image"; -import { Movie, MovieList } from "@/service/TMDB.type"; -import Link from "next/link"; -import MovieCard from "@/app/components/MovieCard"; -import tmdb from "@/service/TMDB"; -export interface Cast { - cast: { - id: number; - profile_path: string; - name: string; - }[]; -} -async function Page({ - params, -}: { - params: { - tvId: number; - }; -}) { - const movie: Movie = await tmdb.getMovieOrTV(params.tvId, "tv"); - const { cast }: Cast = await tmdb.getCast(params.tvId, "tv"); - const { results: listRecommendations }: MovieList = - await tmdb.getRecomendations(params.tvId, "tv"); - return ( -
-
-
-
-

Original Title

-

- {movie.original_title} -

-
-
-

Status

-

- {movie.status} -

-
-
-

Production Companies

-
- {movie.production_companies.map((company) => ( -

- {company.name} -

- ))} -
-
-
-

Budget

-

- {movie.budget.toLocaleString("en-US", { - style: "currency", - currency: "USD", - })} -

-
-
-

Revenue

-

- {movie.revenue.toLocaleString("en-US", { - style: "currency", - currency: "USD", - })} -

-
-
-
-
-
-

{movie.overview}

-
-
-

Production Countries

- {movie.production_countries.map((country) => ( -

{country.name}

- ))} -
-
- Spoken Languages - {movie.spoken_languages.map((l) => ( -

{l.name}

- ))} -
-
-
-
-

- Top Diễn Viên -

- - Xem Thêm - -
- {cast?.slice(0, 5).map((cast) => ( -
- cast - - {cast.name} - -
- ))} -
-
-
-

- Phim Đề Xuất -

- - Xem Thêm - -
- {listRecommendations?.slice(0, 3).map((r: any) => ( - - ))} -
-
-
-
-
- ); -} - -export default Page; +import React from "react"; +import Image from "next/image"; +import { Movie, MovieList } from "@/service/TMDB.type"; +import Link from "next/link"; +import MovieCard from "@/app/components/MovieCard"; +import tmdb from "@/service/TMDB"; +export interface Cast { + cast: { + id: number; + profile_path: string; + name: string; + }[]; +} +async function Page({ + params, +}: { + params: { + tvId: number; + }; +}) { + const movie: Movie = await tmdb.getMovieOrTV(params.tvId, "tv"); + const { cast }: Cast = await tmdb.getCast(params.tvId, "tv"); + const { results: listRecommendations }: MovieList = + await tmdb.getRecomendations(params.tvId, "tv"); + return ( +
+
+
+
+

Original Title

+

+ {movie.original_title} +

+
+
+

Status

+

+ {movie.status} +

+
+
+

Production Companies

+
+ {movie.production_companies.map((company) => ( +

+ {company.name} +

+ ))} +
+
+
+

Budget

+

+ {movie.budget.toLocaleString("en-US", { + style: "currency", + currency: "USD", + })} +

+
+
+

Revenue

+

+ {movie.revenue.toLocaleString("en-US", { + style: "currency", + currency: "USD", + })} +

+
+
+
+
+
+

{movie.overview}

+
+
+

Production Countries

+ {movie.production_countries.map((country) => ( +

{country.name}

+ ))} +
+
+ Spoken Languages + {movie.spoken_languages.map((l) => ( +

{l.name}

+ ))} +
+
+
+
+

+ Top Diễn Viên +

+ + Xem Thêm + +
+ {cast?.slice(0, 5).map((cast) => ( +
+ cast + + {cast.name} + +
+ ))} +
+
+
+

+ Phim Đề Xuất +

+ + Xem Thêm + +
+ {listRecommendations?.slice(0, 3).map((r: any) => ( + + ))} +
+
+
+
+
+ ); +} + +export default Page; diff --git a/app/tv-show/[tvId]/recommendations/page.tsx b/app/tv-show/[tvId]/recommendations/page.tsx index 36f868d..73b4cb2 100644 --- a/app/tv-show/[tvId]/recommendations/page.tsx +++ b/app/tv-show/[tvId]/recommendations/page.tsx @@ -1,37 +1,37 @@ -import React from "react"; -import Image from "next/image"; -import { MovieList } from "@/service/TMDB.type"; -import Link from "next/link"; -import tmdb from "@/service/TMDB"; -async function page({ params }: { params: any }) { - const { results: listRecommendations }: MovieList = - await tmdb.getRecomendations(params.tvId, "movie"); - - return ( -
- {listRecommendations.map((l) => ( -
-
- - cast - -
-
- {l.title} -
-
- ))} -
- ); -} - -export default page; +import React from "react"; +import Image from "next/image"; +import { MovieList } from "@/service/TMDB.type"; +import Link from "next/link"; +import tmdb from "@/service/TMDB"; +async function page({ params }: { params: any }) { + const { results: listRecommendations }: MovieList = + await tmdb.getRecomendations(params.tvId, "movie"); + + return ( +
+ {listRecommendations.map((l) => ( +
+
+ + cast + +
+
+ {l.title} +
+
+ ))} +
+ ); +} + +export default page; diff --git a/context/AppProvider.tsx b/context/AppProvider.tsx index 51c22ec..758912d 100644 --- a/context/AppProvider.tsx +++ b/context/AppProvider.tsx @@ -1,56 +1,56 @@ -import React, { createContext, useState, useEffect } from "react"; -import Image from "next/image"; -import { motion } from "framer-motion"; -import useStore from "@/app/(store)/store"; -interface IAppContext {} -interface IProps { - children: React.ReactNode; -} -export const AppContext = createContext({}); -function AppProvider({ children }: IProps) { - const [loading, setLoading] = useState(true); - const setTheme = useStore((state) => state.setTheme); - - useEffect(() => { - const currentTheme = localStorage.getItem("theme"); - if (currentTheme) { - setTheme(currentTheme as string); - document - .getElementById("html") - ?.setAttribute("data-theme", currentTheme); - } - setTimeout(() => setLoading(false), 2000); - }, [setTheme]); - return ( - - {loading ? ( - -
- logo - - The Movies - -
- -
- ) : ( - children - )} -
- ); -} - -export default AppProvider; +import React, { createContext, useState, useEffect } from "react"; +import Image from "next/image"; +import { motion } from "framer-motion"; +import useStore from "@/app/(store)/store"; +interface IAppContext {} +interface IProps { + children: React.ReactNode; +} +export const AppContext = createContext({}); +function AppProvider({ children }: IProps) { + const [loading, setLoading] = useState(true); + const setTheme = useStore((state) => state.setTheme); + + useEffect(() => { + const currentTheme = localStorage.getItem("theme"); + if (currentTheme) { + setTheme(currentTheme as string); + document + .getElementById("html") + ?.setAttribute("data-theme", currentTheme); + } + setTimeout(() => setLoading(false), 2000); + }, [setTheme]); + return ( + + {loading ? ( + +
+ logo + + The Movies + +
+ +
+ ) : ( + children + )} +
+ ); +} + +export default AppProvider; diff --git a/hooks/useWindowDemensions.tsx b/hooks/useWindowDemensions.tsx index 3340a56..aebd5e5 100644 --- a/hooks/useWindowDemensions.tsx +++ b/hooks/useWindowDemensions.tsx @@ -1,42 +1,42 @@ -import useStore from "@/app/(store)/store"; -import { useState, useEffect } from "react"; -function getWindowDimensions() { - if (typeof window !== "undefined") { - const { innerWidth: width, innerHeight: height } = window; - return { - width, - height, - }; - } - return { - width: 0, - height: 0, - }; -} - -function useWindowDimensions() { - const [windowDimensions, setWindowDimensions] = useState( - {} as { width: number; height: number } - ); - - useEffect(() => { - setWindowDimensions({ - width: window.innerWidth, - height: window.innerHeight, - }); - }, []); - - useEffect(() => { - function handleResize() { - setWindowDimensions(getWindowDimensions()); - } - if (typeof window !== "undefined") { - window.addEventListener("resize", handleResize); - - return () => window.removeEventListener("resize", handleResize); - } - }, []); - - return windowDimensions; -} -export default useWindowDimensions; +import useStore from "@/app/(store)/store"; +import { useState, useEffect } from "react"; +function getWindowDimensions() { + if (typeof window !== "undefined") { + const { innerWidth: width, innerHeight: height } = window; + return { + width, + height, + }; + } + return { + width: 0, + height: 0, + }; +} + +function useWindowDimensions() { + const [windowDimensions, setWindowDimensions] = useState( + {} as { width: number; height: number } + ); + + useEffect(() => { + setWindowDimensions({ + width: window.innerWidth, + height: window.innerHeight, + }); + }, []); + + useEffect(() => { + function handleResize() { + setWindowDimensions(getWindowDimensions()); + } + if (typeof window !== "undefined") { + window.addEventListener("resize", handleResize); + + return () => window.removeEventListener("resize", handleResize); + } + }, []); + + return windowDimensions; +} +export default useWindowDimensions; diff --git a/lib/prisma/index.ts b/lib/prisma/index.ts index b5bf6ce..fd52199 100644 --- a/lib/prisma/index.ts +++ b/lib/prisma/index.ts @@ -1,5 +1,5 @@ -import { PrismaClient } from "@prisma/client"; - -const prisma = new PrismaClient(); - -export default prisma; +import { PrismaClient } from "@prisma/client"; + +const prisma = new PrismaClient(); + +export default prisma; diff --git a/lib/prisma/task.ts b/lib/prisma/task.ts index cde68de..db4d193 100644 --- a/lib/prisma/task.ts +++ b/lib/prisma/task.ts @@ -1,21 +1,21 @@ -"use server"; -import prisma from "."; -import { revalidatePath } from "next/cache"; - -export const getAllTask = async () => { - const task = await prisma.task.findMany({}); - return task; -}; -// export const createTask = async (task: Task) => { -// await prisma.task.create({ -// data: task, -// }); -// }; -// export const deleteTask = async (id: string | undefined) => { -// await prisma.task.delete({ -// where: { -// id, -// }, -// }); -// revalidatePath("/"); -// }; +"use server"; +import prisma from "."; +import { revalidatePath } from "next/cache"; + +export const getAllTask = async () => { + const task = await prisma.task.findMany({}); + return task; +}; +// export const createTask = async (task: Task) => { +// await prisma.task.create({ +// data: task, +// }); +// }; +// export const deleteTask = async (id: string | undefined) => { +// await prisma.task.delete({ +// where: { +// id, +// }, +// }); +// revalidatePath("/"); +// }; diff --git a/package.json b/package.json index 0222052..9a7164a 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev --turbo", + "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" diff --git a/service/TMDB.ts b/service/TMDB.ts index 5eb108a..ec0fbcf 100644 --- a/service/TMDB.ts +++ b/service/TMDB.ts @@ -1,141 +1,141 @@ -import { MovieCredits, People, Photo } from "./TMDB.type"; - -class TMDB { - private readonly IMG_PATH: string = "https://image.tmdb.org/t/p/" as const; - private readonly BASE_URL: string = "https://api.themoviedb.org/3" as const; - - getImage(path: string, size?: "w300" | "w500") { - if (size) { - return `${this.IMG_PATH}/${size}/${path}`; - } else { - return `${this.IMG_PATH}/original/${path}`; - } - } - async getMovieOrTV(id: number, type: "movie" | "tv") { - const respone = await fetch( - `https://api.themoviedb.org/3/${type}/${id}?api_key=${process.env.TMDB}&language=vi-VN` - ); - const data = await respone.json(); - - return data; - } - async getTrending( - type: "movie" | "tv" | "person", - timeWindow: "day" | "week", - page?: number - ) { - if (page) { - const respone = await fetch( - `${this.BASE_URL}/trending/${type}/${timeWindow}?api_key=${process.env.TMDB}&page=${page}&language=vi-VN` - ); - const data = await respone.json(); - return data; - } else { - const respone = await fetch( - `${this.BASE_URL}/trending/${type}/${timeWindow}?api_key=${process.env.TMDB}&language=vi-VN` - ); - const data = await respone.json(); - return data; - } - } - - async getTopRate(type: "movie" | "tv" | "person") { - const respone = await fetch( - `${this.BASE_URL}/${type}/top_rated?api_key=${process.env.TMDB}&language=vi-VN` - ); - const data = await respone.json(); - return data; - } - async getPopular(type: "movie" | "tv" | "person") { - const respone = await fetch( - `${this.BASE_URL}/${type}/popular?api_key=${process.env.TMDB}&language=vi-VN` - ); - const data = await respone.json(); - return data; - } - async getCast(id: number, type: "movie" | "tv") { - const respone = await fetch( - `${this.BASE_URL}/${type}/${id}/credits?api_key=${process.env.TMDB}&language=vi-VN` - ); - const data = await respone.json(); - return data; - } - async getRecomendations(id: number, type: "movie" | "tv") { - const respone = await fetch( - `${this.BASE_URL}/${type}/${id}/recommendations?api_key=${process.env.TMDB}&language=vi-VN` - ); - const data = await respone.json(); - - return data; - } - async search( - query: string, - type: "movie" | "tv" | "person", - page?: number - ) { - const respone = await fetch( - `${this.BASE_URL}/search/${type}?api_key=${ - process.env.TMDB - }&query=${query}&language=vi-VN&page=${page ?? 1}` - ); - const data = await respone.json(); - - return data; - } - async getListGenres(type: "movie" | "tv") { - const respone = await fetch( - `${this.BASE_URL}/genre/${type}/list?api_key=${process.env.TMDB}&language=vi-VN` - ); - const data = await respone.json(); - return data; - } - async discover(type: "movie" | "tv", genres?: number, page?: number) { - let url = `${this.BASE_URL}/discover/${type}?api_key=${process.env.TMDB}&language=vi-VN`; - if (genres) { - url += `&with_genres=${genres}`; - } - if (page) { - url += `&page=${page}`; - } - const respone = await fetch(url); - const data = await respone.json(); - return data; - } - async getPhotos(type: "movie" | "tv", id: number) { - const respone = await fetch( - `${this.BASE_URL}/${type}/${id}/images?api_key=${process.env.TMDB}` - ); - const data = await respone.json(); - return data; - } - async getVideos(type: "movie" | "tv", id: number) { - const respone = await fetch( - `${this.BASE_URL}/${type}/${id}/videos?api_key=${process.env.TMDB}` - ); - const data = await respone.json(); - return data; - } - async getPeople(id: number): Promise { - const respone = await fetch( - `${this.BASE_URL}/person/${id}?api_key=${process.env.TMDB}` - ); - const data = await respone.json(); - return data; - } - async getMovieCredits(id: number): Promise<{ cast: MovieCredits[] }> { - const respone = await fetch( - `${this.BASE_URL}/person/${id}/movie_credits?api_key=${process.env.TMDB}&language=vi-VN` - ); - const data = await respone.json(); - return data; - } - async getPeoplePhotos(id: number): Promise<{ profiles: Photo[] }> { - const respone = await fetch( - `${this.BASE_URL}/person/${id}/images?api_key=${process.env.TMDB}` - ); - const data = await respone.json(); - return data; - } -} -const tmdb = new TMDB(); -export default tmdb; +import { MovieCredits, People, Photo } from "./TMDB.type"; + +class TMDB { + private readonly IMG_PATH: string = "https://image.tmdb.org/t/p/" as const; + private readonly BASE_URL: string = "https://api.themoviedb.org/3" as const; + + getImage(path: string, size?: "w300" | "w500") { + if (size) { + return `${this.IMG_PATH}/${size}/${path}`; + } else { + return `${this.IMG_PATH}/original/${path}`; + } + } + async getMovieOrTV(id: number, type: "movie" | "tv") { + const respone = await fetch( + `https://api.themoviedb.org/3/${type}/${id}?api_key=${process.env.TMDB}&language=vi-VN` + ); + const data = await respone.json(); + + return data; + } + async getTrending( + type: "movie" | "tv" | "person", + timeWindow: "day" | "week", + page?: number + ) { + if (page) { + const respone = await fetch( + `${this.BASE_URL}/trending/${type}/${timeWindow}?api_key=${process.env.TMDB}&page=${page}&language=vi-VN` + ); + const data = await respone.json(); + return data; + } else { + const respone = await fetch( + `${this.BASE_URL}/trending/${type}/${timeWindow}?api_key=${process.env.TMDB}&language=vi-VN` + ); + const data = await respone.json(); + return data; + } + } + + async getTopRate(type: "movie" | "tv" | "person") { + const respone = await fetch( + `${this.BASE_URL}/${type}/top_rated?api_key=${process.env.TMDB}&language=vi-VN` + ); + const data = await respone.json(); + return data; + } + async getPopular(type: "movie" | "tv" | "person") { + const respone = await fetch( + `${this.BASE_URL}/${type}/popular?api_key=${process.env.TMDB}&language=vi-VN` + ); + const data = await respone.json(); + return data; + } + async getCast(id: number, type: "movie" | "tv") { + const respone = await fetch( + `${this.BASE_URL}/${type}/${id}/credits?api_key=${process.env.TMDB}&language=vi-VN` + ); + const data = await respone.json(); + return data; + } + async getRecomendations(id: number, type: "movie" | "tv") { + const respone = await fetch( + `${this.BASE_URL}/${type}/${id}/recommendations?api_key=${process.env.TMDB}&language=vi-VN` + ); + const data = await respone.json(); + + return data; + } + async search( + query: string, + type: "movie" | "tv" | "person", + page?: number + ) { + const respone = await fetch( + `${this.BASE_URL}/search/${type}?api_key=${ + process.env.TMDB + }&query=${query}&language=vi-VN&page=${page ?? 1}` + ); + const data = await respone.json(); + + return data; + } + async getListGenres(type: "movie" | "tv") { + const respone = await fetch( + `${this.BASE_URL}/genre/${type}/list?api_key=${process.env.TMDB}&language=vi-VN` + ); + const data = await respone.json(); + return data; + } + async discover(type: "movie" | "tv", genres?: number, page?: number) { + let url = `${this.BASE_URL}/discover/${type}?api_key=${process.env.TMDB}&language=vi-VN`; + if (genres) { + url += `&with_genres=${genres}`; + } + if (page) { + url += `&page=${page}`; + } + const respone = await fetch(url); + const data = await respone.json(); + return data; + } + async getPhotos(type: "movie" | "tv", id: number) { + const respone = await fetch( + `${this.BASE_URL}/${type}/${id}/images?api_key=${process.env.TMDB}` + ); + const data = await respone.json(); + return data; + } + async getVideos(type: "movie" | "tv", id: number) { + const respone = await fetch( + `${this.BASE_URL}/${type}/${id}/videos?api_key=${process.env.TMDB}` + ); + const data = await respone.json(); + return data; + } + async getPeople(id: number): Promise { + const respone = await fetch( + `${this.BASE_URL}/person/${id}?api_key=${process.env.TMDB}` + ); + const data = await respone.json(); + return data; + } + async getMovieCredits(id: number): Promise<{ cast: MovieCredits[] }> { + const respone = await fetch( + `${this.BASE_URL}/person/${id}/movie_credits?api_key=${process.env.TMDB}&language=vi-VN` + ); + const data = await respone.json(); + return data; + } + async getPeoplePhotos(id: number): Promise<{ profiles: Photo[] }> { + const respone = await fetch( + `${this.BASE_URL}/person/${id}/images?api_key=${process.env.TMDB}` + ); + const data = await respone.json(); + return data; + } +} +const tmdb = new TMDB(); +export default tmdb; diff --git a/service/TMDB.type.ts b/service/TMDB.type.ts index faf0602..0fa59ca 100644 --- a/service/TMDB.type.ts +++ b/service/TMDB.type.ts @@ -1,176 +1,176 @@ -export interface TrendingMovie { - adult: boolean; - backdrop_path: string; - id: number; - title: string; - original_language: string; - original_title: string; - overview: string; - poster_path: string; - media_type: string; - genre_ids: number[]; - popularity: number; - release_date: string; - video: boolean; - vote_average: number; - vote_count: number; -} -export interface Movie { - adult: boolean; - backdrop_path: string; - belongs_to_collection: Belongstocollection; - budget: number; - genres: Genre[]; - homepage: string; - id: number; - imdb_id: string; - name: string; - original_language: string; - original_title: string; - overview: string; - popularity: number; - poster_path: string; - production_companies: Productioncompany[]; - production_countries: Productioncountry[]; - release_date: string; - revenue: number; - runtime: number; - spoken_languages: Spokenlanguage[]; - status: string; - tagline: string; - title: string; - video: boolean; - vote_average: number; - vote_count: number; -} -export interface MovieCredits { - adult: boolean; - backdrop_path: string; - genre_ids: number[]; - id: number; - original_language: string; - original_title: string; - overview: string; - popularity: number; - poster_path: string; - release_date: string; - title: string; - video: boolean; - vote_average: number; - vote_count: number; - character: string; - credit_id: string; - order: number; -} -interface Spokenlanguage { - english_name: string; - iso_639_1: string; - name: string; -} - -interface Productioncountry { - iso_3166_1: string; - name: string; -} - -interface Productioncompany { - id: number; - logo_path?: string; - name: string; - origin_country: string; -} - -export interface Genre { - id: number; - name: string; -} - -interface Belongstocollection { - id: number; - name: string; - poster_path: string; - backdrop_path: string; -} - -export interface TVShow {} - -export interface Photo { - aspect_ratio: number; - height: number; - iso_639_1: string; - file_path: string; - vote_average: number; - vote_count: number; - width: number; -} -export interface ListPhotos { - backdrops: Photo[]; - logos: Photo[]; - posters: Photo[]; -} - -export enum videoTypes { - Clip = "Clip", - Featurette = "Featurette", - BehindTheScenes = "Behind the Scenes", - Teaser = "Teaser", - Trailer = "Trailer", -} -export interface Videos { - iso_639_1: string; - iso_3166_1: string; - name: string; - key: string; - site: string; - size: number; - type: videoTypes; - official: true; - published_at: Date; - id: string; -} - -export interface TrendingPeople { - adult: boolean; - id: number; - name: string; - original_name: string; - media_type: string; - popularity: number; - gender: number; - known_for_department: string; - profile_path: string; - known_for: Knownfor[]; -} -export interface People { - adult: boolean; - also_known_as: string[]; - biography: string; - birthday: string; - deathday?: any; - gender: number; - homepage: string; - id: number; - imdb_id: string; - known_for_department: string; - name: string; - place_of_birth: string; - popularity: number; - profile_path: string; -} -interface Knownfor { - adult: boolean; - backdrop_path: string; - id: number; - title: string; - original_language: string; - original_title: string; - overview: string; - poster_path: string; - media_type: string; - genre_ids: number[]; - popularity: number; - release_date: string; - video: boolean; - vote_average: number; - vote_count: number; -} +export interface TrendingMovie { + adult: boolean; + backdrop_path: string; + id: number; + title: string; + original_language: string; + original_title: string; + overview: string; + poster_path: string; + media_type: string; + genre_ids: number[]; + popularity: number; + release_date: string; + video: boolean; + vote_average: number; + vote_count: number; +} +export interface Movie { + adult: boolean; + backdrop_path: string; + belongs_to_collection: Belongstocollection; + budget: number; + genres: Genre[]; + homepage: string; + id: number; + imdb_id: string; + name: string; + original_language: string; + original_title: string; + overview: string; + popularity: number; + poster_path: string; + production_companies: Productioncompany[]; + production_countries: Productioncountry[]; + release_date: string; + revenue: number; + runtime: number; + spoken_languages: Spokenlanguage[]; + status: string; + tagline: string; + title: string; + video: boolean; + vote_average: number; + vote_count: number; +} +export interface MovieCredits { + adult: boolean; + backdrop_path: string; + genre_ids: number[]; + id: number; + original_language: string; + original_title: string; + overview: string; + popularity: number; + poster_path: string; + release_date: string; + title: string; + video: boolean; + vote_average: number; + vote_count: number; + character: string; + credit_id: string; + order: number; +} +interface Spokenlanguage { + english_name: string; + iso_639_1: string; + name: string; +} + +interface Productioncountry { + iso_3166_1: string; + name: string; +} + +interface Productioncompany { + id: number; + logo_path?: string; + name: string; + origin_country: string; +} + +export interface Genre { + id: number; + name: string; +} + +interface Belongstocollection { + id: number; + name: string; + poster_path: string; + backdrop_path: string; +} + +export interface TVShow {} + +export interface Photo { + aspect_ratio: number; + height: number; + iso_639_1: string; + file_path: string; + vote_average: number; + vote_count: number; + width: number; +} +export interface ListPhotos { + backdrops: Photo[]; + logos: Photo[]; + posters: Photo[]; +} + +export enum videoTypes { + Clip = "Clip", + Featurette = "Featurette", + BehindTheScenes = "Behind the Scenes", + Teaser = "Teaser", + Trailer = "Trailer", +} +export interface Videos { + iso_639_1: string; + iso_3166_1: string; + name: string; + key: string; + site: string; + size: number; + type: videoTypes; + official: true; + published_at: Date; + id: string; +} + +export interface TrendingPeople { + adult: boolean; + id: number; + name: string; + original_name: string; + media_type: string; + popularity: number; + gender: number; + known_for_department: string; + profile_path: string; + known_for: Knownfor[]; +} +export interface People { + adult: boolean; + also_known_as: string[]; + biography: string; + birthday: string; + deathday?: any; + gender: number; + homepage: string; + id: number; + imdb_id: string; + known_for_department: string; + name: string; + place_of_birth: string; + popularity: number; + profile_path: string; +} +interface Knownfor { + adult: boolean; + backdrop_path: string; + id: number; + title: string; + original_language: string; + original_title: string; + overview: string; + poster_path: string; + media_type: string; + genre_ids: number[]; + popularity: number; + release_date: string; + video: boolean; + vote_average: number; + vote_count: number; +}