Added MovieCard and Billboard
parent
ca2049d363
commit
2116b70e22
|
@ -1,9 +1,25 @@
|
|||
import React from 'react'
|
||||
import { signOut } from 'next-auth/react'
|
||||
|
||||
const AccountMenu = () => {
|
||||
interface AccountMenuProps {
|
||||
visible?: boolean;
|
||||
}
|
||||
|
||||
const AccountMenu: React.FC<AccountMenuProps> = ({visible}) => {
|
||||
if(!visible){
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div>AccountMenu</div>
|
||||
<div className='bg-black w-56 absolute top-14 right-0 py-5 flex-col border-2 border-gray-800 flex '>
|
||||
<div className='flex flex-col gap-3'>
|
||||
<div className='px-3 group/item flex flex-row gap-3 items-center w-full'>
|
||||
<img className='w-8 rounded-md' src='' alt='Immagine Profilo' />
|
||||
<p className='text-white text-sm group-hover/item:underline'>Username</p>
|
||||
</div>
|
||||
<hr className='bg-gray-600 border-0 h-px my-4' />
|
||||
<div onClick={() => signOut()} className='px-3 text-center text-white text-sm hover:underline'>Logout</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import useBillBoard from "@/hooks/useBillboard";
|
||||
import React from "react";
|
||||
import {AiOutlineInfoCircle} from "react-icons/ai"
|
||||
|
||||
const Billboard = () => {
|
||||
const { data } = useBillBoard();
|
||||
return (
|
||||
<div className="relative aspect-video w-full">
|
||||
<video className="w-full aspect-video object-cover brightness-[60%]" autoPlay muted loop poster={data?.thumbnailUrl} src={data?.videoUrl}></video>
|
||||
<div className="absolute top-[30%] md:top-[40%] ml-4 md:ml-16">
|
||||
<p className="text-white text-1xl md:text-5xl h-full w-[50%] lg:text-6xl font-bold drop-shadow-xl">
|
||||
{data?.title}
|
||||
</p>
|
||||
<p className="text-white text-[8px] md:text-lg mt-3 md:mt-8 w-[90%] md:w-[80%] lg:w-[50%] drop-shadow-xl">
|
||||
{data?.description}
|
||||
</p>
|
||||
<div className="flex flex-row items-center mt-3 md:mt-4 gap-3">
|
||||
<button className="bg-white text-white bg-opacity-30 rounded-md py-1 md:py-2 px-2 md:px-4 w-auto text-xs lg:text-lg font-semibold flex flex-row items-center hover:bg-opacity-20 transition">
|
||||
<AiOutlineInfoCircle className="mr-1" />
|
||||
More Info
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Billboard;
|
|
@ -0,0 +1,46 @@
|
|||
import React from "react";
|
||||
import { BsFillPlayFill } from "react-icons/bs";
|
||||
|
||||
interface MovieCardProps {
|
||||
data: Record<string,any>[];
|
||||
}
|
||||
|
||||
const MovieCard: React.FC<MovieCardProps> = ({ data }) => {
|
||||
return (
|
||||
<div className="group bg-zinc-900 col-span relative h-[12vw]">
|
||||
<img className="cursor-pointer object-cover transition duration shadow-xl rounded-md group-hover:opacity-90 sm:group-hover:opacity-0 delay-300 w-full h-[12vw]"
|
||||
src={data.thumbnailUrl} alt="Thumbnail" />
|
||||
<div className="opacity-0 absolute top-0 transition duration-200 z-10 invisible sm:visible delay-300 w-full scale-0 group-hover:scale-110 group-hover:-translate-y-[6vw] group-hover:translate-x-[2vw] group-hover:opacity-100">
|
||||
<img className="cursor-pointer object-cover transition duration shadow-xl rounded-md w-full h-[12vw]" src={data.thumbnailUrl} alt="Thumbnail" />
|
||||
<div className="
|
||||
z-10
|
||||
bg-zinc-800
|
||||
p-2
|
||||
lg:p-4
|
||||
absolute
|
||||
w-full
|
||||
transition
|
||||
shadow-md
|
||||
rounded-b-md
|
||||
">
|
||||
<div className="flex flex-row items-center gap-3">
|
||||
<div className="cursor-pointer w-6 h-6 lg:w-10 lg:h-10 bg-white rounded-full flex justify-center items-center transition hover:bg-neutral-300" onClick={() => {}}>
|
||||
<BsFillPlayFill size={30} />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-green-400 font-semibold mt-4">
|
||||
New <span className="text-white">2023</span>
|
||||
</p>
|
||||
<div className="flex flex-row mt-4 gap-2 items-center">
|
||||
<p className="text-white text-[10px] lg:text-sm">{data.duration}</p>
|
||||
</div>
|
||||
<div className="flex flex-row mt-4 gap-2 items-center">
|
||||
<p className="text-white text-[10px] lg:text-sm">{data.genre}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MovieCard;
|
|
@ -0,0 +1,29 @@
|
|||
import React from 'react'
|
||||
import { isEmpty } from 'lodash';
|
||||
import MovieCard from './MovieCard';
|
||||
|
||||
interface MovieListProps {
|
||||
data: Record<string, any>[];
|
||||
title: string;
|
||||
}
|
||||
|
||||
|
||||
const MovieList: React.FC<MovieListProps> = ({ data, title }) => {
|
||||
if (isEmpty(data)) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className='px-4 md:px-12 mt-4 space-y-8'>
|
||||
<div className='text-white text-md md:text-xl lg:text-2xl font-semibold mb-4'>
|
||||
<p>{title}</p>
|
||||
<div className='grid grid-cols-4 gap-2'>
|
||||
{data.map((film) => (
|
||||
<MovieCard key={film.id} data={film} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MovieList;
|
|
@ -1,17 +1,42 @@
|
|||
import MobileMenu from "./MobileMenu"
|
||||
import NavbarItem from "./NavbarItem"
|
||||
import { BsChevronDown,BsSearch,BsBell } from "react-icons/bs"
|
||||
import { useCallback,useState } from "react"
|
||||
import { useCallback,useState,useEffect } from "react"
|
||||
import AccountMenu from "./AccountMenu";
|
||||
|
||||
function Navbar() {
|
||||
const [showMobileMenu,setShowMobileMenu] = useState(false);
|
||||
const [showAccountMenu,setshowAccountMenu] = useState(false);
|
||||
const [showBackground,setShowBackground] = useState(false);
|
||||
|
||||
const TOP_OFFSET = 66;
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
if(window.scrollY >= TOP_OFFSET){
|
||||
setShowBackground(true);
|
||||
}else{
|
||||
setShowBackground(false)
|
||||
}
|
||||
}
|
||||
window.addEventListener('scroll',handleScroll);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll',handleScroll);
|
||||
}
|
||||
},[])
|
||||
|
||||
const toggleMobileMenu = useCallback(()=>{
|
||||
setShowMobileMenu((currentvisible) => !currentvisible)
|
||||
},[])
|
||||
},[]);
|
||||
|
||||
const toggleAccountMenu = useCallback(()=>{
|
||||
setshowAccountMenu((currentvisible) => !currentvisible)
|
||||
},[]);
|
||||
|
||||
return (
|
||||
<nav className="w-full fixed z-40">
|
||||
<div className="px-4 md:px-16 py-6 flex flex-row items-center transition duration-500 bg-zinc-900 bg-opacity-90">
|
||||
<div className={`px-4 md:px-16 py-6 flex flex-row items-center transition duration-500 ${showBackground ? 'bg-zinc-900 bg-opacity-90' : ''}`}>
|
||||
<img className="h-10 lg:h-16" src="https://i.imgur.com/6awt6G7.png" alt="Logo" />
|
||||
<div className="flex-row ml-8 gap-7 hidden lg:flex">
|
||||
<NavbarItem label="Home"/>
|
||||
|
@ -23,7 +48,7 @@ function Navbar() {
|
|||
</div>
|
||||
<div onClick={toggleMobileMenu} className="lg:hidden flex flex-row items-center gap-2 ml-8 cursor-pointer relative">
|
||||
<p className="text-white text-sm">Sfoglia</p>
|
||||
<BsChevronDown className="text-white transition" />
|
||||
<BsChevronDown className={`text-white transition ${showMobileMenu ? 'rotate-180' : 'rotate-0'}`} />
|
||||
<MobileMenu visible={showMobileMenu} />
|
||||
</div>
|
||||
<div className="flex flex-row ml-auto gap-7 items-center">
|
||||
|
@ -33,13 +58,13 @@ function Navbar() {
|
|||
<div className="text-gray-200 hover:text-gray-300 cursor-pointer transition ">
|
||||
<BsBell />
|
||||
</div>
|
||||
<div className="flex flex-row items-center gap-2 cursor-pointer relative">
|
||||
<div onClick={toggleAccountMenu} className="flex flex-row items-center gap-2 cursor-pointer relative">
|
||||
<div className="w-6 h-6 lg:w-10 lg:h-10 rounded-md overflow-hidden">
|
||||
<img src="https://api.dicebear.com/6.x/fun-emoji/svg?seed=Michael" alt="Profilo" />
|
||||
</div>
|
||||
<BsChevronDown className="text-white transition" />
|
||||
<BsChevronDown className={`text-white transition ${showAccountMenu ? 'rotate-180' : 'rotate-0'}`} />
|
||||
</div>
|
||||
<AccountMenu />
|
||||
<AccountMenu visible={showAccountMenu} />
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import useSWR from "swr";
|
||||
import fetcher from "@/lib/fetcher";
|
||||
|
||||
const useBillBoard = () => {
|
||||
const { data,error,isLoading } = useSWR('/api/random',fetcher,{
|
||||
revalidateIfStale: false,
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false
|
||||
});
|
||||
return {
|
||||
data,
|
||||
error,
|
||||
isLoading
|
||||
}
|
||||
}
|
||||
|
||||
export default useBillBoard;
|
|
@ -0,0 +1,17 @@
|
|||
import useSWR from "swr";
|
||||
import fetcher from "@/lib/fetcher";
|
||||
|
||||
const useMoviesList = () => {
|
||||
const {data,error,isLoading} = useSWR('/api/movies',fetcher,{
|
||||
revalidateIfStale: false,
|
||||
revalidateOnReconnect: false,
|
||||
revalidateOnFocus: false
|
||||
});
|
||||
return {
|
||||
data,
|
||||
error,
|
||||
isLoading
|
||||
}
|
||||
}
|
||||
|
||||
export default useMoviesList;
|
|
@ -18,6 +18,8 @@
|
|||
"bcrypt": "^5.1.0",
|
||||
"eslint": "8.42.0",
|
||||
"eslint-config-next": "13.4.4",
|
||||
"loadash": "^1.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"next": "13.4.4",
|
||||
"next-auth": "^4.22.1",
|
||||
"postcss": "8.4.24",
|
||||
|
@ -30,6 +32,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/lodash": "^4.14.195",
|
||||
"prisma": "^4.13.0"
|
||||
}
|
||||
},
|
||||
|
@ -479,6 +482,12 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
|
||||
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.14.195",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.195.tgz",
|
||||
"integrity": "sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz",
|
||||
|
@ -2989,6 +2998,12 @@
|
|||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
||||
},
|
||||
"node_modules/loadash": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/loadash/-/loadash-1.0.0.tgz",
|
||||
"integrity": "sha512-xlX5HBsXB3KG0FJbJJG/3kYWCfsCyCSus3T+uHVu6QL6YxAdggmm3QeyLgn54N2yi5/UE6xxL5ZWJAAiHzHYEg==",
|
||||
"deprecated": "Package is unsupport. Please use the lodash package instead."
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||
|
@ -3003,6 +3018,11 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
|
|
13
package.json
13
package.json
|
@ -11,27 +11,28 @@
|
|||
"dependencies": {
|
||||
"@next-auth/prisma-adapter": "^1.0.6",
|
||||
"@prisma/client": "^4.13.0",
|
||||
"axios": "^1.4.0",
|
||||
"bcrypt": "^5.1.0",
|
||||
"next-auth": "^4.22.1",
|
||||
"react-icons": "^4.8.0",
|
||||
"swr": "^2.1.5",
|
||||
"@types/node": "20.2.5",
|
||||
"@types/react": "18.2.8",
|
||||
"@types/react-dom": "18.2.4",
|
||||
"autoprefixer": "10.4.14",
|
||||
"axios": "^1.4.0",
|
||||
"bcrypt": "^5.1.0",
|
||||
"eslint": "8.42.0",
|
||||
"eslint-config-next": "13.4.4",
|
||||
"lodash": "^4.17.21",
|
||||
"next": "13.4.4",
|
||||
"next-auth": "^4.22.1",
|
||||
"postcss": "8.4.24",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-icons": "^4.8.0",
|
||||
"swr": "^2.1.5",
|
||||
"tailwindcss": "3.3.2",
|
||||
"typescript": "5.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/lodash": "^4.14.195",
|
||||
"prisma": "^4.13.0"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import {NextApiRequest,NextApiResponse} from "next";
|
||||
|
||||
import prismadb from "@/lib/prismadb";
|
||||
import serverAuth from "@/lib/serverAuth";
|
||||
|
||||
export default async function handler(req: NextApiRequest,res: NextApiResponse) {
|
||||
if(req.method !== "GET"){
|
||||
return res.status(405).end();
|
||||
}
|
||||
try{
|
||||
await serverAuth(req);
|
||||
const movies = await prismadb.movie.findMany();
|
||||
return res.status(200).json(movies);
|
||||
}catch(error){
|
||||
console.log(error);
|
||||
return res.status(400).end();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import prismadb from "@/lib/prismadb";
|
||||
import serverAuth from "@/lib/serverAuth";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (req.method !== 'GET') {
|
||||
return res.status(405).end
|
||||
}
|
||||
try {
|
||||
await serverAuth(req);
|
||||
const movieCount = await prismadb.movie.count();
|
||||
const randomIndex = Math.floor(Math.random() * movieCount);
|
||||
const randomMovies = await prismadb.movie.findMany({
|
||||
take: 1,
|
||||
skip: randomIndex
|
||||
});
|
||||
return res.status(200).json(randomMovies[0])
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return res.status(400).end()
|
||||
}
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
import Billboard from "@/components/Billboard"
|
||||
import MovieList from "@/components/MovieList"
|
||||
import Navbar from "@/components/Navbar"
|
||||
import useCurrentUser from "@/hooks/useCurrentUser"
|
||||
import useMoviesList from "@/hooks/useMoviesList"
|
||||
import { NextPageContext } from "next"
|
||||
import { signOut,getSession } from "next-auth/react"
|
||||
|
||||
|
@ -20,9 +23,14 @@ export async function getServerSideProps(context: NextPageContext){
|
|||
|
||||
|
||||
export default function Home() {
|
||||
const { data: film = [] } = useMoviesList();
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<Billboard />
|
||||
<div>
|
||||
<MovieList title="Popolari" data={film} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue