In this tutorial we have integrate other Api. Previously we did 1st part and 2nd part. If you don’t know then see this link
Now we are do 3rd part. Let’s derive on-
Step-1 : Authentication Api Setup
Go to reduxStore/features/auth folder and create this file
- authApi.js
- authSlice.js
authApi.js
import { apiSlice } from "../api/apiSlice";
import { userLoggedIn } from "./authSlice";
export const authApi = apiSlice.injectEndpoints({
endpoints: (builder) => ({
register: builder.mutation({
query: (data) => ({
url: "/register",
method: "POST",
body: data,
}),
async onQueryStarted(arg, { queryFulfilled, dispatch }) {
try {
const result = await queryFulfilled;
localStorage.setItem(
"auth",
JSON.stringify({
accessToken: result.data.accessToken,
user: result.data.user,
})
);
dispatch(
userLoggedIn({
accessToken: result.data.accessToken,
user: result.data.user,
})
);
} catch (err) {
// do nothing
}
},
}),
login: builder.mutation({
query: (data) => ({
url: "/login",
method: "POST",
body: data,
}),
async onQueryStarted(arg, { queryFulfilled, dispatch }) {
try {
const result = await queryFulfilled;
console.log('data fetch', result)
localStorage.setItem(
"auth",
JSON.stringify({
accessToken: result.data.payload.token,
user:result.data.payload,
})
);
dispatch(
userLoggedIn({
accessToken: result.data.payload.token,
user: result.data.payload,
})
);
} catch (err) {
// do nothing
}
},
}),
}),
});
export const { useLoginMutation, useRegisterMutation } = authApi;
Here this authApi we have 2 end points.
1. "/register" for register user
2. "/login" for login user.
authSlice.js
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
accessToken: undefined,
user: undefined,
};
const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
userLoggedIn: (state, action) => {
state.accessToken = action.payload.accessToken;
state.user = action.payload.user;
},
userLoggedOut: (state) => {
state.accessToken = undefined;
state.user = undefined;
},
},
});
export const { userLoggedIn, userLoggedOut } = authSlice.actions;
export default authSlice.reducer;
Here this authSlice we have two actions.
- userLoggedIn
- userLoggedOut.
Step-2 : Login page Setup and Others
Go to login folder and create page.js file and do this
page.js
"use client";
import { useRouter } from 'next/navigation';
import { useEffect, useState } from "react";
import { useLoginMutation } from "../reduxStore/features/auth/authApi";
import Error from "../component/common/Error";
const Page = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [login, { data, isLoading, error: responseError }] = useLoginMutation();
const router = useRouter();
console.log('login data', data)
const handleSubmit = (e) => {
e.preventDefault();
console.log("loging data", email, password)
setError("");
login({
email,
password,
});
router.push("/");
};
useEffect(() => {
if (responseError?.data) {
setError(responseError.data);
}
if (data?.accessToken && data?.user) {
router.push("/");
}
}, [data, responseError, router]);
return (
<div className="flex h-screen items-center justify-center bg-gray-100">
<div className="bg-white p-8 shadow-md rounded-lg w-80">
<h1 className="text-2xl font-semibold mb-4">Login </h1>
<form onSubmit={handleSubmit}>
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700">Email</label>
<input
type="email"
className="mt-1 block w-full border rounded-md px-3 py-2 text-red-800"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700">
Password
</label>
<input
type="password"
className="mt-1 block w-full border rounded-md px-3 py-2 text-red-800"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<button
type="submit"
className="bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600"
>
Login
</button>
</form>
{error !== "" && <Error message={error} />}
</div>
</div>
);
};
export default Page;
Hit “/login” URL show the Login page.
Now go to “/component/common/LeftSideBar.js. Here do this-
'use client';
import { AuthCheck } from '@/app/hooks/AuthCheck';
import { userLoggedOut } from '@/app/reduxStore/features/auth/authSlice';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { useDispatch } from 'react-redux';
import ComingSoon from "../icons/ComingSoon";
import Community from "../icons/Community";
import Friends from "../icons/Friends";
import Home from "../icons/Home";
import Logout from "../icons/Logout";
import MakeMovie from "../icons/MakeMovie";
import Media from "../icons/Media";
import MovieLogo from "../icons/MovieLogo";
import Profile from "../icons/Profile";
import Settings from "../icons/Settings";
const LiftSideBar = () => {
const isAuthenticated = AuthCheck();
const dispatch = useDispatch();
const router = useRouter();
const logout = () => {
dispatch(userLoggedOut());
localStorage.clear();
router.push("/login");
};
return (
<aside className=" w-1/6 py-10 pl-10 min-w-min border-r border-gray-300 dark:border-zinc-700 hidden md:block ">
<Link href="/">
<div className=" font-bold text-lg flex items-center gap-x-3">
<MovieLogo />
<div className="tracking-wide dark:text-white">
RMovie<span className="text-red-600">.</span>
</div>
</div>
</Link>
<div className="mt-12 flex flex-col gap-y-4 text-gray-500 fill-gray-500 text-sm">
<div className="text-gray-400/70 font-medium uppercase">Menu</div>
<Link
href="/"
className="flex items-center space-x-2 py-1 dark:text-white font-semibold border-r-4 border-r-red-600 pr-20 "
>
<Home />
<span>Home</span>
</Link>
<Link
href="/ai-make-movie"
className="flex items-center space-x-2 py-1 group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white"
>
<MakeMovie />
<span>Ai Make Movie</span>
</Link>
<Link
href="/manual-make-movie"
className="flex items-center space-x-2 py-1 group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white"
>
<MakeMovie />
<span>Manual Make Movie</span>
</Link>
<a
className=" flex items-center space-x-2 py-1 group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white "
href="#"
>
<Community />
<span>Community</span>
</a>
<a
className=" flex items-center space-x-2 py-1 group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white "
href="#"
>
<ComingSoon />
<span>Coming Soon</span>
</a>
<div className="mt-8 text-gray-400/70 font-medium uppercase">
Social
</div>
<a
className=" flex items-center space-x-2 py-1 group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white "
href="#"
>
<Profile />
<span>Profile</span>
</a>
<a
className=" flex items-center space-x-2 py-1 group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white "
href="#"
>
<Friends />
<span>Friends</span>
</a>
<Link
className=" flex items-center space-x-2 py-1 group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white "
href="/make-category"
>
<Media />
<span>Make Category</span>
</Link>
<div className="mt-8 text-gray-400/70 font-medium uppercase">
General
</div>
<a
className=" flex items-center space-x-2 py-1 group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white "
href="#"
>
<Settings />
<span>Settings</span>
</a>
{ isAuthenticated && (
<a
className=" flex items-center space-x-2 py-1 group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white"
href="#"
onClick={logout}
>
<Logout />
<span>Logout</span>
</a>
)}
{ !isAuthenticated && (
<Link href="/login"
className=" flex items-center space-x-2 py-1 group hover:border-r-4 hover:border-r-red-600 hover:font-semibold dark:hover:text-white"
>
<Logout />
<span>login {AuthCheck}</span>
</Link>
)}
</div>
</aside>
);
};
export default LiftSideBar;
Here we have Two action hook.
- Login Button
- Logout Button.
Step-3 : Category Api Setup
Go to reduxStore/features/category folder and create this file
- categoryApi.js
- categorySlice.js
categoryApi.js
import { apiSlice } from "../api/apiSlice";
export const categoryApi = apiSlice.injectEndpoints({
endpoints: (builder) => ({
categoryWiseMovies: builder.query({
query: () =>
`/category-wise-movies`,
}),
allCategories: builder.query({
query: () =>
`/all-category`,
}),
categoryStore: builder.mutation({
query: (data) => ({
url: "/category-store",
method: "POST",
body: data,
}),
})
}),
});
export const { useCategoryWiseMoviesQuery, useAllCategoriesQuery, useCategoryStoreMutation} = categoryApi;
Here we have Three end point. There are
/category-wise-movies
/all-category
/category-store
categorySlice.js
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
topMovies: [],
};
const categorySlice = createSlice({
name: "movie",
initialState,
reducers: {
topMovies: (state, action) => {
state.topMovies = action.payload;
}
},
});
export const { topMovies} = categorySlice.actions;
export default categorySlice.reducer;
Here we have one action “topMovies”.
Step-4: Category Page Setup and Others
Go to make-category folder and we can see page.js, now write this-
page.js
import LeftSideBar from "../component/common/LeftSideBar";
import RightSideBar from "../component/common/RightSideBar";
import AiFormInput from "../component/make-category/FormInput";
import ProtectedRoute from "../hooks/ProtectedRoute";
const Page = () => {
return (
<ProtectedRoute>
<div className="flex min-h-screen">
<LeftSideBar />
<div className=" flex-1 py-10 px-5 sm:px-10 ">
<AiFormInput />
</div>
<RightSideBar />
</div>
</ProtectedRoute>
);
};
export default Page;
and go to component/make-category and see FormInput.js. Below this write-
FormInput.js
'use client';
import { useCategoryStoreMutation } from "@/app/reduxStore/features/category/categoryApi";
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { useState } from "react";
const FormInput = () => {
const [categoryStore] = useCategoryStoreMutation();
const router = useRouter();
const [categoryData, setCategoryData] = useState({
title: '',
image: null,
});
const handleChange = (e) => {
const { name, value } = e.target;
setCategoryData({ ...categoryData, [name]: value });
};
const handleFileChange = (e) => {
const file = e.target.files[0];
setCategoryData({ ...categoryData, image: file });
};
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
formData.append('name', categoryData.title);
formData.append('image', categoryData.image);
console.log('categry submit form', categoryData)
try {
const response = await categoryStore(formData);
// Handle the success response
console.log('Response from server:', response.data);
// Assuming you want to reset the form after successful submission
setCategoryData({
name: '',
image: null,
});
router.push("/");
} catch (error) {
console.error('Error saving the movie:', error);
}
};
return (
<div className="bg-gray-200 p-5 rounded-md dark:bg-black">
<div className="text-center mb-8 relative">
<h1 className="text-2xl font-bold text-gray-700 mb-3 dark:text-white">
Create Category
</h1>
<Image
src="/AS0007464_07.gif"
alt="form"
className="rounded-full absolute -top-5 right-0 sm::hidden"
width={105}
height={105}
/>
</div>
<form onSubmit={handleSubmit}>
<div className="mx-10 px-10 py-16 bg-gray-400 dark:bg-black dark:border rounded-md">
<div className="mb-6">
<label
htmlFor="large-input"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
Category Name
</label>
<input
type="text"
id="large-input"
className="block w-full p-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Type your Category name..."
name="title"
onChange={handleChange}
/>
</div>
<div className="mb-6">
<label
htmlFor="large-input"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
Select Category Image
</label>
<input
type="file"
id="large-input"
className="block w-full p-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
name="image"
onChange={handleFileChange}
/>
</div>
<div className="text-right">
<button
type="submit"
className="bg-black text-white p-3 rounded-md dark:border"
>
Make Category
</button>
</div>
</div>
</form>
</div>
);
};
export default FormInput;
Now “/make-category” URL show this page and we can create category.
Step-5: movie Api Setup
Go to reduxStore/features/movie folder and create this file
- movieApi.js
- movieSlice.js
movieApi.js
import { apiSlice } from "../api/apiSlice";
export const moviesApi = apiSlice.injectEndpoints({
endpoints: (builder) => ({
topMovies: builder.query({
query: () =>
`/top-movies`,
}),
manualMovieStore: builder.mutation({
query: (data) => ({
url: "/movie-store",
method: "POST",
body: data,
}),
}),
aiMovieStore: builder.mutation({
query: (data) => ({
url: "/ai-movie-store",
method: "POST",
body: data,
}),
async onQueryStarted(arg, { queryFulfilled, dispatch }) {
console.log('redux', arg)
},
}),
}),
});
export const { useTopMoviesQuery, useManualMovieStoreMutation, useAiMovieStoreMutation} = moviesApi;
Here we have Three end points. There are
- "/top-movies"
- "/movie-store"
- "/ai-movie-store"
movieSlice.js
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
topMovies: [],
aiMovieData:{},
sampleData:"sdfsf sdfsf"
};
const movieSlice = createSlice({
name: "movie",
initialState,
reducers: {
topMovies: (state, action) => {
state.topMovies = action.payload;
},
aiMovieData:(state, action) =>{
console.log('redux movie')
state.aiMovieData = action.payload;
}
},
});
export const { topMovies, aiMovieData} = movieSlice.actions;
export default movieSlice.reducer;
Here we have Two actions.
- topMovies
- aiMovieData
Step-6: Manual Movie Page Setup and Others
Go to manual-make-movie folder and see page.js file. Write this code-
import LeftSideBar from "../component/common/LeftSideBar";
import RightSideBar from "../component/common/RightSideBar";
import FormInput from "../component/manual-make-movie/FormInput";
const Page = () => {
return (
<div className="flex min-h-screen">
<LeftSideBar />
<div className=" flex-1 py-10 px-5 sm:px-10 ">
<FormInput />
</div>
<RightSideBar />
</div>
);
};
export default Page;
Now go to component/manual-make-movie and write this
FormInput.js
'use client';
import { useAllCategoriesQuery } from "@/app/reduxStore/features/category/categoryApi";
import { useManualMovieStoreMutation } from "@/app/reduxStore/features/movie/moviesApi";
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { useState } from "react";
const FormInput = () => {
const { data, isLoading, isError } = useAllCategoriesQuery();
const [manualMovieStore, { isSuccess }] = useManualMovieStoreMutation();
const router = useRouter();
console.log("all manualMovieStore", manualMovieStore)
const [movieData, setMovieData] = useState({
title: '',
category_id: '',
description: '',
image: null,
});
const handleChange = (e) => {
const { name, value } = e.target;
setMovieData({ ...movieData, [name]: value });
};
const handleFileChange = (e) => {
const file = e.target.files[0];
setMovieData({ ...movieData, image: file });
};
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
formData.append('title', movieData.title);
formData.append('category_id', movieData.category_id);
formData.append('description', movieData.description);
formData.append('image', movieData.image);
console.log('form submit', movieData)
try {
const response = await manualMovieStore(formData);
// Handle the success response
console.log('Response from server:', response.data);
// Assuming you want to reset the form after successful submission
setMovieData({
title: '',
category_id: '',
description: '',
image: null,
});
router.push("/");
} catch (error) {
// Handle the error
console.error('Error saving the movie:', error);
}
};
if (isLoading) {
return <div>Loading...</div>;
}
if (isError) {
return <div>Error fetching categories.</div>;
}
return (
<div>
<div className="text-center mb-8 relative">
<h1 className="text-2xl font-bold text-gray-700 mb-3 dark:text-white">
Manual Movie Maker
</h1>
<Image
src="/storytelling-08.gif"
alt="input"
className="rounded-full w-48 h-48 absolute -top-5 right-0 sm::hidden"
width={200}
height={200}
/>
</div>
<form onSubmit={handleSubmit}>
<div className="mx-10 px-10 py-16 bg-gray-400 dark:bg-black dark:border rounded-md">
<div className="mb-6">
<label
htmlFor="large-input"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
Movie Title
</label>
<input
type="text"
id="large-input"
className="block w-full p-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Type your Movie Title..."
name="title"
onChange={handleChange}
/>
</div>
<div className="mb-6">
<label
htmlFor="base-input"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
Movie Category
</label>
<select
name="category_id"
onChange={handleChange}
id="category"
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
>
<option value="">Choose category</option>
{data && data.data?.map((category) => (
<option key={category._id} value={category._id}>
{category.name}
</option>
))}
</select>
</div>
<div className="mb-6">
<label
htmlFor="message"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
Movie Short Description
</label>
<textarea
id="message"
rows={4}
className="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Type Movie short description..."
v-model="movieData.description"
defaultValue={""}
name="descriptin"
onChange={handleChange}
/>
</div>
<div className="mb-6">
<label
htmlFor="large-input"
className="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
>
Select Movie image
</label>
<input
type="file"
id="large-input"
className="block w-full p-4 text-gray-900 border border-gray-300 rounded-lg bg-gray-50 sm:text-md focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Type your Movie Title..."
name="image"
onChange={handleFileChange}
/>
</div>
<div className="text-right">
<button
type="submit"
className="bg-black text-white p-3 rounded-md dark:border"
>
Make Movie
</button>
</div>
</div>
</form>
</div>
);
};
export default FormInput;
Now we are making manual movie form. If you hit “/manual-make-movie” URL then you see this page.
We can now create any movie using this form.
Step-7 : Home Page setup
Go to app folder and see page.js file. Write this code
import LeftSideBar from "./component/common/LeftSideBar";
import RightSideBar from "./component/common/RightSideBar";
import CategoryWiseMovies from "./component/index/CategoryWiseMovies";
import HeroSection from "./component/index/HeroSection";
import TopMovies from "./component/index/TopMovies";
export default function Home() {
return (
<div className="flex min-h-screen">
<LeftSideBar />
<div className=" flex-1 py-10 px-5 sm:px-10 ">
<HeroSection />
<TopMovies />
<CategoryWiseMovies />
</div>
<RightSideBar />
</div>
)
}
Now Go to component/index Folder and see TipMovie.js file. Write below this code-
'use client';
import { useTopMoviesQuery } from "@/app/reduxStore/features/movie/moviesApi";
import Image from 'next/image';
import ImdbLogo from "../icons/ImdbLogo";
import NextPrev from "../icons/NextPrev";
const TopMovies = () => {
const { data, error, isLoading } = useTopMoviesQuery();
console.log("movie data", data)
if (isLoading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error.message}</div>;
}
const getRandomNumber = () => {
return Math.floor(Math.random() * 10) + 1;
};
return (
<section className="mt-9 bg-lime-300 dark:bg-rose-500 p-5 rounded-md">
<div className="flex items-center justify-between">
<span className="font-semibold text-gray-700 text-base dark:text-white">
My Favourite Movies and Collections
</span>
<div className="flex items-center space-x-2 fill-gray-500">
<NextPrev />
</div>
</div>
<div className="mt-4 grid grid-cols-2 sm:grid-cols-4 gap-x-5 gap-y-5">
{data.data && data.data.map((movie, index) => (
<div
key={index}
className="flex flex-col rounded-xl overflow-hidden aspect-square border dark:border-zinc-600"
>
<div className="w-full h-1/5 bg-white dark:bg-zinc-800 dark:text-white px-3 flex items-center justify-between border-t-2 border-t-red-600">
<span className="capitalize font-medium truncate">{movie.title}</span>
<div className="flex space-x-2 items-center text-xs">
<ImdbLogo />
<span>{getRandomNumber()}</span>
</div>
</div>
<Image
src={movie.image}
className="h-4/5 object-cover w-full"
alt={movie.title}
width={105}
height={105}
layout="responsive"
/>
</div>
))}
</div>
{isLoading && isLoading}
{error && error}
</section>
);
};
export default TopMovies;
Now same Folder we can see CategoryWiseMovies.js file. Write this code-
/* eslint-disable react/jsx-key */
'use client';
import { useCategoryWiseMoviesQuery } from "@/app/reduxStore/features/category/categoryApi";
import Image from 'next/image';
import ImdbLogo from "../icons/ImdbLogo";
import NextPrev from "../icons/NextPrev";
const CategoryWiseMovies = () => {
const { data, error, isLoading } = useCategoryWiseMoviesQuery();
console.log('category wise movie data', data)
const getRandomColor = () => {
const colors = ['#FF5733', '#33FF57', '#5733FF', '#33FFFF', '#FF33FF', '#FFFF33'];
return colors[Math.floor(Math.random() * colors.length)];
};
const getRandomNumber = () => {
return Math.floor(Math.random() * 10) + 1;
};
if (isLoading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error.message}</div>;
}
return (
<>
{data.data.map((category, indexOne) => (
<section className="mt-9 bg-blue-400 dark:bg-black p-5 rounded-md" style={{ backgroundColor: getRandomColor() }} key={indexOne}>
<div className="flex items-center justify-between">
<span className="font-semibold text-gray-700 text-base dark:text-white">{category.name} Movies</span>
<div className="flex items-center space-x-2 fill-gray-500">
<NextPrev />
</div>
</div>
<div className="mt-4 grid grid-cols-2 gap-y-5 sm:grid-cols-3 gap-x-5 ">
{category.movies.map((movie, indexTwo) => (
<div className="flex flex-col rounded-xl overflow-hidden aspect-square border dark:border-zinc-600" key={indexTwo}>
<div className="w-full h-1/5 bg-white dark:bg-zinc-800 dark:text-white px-3 flex items-center justify-between border-t-2 border-t-red-600">
<span className="capitalize font-medium truncate">{movie.title}</span>
<div className="flex space-x-2 items-center text-xs">
<ImdbLogo />
<span>{getRandomNumber()}</span>
</div>
</div>
<Image
src={movie.image}
className=" h-4/5 object-cover w-full"
alt={movie.title}
width={105}
height={105}
layout="responsive"
/>
</div>
))}
</div>
</section>
))}
</>
);
};
export default CategoryWiseMovies;
Now ready home page. If we are hit http://localhost:3000/ then we can see our home page.
If your database is empty then you don't see any movie 😊
Next we set up AI Prompt. Here is this Tutorial-
AI Prompt setup on Nextjs. Link
full Project github
Node
https://github.com/kamruzzamanripon/node-movie-api
NextJs
https://github.com/kamruzzamanripon/next-movie-ui-with-node-api
NextJs UI
https://github.com/kamruzzamanripon/next-movie-ui
That's all. Happy Learning :) .
[if it is helpful, giving a star to the repository 😇]
Top comments (0)