From afd9170d6dec539dc85cb04775992e37f50ddd71 Mon Sep 17 00:00:00 2001 From: qpismont Date: Mon, 1 Jul 2024 23:33:36 +0200 Subject: [PATCH] add tmdb search --- src/components/DebouceInput.tsx | 53 +++++++++++++++++++ src/components/TmdbSearch.tsx | 27 ++++++++++ src/components/TmdbSearchInput.tsx | 34 ++++++++++++ .../lists/movies/GroupMoviesList.tsx | 18 +++++++ .../lists/movies/GroupMoviesListItem.tsx | 43 +++++++++++++++ src/pages/api/tmdb/movies/search.ts | 20 +++++++ src/pages/home/settings/index.astro | 2 +- src/pages/home/settings/movies.astro | 21 ++++++++ src/types.ts | 7 +++ 9 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 src/components/DebouceInput.tsx create mode 100644 src/components/TmdbSearch.tsx create mode 100644 src/components/TmdbSearchInput.tsx create mode 100644 src/components/lists/movies/GroupMoviesList.tsx create mode 100644 src/components/lists/movies/GroupMoviesListItem.tsx create mode 100644 src/pages/api/tmdb/movies/search.ts create mode 100644 src/types.ts diff --git a/src/components/DebouceInput.tsx b/src/components/DebouceInput.tsx new file mode 100644 index 0000000..e691831 --- /dev/null +++ b/src/components/DebouceInput.tsx @@ -0,0 +1,53 @@ +import type React from "react"; +import { useEffect, useRef, useState } from "react"; + +interface DebounceProps { + debouceDelay: number; + onDebounce: (value: string) => void; +} + +type DebouceInputProps = DebounceProps & + React.InputHTMLAttributes; + +export default function DebounceInput(props: DebouceInputProps) { + const [value, setValue] = useState(""); + + const debounceTimeout = useRef(null); + + useEffect(() => { + if (typeof props.value === "string") { + setValue(props.value); + } else { + setValue(""); + } + }, [props.value]); + + function handleOnChange(e: React.ChangeEvent) { + if (debounceTimeout.current) { + clearTimeout(debounceTimeout.current); + } + + setValue(e.target.value); + + debounceTimeout.current = setTimeout( + () => props.onDebounce(e.target.value), + props.debouceDelay, + ); + } + + const inputAttr = { + ...props, + debouceDelay: undefined, + onDebounce: undefined, + }; + + return ( + + ); +} diff --git a/src/components/TmdbSearch.tsx b/src/components/TmdbSearch.tsx new file mode 100644 index 0000000..b7f9b01 --- /dev/null +++ b/src/components/TmdbSearch.tsx @@ -0,0 +1,27 @@ +import { useState } from "react"; +import type { Movie } from "../types"; +import TmdbSearchInput from "./TmdbSearchInput"; +import GroupMoviesList from "./lists/movies/GroupMoviesList"; + +export default function TmdbSearch() { + const [movies, setMovies] = useState([]); + + function handleOnSearch(movies: Movie[]) { + setMovies(movies); + } + + return ( + <> +
+
+ +
+
+
+
+ +
+
+ + ); +} diff --git a/src/components/TmdbSearchInput.tsx b/src/components/TmdbSearchInput.tsx new file mode 100644 index 0000000..d2c0191 --- /dev/null +++ b/src/components/TmdbSearchInput.tsx @@ -0,0 +1,34 @@ +import type { Movie } from "../types"; +import DebounceInput from "./DebouceInput"; + +interface TmdbSearchInputProps { + onSearch: (movies: Movie[]) => void; +} + +export default function TmdbSearchInput({ onSearch }: TmdbSearchInputProps) { + function handleOnSearchInputDebounce(value: string) { + fetch(`/api/tmdb/movies/search?query=${value}`, { + method: "GET", + credentials: "include", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + }) + .then((res) => res.json()) + .then((json) => { + onSearch(json.movies); + }) + .catch((err) => console.log(err)); + onSearch([]); + } + + return ( + + ); +} diff --git a/src/components/lists/movies/GroupMoviesList.tsx b/src/components/lists/movies/GroupMoviesList.tsx new file mode 100644 index 0000000..f4f2f25 --- /dev/null +++ b/src/components/lists/movies/GroupMoviesList.tsx @@ -0,0 +1,18 @@ +import type { Movie } from "../../../types"; +import GroupMoviesListItem from "./GroupMoviesListItem"; + +interface GroupMoviesListProps { + movies: Movie[]; + onClick?: (movie: Movie) => void; +} + +export default function GroupMoviesList({ + movies, + onClick, +}: GroupMoviesListProps) { + const items = movies.map((elt) => ( + + )); + + return
{items}
; +} diff --git a/src/components/lists/movies/GroupMoviesListItem.tsx b/src/components/lists/movies/GroupMoviesListItem.tsx new file mode 100644 index 0000000..8733fb4 --- /dev/null +++ b/src/components/lists/movies/GroupMoviesListItem.tsx @@ -0,0 +1,43 @@ +import type { Movie } from "../../../types"; + +interface GroupMoviesListItemProps { + item: Movie; + onClick?: (movie: Movie) => void; +} + +export default function GroupMoviesListItem({ + item, + onClick, +}: GroupMoviesListItemProps) { + function handleOnClick(e: React.MouseEvent) { + e.stopPropagation(); + e.preventDefault(); + + if (onClick) onClick(item); + } + + return ( + + ); +} diff --git a/src/pages/api/tmdb/movies/search.ts b/src/pages/api/tmdb/movies/search.ts new file mode 100644 index 0000000..73e270d --- /dev/null +++ b/src/pages/api/tmdb/movies/search.ts @@ -0,0 +1,20 @@ +import type { APIRoute } from "astro"; + +export const GET: APIRoute = async ({ request, cookies }) => { + const jwt = cookies.get("jwt")?.value as string; + const url = new URL(request.url); + + const res = await fetch( + `${import.meta.env.API_URL}/tmdb/movies/search?query=${url.searchParams.get("query")}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${jwt}`, + }, + }, + ); + + const resBody = await res.text(); + return new Response(resBody, { status: res.status }); +}; diff --git a/src/pages/home/settings/index.astro b/src/pages/home/settings/index.astro index 2925f80..f792e19 100644 --- a/src/pages/home/settings/index.astro +++ b/src/pages/home/settings/index.astro @@ -26,7 +26,7 @@ import HomeLayout from "../../../layouts/HomeLayout.astro"; Movies Add movie diff --git a/src/pages/home/settings/movies.astro b/src/pages/home/settings/movies.astro index e69de29..8c66554 100644 --- a/src/pages/home/settings/movies.astro +++ b/src/pages/home/settings/movies.astro @@ -0,0 +1,21 @@ +--- +import TmdbSearch from "../../../components/TmdbSearch"; +import HomeLayout from "../../../layouts/HomeLayout.astro"; +--- + + +
+
+
+

Add movie

+
+
+
+

Search on TMDB Database

+ +
+
+
+
+
+
diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..30676cd --- /dev/null +++ b/src/types.ts @@ -0,0 +1,7 @@ +export interface Movie { + id: number; + title: string; + overview: string; + poster_path: string; + release_date: string; +}