Skip to content

Commit

Permalink
Merge pull request #47 from nnivxix/feat/seo-meta
Browse files Browse the repository at this point in the history
Feat/seo meta
  • Loading branch information
nnivxix authored Oct 27, 2024
2 parents ae08b4f + edb1c1b commit 13fa99d
Show file tree
Hide file tree
Showing 44 changed files with 1,726 additions and 1,408 deletions.
30 changes: 12 additions & 18 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
],
ignorePatterns: ["dist", ".eslintrc.cjs"],
parser: "@typescript-eslint/parser",
};

64 changes: 54 additions & 10 deletions app/discover/page.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,67 @@
"use client"

import type { Response, MovieTv } from "../../src/types/response";
import useFetch from "@/hooks/useFetch";
import type { Response, MovieTv } from "@/types/response";
import CardItem from "@/components/CardItem";
// import "../../src/index.css";
import { cookies } from "next/headers";
import config from "@/config";
import { Metadata } from "next";

type Status = "idle" | "pending" | "success" | "error";

const { apiUrl, token } = config;

export default function Page() {
const { data: movies, isLoading } = useFetch<Response<MovieTv[]>>("/trending/all/day");
export const metadata: Metadata = {
title: "Vilm - Discover Movies and Tv Shows ",
description: "Discover movies and tv shows.",
};

if (isLoading) {
return "Loading..."
export default async function Page() {
const { data, status } = await getDiscover();

if (status === "pending") {
return "Loading...";
}
return (
<div>
<div className="grid lg:grid-cols-8 md:grid-cols-4 grid-cols-2 gap-5 mx-auto px-5 mt-5">
{movies?.results.map((movie: MovieTv) => (
{data?.results.map((movie: MovieTv) => (
<CardItem movie={movie} key={movie.id} />
))}
</div>
</div>
);
}

async function getDiscover(): Promise<{
data: Response<MovieTv[]> | null;
status: Status;
error: string | null;
}> {
const apiToken = cookies().get("API_TOKEN")?.value ?? token;
let status: Status = "idle";
let data: Response<MovieTv[]> | null = null;
let error: string | null = null;

status = "pending";
try {
const response = await fetch(`${apiUrl}/trending/all/day`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: `Bearer ${apiToken}`,
},
});

if (!response.ok) {
throw new Error("Failed to fetch the movie data");
}

data = await response.json();
status = "success";
} catch (err) {
console.error(err);
status = "error";
error = err instanceof Error ? err.message : "Unknown error";
}

return { data, status, error };
}
36 changes: 16 additions & 20 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,36 @@ import "../src/index.css";
import Navbar from "@/components/Navbar";
import Footer from "@/components/Footer";
import { Toaster } from "@/components/ui/toaster";
import { Metadata } from 'next';
import { Metadata } from "next";

// eslint-disable-next-line react-refresh/only-export-components
export const metadata: Metadata = {
title: 'Vilm',
description: 'Get movies and tv shows information.',
title: "Vilm",
description: "Get movies and tv shows information.",
openGraph: {
siteName: 'Vilm',
type: 'website',
url: 'https://vilm-react.vercel.app/',
title: 'Vilm',
description: 'Get movies and tv shows information.',
siteName: "Vilm",
type: "website",
url: "https://vilm-react.vercel.app/",
title: "Vilm",
description: "Get movies and tv shows information.",
images: [
{
url: '/og-image.jpg',
url: "/og-image.jpg",
},
],
},
twitter: {
card: 'summary_large_image',
creator: '@nnivxix',
title: 'Get movies and tv shows information.',
description: 'Get movies and tv shows information.',
images: ['/og-image.jpg'],
card: "summary_large_image",
creator: "@nnivxix",
title: "Get movies and tv shows information.",
description: "Get movies and tv shows information.",
images: ["/og-image.jpg"],
},
};


export default function RootLayout({
children,
}: {
children: React.ReactNode
children: React.ReactNode;
}) {
return (
<html lang="en" className="dark">
Expand Down Expand Up @@ -73,8 +71,6 @@ export default function RootLayout({
href="/backdrop-fallback.png"
type="image/png"
/>


</head>
<body>
<Navbar />
Expand All @@ -84,4 +80,4 @@ export default function RootLayout({
</body>
</html>
);
}
}
24 changes: 16 additions & 8 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"use client"
"use client";
import type { FormEvent } from "react";
import { buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { Suspense, useState } from "react";
import { Input } from "@/components/ui/input"
import { Input } from "@/components/ui/input";
import Link from "next/link";
import { useSearchParams } from "next/navigation";
import { useRouter } from "next/navigation";
Expand Down Expand Up @@ -40,9 +40,11 @@ function SearchForm() {
}

export default function Page() {

return (
<div className="lg:bg-[url('/home-banner.jpg')] bg-[url('/home-banner-vertical.jpg')] bg-center bg-cover" data-bg-src="https://www.pexels.com/photo/three-friends-watching-at-a-movie-theater-while-eating-popcorn-8263318/">
<div
className="lg:bg-[url('/home-banner.jpg')] bg-[url('/home-banner-vertical.jpg')] bg-center bg-cover"
data-bg-src="https://www.pexels.com/photo/three-friends-watching-at-a-movie-theater-while-eating-popcorn-8263318/"
>
<div className="mx-auto max-w-4xl px-5 mt-5">
<div className="flex w-full lg:h-[63vh] h-[45vh] md:h-[56vh] justify-center flex-col gap-3 items-center">
<div className="text-center">
Expand All @@ -52,10 +54,16 @@ export default function Page() {
<Suspense>
<SearchForm />
</Suspense>
<p>
Or
</p>
<Link href='/discover' className={`lg:w-1/2 w-full ${cn(buttonVariants({ variant: "default" }))}`}> Discover </Link>
<p>Or</p>
<Link
href="/discover"
className={`lg:w-1/2 w-full ${cn(
buttonVariants({ variant: "default" })
)}`}
>
{" "}
Discover{" "}
</Link>
</div>
</div>
</div>
Expand Down
128 changes: 21 additions & 107 deletions app/search/page.tsx
Original file line number Diff line number Diff line change
@@ -1,119 +1,33 @@
"use client"
import { SearchForm } from "@/components/SearchForm";
import { Suspense } from "react";

import CardItem from "@/components/CardItem";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import useFetch from "@/hooks/useFetch";
import useHead from "@/hooks/useHead";
import type { MovieTv, Response } from "@/types/response";
import { useSearchParams } from "next/navigation";
import { useRouter } from "next/navigation";
import { useEffect, useState, type FormEvent } from "react";
import { Suspense } from 'react'


function SearchForm() {
const searchParams = useSearchParams();
const router = useRouter();

const queryTitle = searchParams?.get("title");
const queryType = searchParams?.get("type");

const [results, setResults] = useState<MovieTv[]>([]);
const [title, setTitle] = useState<string>(queryTitle ?? "");
const [type, setType] = useState<string>(queryType ?? "movie");
interface Props {
searchParams: { [key: string]: string | string[] | undefined };
}

const { data } = useFetch<Response<MovieTv[]>>(
`/search/${queryType}?query=${queryTitle}`
);
export async function generateMetadata({ searchParams }: Props) {
const title = searchParams.title;
const type = () => {
const paramType = searchParams.type;

const changeType = (value: string) => {
setType(value);
router.push(`/search?title=${title}&type=${value}`);
window.scrollTo({
top: 0,
left: 0,
behavior: "smooth",
});
if (paramType === "tv") return "Tv Shows";
if (paramType === "movie") return "Movies";
return "";
};

const handleSearch = async (e: FormEvent) => {
e.preventDefault();
router.push(`/search?title=${title}&type=${type}`);
if (title) {
return {
title: `Vilm - Search ${type()} for "${title}" `,
};
}
return {
title: "Vilm - Search",
};

useHead({
title: `Vilm - Search ${type} "${title}"`,
meta: {
description: 'Here you can search movies or tv shows'
}
});
useEffect(() => {
if (!queryTitle && !queryType) {
router.push("/");
}

setType(queryType!);
setTitle(queryTitle!);
setResults(data?.results as MovieTv[]);
}, [data, router, queryTitle, queryType, results]);
return (
<div>
<form
onSubmit={handleSearch}
className="grid md:grid-cols-4 grid-cols-1 px-4 max-w-4xl mx-auto gap-2 mt-5 text-white mb-20 md:mb-16"
>
<Input
type="text"
value={title}
className="bg-slate-800 text-white md:col-span-3 col-span-1"
onChange={(e) => setTitle(e.target.value)}
/>
<Select value={type} onValueChange={(value) => changeType(value)}>
<SelectTrigger className="w-[180px] col-span-1 bg-slate-800">
<SelectValue placeholder="Select Media Type " />
</SelectTrigger>
<SelectContent className="bg-slate-800 text-white">
<SelectGroup>
<SelectItem className=" hover:bg-slate-600" value="movie">
Movie
</SelectItem>
<SelectItem className=" hover:bg-slate-600" value="tv">
Tv
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</form>
<div className="grid lg:grid-cols-8 md:grid-cols-4 grid-cols-2 gap-5 mx-auto px-5 mt-5">
{!!results?.length &&
results?.map((movie: MovieTv) => (
<CardItem media={queryType!} movie={movie} key={movie.id} />
))}
<div className="col-span-full py-3 mx-auto">
<p>
didn't found what you search?{" "}
<button
className="underline"
onClick={() => changeType(type === "movie" ? "tv" : "movie")}
>
please change type
</button>
</p>
</div>
{/* Fallback if results is null */}
</div>
</div>
);

}

export default function Page() {
export default async function Page() {
return (
<Suspense>
<SearchForm />
</Suspense>
)
);
}


Loading

0 comments on commit 13fa99d

Please sign in to comment.