Refactor movie filtering and management features: update MovieList and MovieCard components to use new filter props; enhance global store with updateMovie functionality; implement movie state updates for seen and favorite statuses.
This commit is contained in:
parent
b08cdea130
commit
3809110a39
|
|
@ -7,13 +7,18 @@ export default async function Home() {
|
|||
<SearchMovies />
|
||||
<MovieList
|
||||
heading="Upcoming"
|
||||
onlyUpcoming
|
||||
filterUpcoming={1}
|
||||
sort="releaseDate"
|
||||
sortDirection="desc"
|
||||
/>
|
||||
<MovieList heading="My Watchlist" onlyReleased showFilters />
|
||||
<MovieList heading="Seen" onlySeen />
|
||||
<MovieList heading="Favorites" onlyFavorites />
|
||||
<MovieList
|
||||
heading="My Watchlist"
|
||||
filterReleased={1}
|
||||
filterSeen={0}
|
||||
showFilters
|
||||
/>
|
||||
<MovieList heading="Seen" filterSeen={1} />
|
||||
<MovieList heading="Favorites" filterFavorites={1} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,20 @@
|
|||
import { movies } from "@/lib/db/schema";
|
||||
import { createContext, FC, use, useState } from "react";
|
||||
|
||||
type Movie = typeof movies.$inferSelect;
|
||||
|
||||
type GlobalStore = {
|
||||
movies: (typeof movies.$inferSelect)[];
|
||||
addMovie: (movie: typeof movies.$inferSelect) => void;
|
||||
movies: Movie[];
|
||||
addMovie: (movie: Movie) => void;
|
||||
deleteMovie: (id: number) => void;
|
||||
updateMovie: (id: number, movie: Partial<Movie>) => void;
|
||||
};
|
||||
|
||||
const globalStore = createContext<GlobalStore>({
|
||||
movies: [],
|
||||
addMovie: () => {},
|
||||
deleteMovie: () => {},
|
||||
updateMovie: () => {},
|
||||
});
|
||||
|
||||
type Props = {
|
||||
|
|
@ -22,7 +26,7 @@ type Props = {
|
|||
export const GlobalStoreProvider: FC<Props> = ({ children, initialMovies }) => {
|
||||
const [movies, setMovies] = useState<GlobalStore["movies"]>(initialMovies);
|
||||
|
||||
const addMovie = async (movie: GlobalStore["movies"][number]) => {
|
||||
const addMovie = async (movie: Movie) => {
|
||||
if (movies.find((m) => m.id === movie.id)) return;
|
||||
|
||||
setMovies((prev) => [...prev, movie]);
|
||||
|
|
@ -32,8 +36,16 @@ export const GlobalStoreProvider: FC<Props> = ({ children, initialMovies }) => {
|
|||
setMovies((prev) => prev.filter((m) => m.id !== id));
|
||||
};
|
||||
|
||||
const updateMovie = async (id: number, movie: Partial<Movie>) => {
|
||||
setMovies((prev) =>
|
||||
prev.map((m) => (m.id === id ? { ...m, ...movie } : m))
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<globalStore.Provider value={{ movies, addMovie, deleteMovie }}>
|
||||
<globalStore.Provider
|
||||
value={{ movies, addMovie, deleteMovie, updateMovie }}
|
||||
>
|
||||
{children}
|
||||
</globalStore.Provider>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
import { FC, useState } from "react";
|
||||
import { FC } from "react";
|
||||
import { ReadMore } from "../ReadMore";
|
||||
import { addMovie, deleteMovie } from "@/lib/db";
|
||||
import { addMovie, deleteMovie, updateMovie } from "@/lib/db";
|
||||
import { useGlobalStore } from "@/app/store/globalStore";
|
||||
|
||||
type Props = {
|
||||
|
|
@ -16,6 +16,8 @@ type Props = {
|
|||
notes?: string;
|
||||
};
|
||||
|
||||
const buttonClass = "px-2 py-6 text-sm w-full transition-colors cursor-pointer";
|
||||
|
||||
export const MovieCard: FC<Props> = ({
|
||||
id,
|
||||
title,
|
||||
|
|
@ -31,9 +33,12 @@ export const MovieCard: FC<Props> = ({
|
|||
movies,
|
||||
addMovie: addMovieToStore,
|
||||
deleteMovie: deleteMovieFromStore,
|
||||
updateMovie: updateMovieInStore,
|
||||
} = useGlobalStore();
|
||||
const alreadyInStore = movies.find((m) => m.id === id);
|
||||
|
||||
const isReleased = new Date(releaseDate) < new Date();
|
||||
|
||||
const handleAdd = async () => {
|
||||
const movie = {
|
||||
id,
|
||||
|
|
@ -55,6 +60,16 @@ export const MovieCard: FC<Props> = ({
|
|||
deleteMovieFromStore(id);
|
||||
};
|
||||
|
||||
const handleSeen = async () => {
|
||||
await updateMovie(id, { seen: seen ? 0 : 1 });
|
||||
updateMovieInStore(id, { seen: seen ? 0 : 1 });
|
||||
};
|
||||
|
||||
const handleFavorite = async () => {
|
||||
await updateMovie(id, { favorite: favorite ? 0 : 1 });
|
||||
updateMovieInStore(id, { favorite: favorite ? 0 : 1 });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex w-full shadow-md rounded-lg overflow-hidden mx-auto group/card">
|
||||
<div className="overflow-hidden rounded-xl relative movie-item text-white movie-card">
|
||||
|
|
@ -85,22 +100,41 @@ export const MovieCard: FC<Props> = ({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute top-0 z-10 bg-transparent inset-x-0 group-hover/card:bg-white/50 transition-all opacity-0 group-hover/card:opacity-100 flex justify-center">
|
||||
{/* <div className="absolute top-0 z-10 bg-transparent inset-x-0 group-hover/card:bg-white/50 transition-all opacity-0 group-hover/card:opacity-100 flex flex-col justify-center"> */}
|
||||
<div className="absolute top-0 z-10 bg-transparent inset-0 group-hover/card:bg-black/50 transition-all opacity-0 group-hover/card:opacity-100 flex flex-col justify-center">
|
||||
{!alreadyInStore && (
|
||||
<button
|
||||
className="bg-primary/70 text-white px-4 py-2 rounded-md w-full hover:bg-primary transition-colors cursor-pointer"
|
||||
className={`${buttonClass} bg-primary/70 text-white hover:bg-primary`}
|
||||
onClick={handleAdd}
|
||||
>
|
||||
Add to watchlist
|
||||
</button>
|
||||
)}
|
||||
{alreadyInStore && (
|
||||
<button
|
||||
className="bg-primary/70 text-white px-4 py-2 rounded-md w-full hover:bg-primary transition-colors cursor-pointer"
|
||||
onClick={handleRemove}
|
||||
>
|
||||
Remove from watchlist
|
||||
</button>
|
||||
<>
|
||||
{isReleased && (
|
||||
<button
|
||||
className={`${buttonClass} bg-accent/70 text-white hover:bg-accent`}
|
||||
onClick={handleSeen}
|
||||
>
|
||||
{seen ? "Mark as unseen" : "Mark as seen"}
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
className={`${buttonClass} bg-amber-400/70 text-white hover:bg-amber-500`}
|
||||
onClick={handleFavorite}
|
||||
>
|
||||
{favorite ? "Remove favorite" : "Add to favorites"}
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`${buttonClass} bg-primary/70 text-white hover:bg-primary`}
|
||||
onClick={handleRemove}
|
||||
>
|
||||
Remove from watchlist
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<figure className="absolute inset-0 w-full bottom-[20%]">
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import { useGlobalStore } from "@/app/store/globalStore";
|
|||
|
||||
type Props = {
|
||||
heading: string;
|
||||
onlySeen?: boolean;
|
||||
onlyFavorites?: boolean;
|
||||
onlyUpcoming?: boolean;
|
||||
onlyReleased?: boolean;
|
||||
filterSeen?: 0 | 1;
|
||||
filterFavorites?: 0 | 1;
|
||||
filterUpcoming?: 0 | 1;
|
||||
filterReleased?: 0 | 1;
|
||||
|
||||
showFilters?: boolean;
|
||||
sort?: "title" | "releaseDate" | "popularity";
|
||||
|
|
@ -17,10 +17,10 @@ type Props = {
|
|||
|
||||
export const MovieList: FC<Props> = ({
|
||||
heading,
|
||||
onlyFavorites,
|
||||
onlySeen,
|
||||
onlyUpcoming,
|
||||
onlyReleased,
|
||||
filterSeen,
|
||||
filterFavorites,
|
||||
filterUpcoming,
|
||||
filterReleased,
|
||||
showFilters = false,
|
||||
sort = "title",
|
||||
sortDirection = "asc",
|
||||
|
|
@ -31,11 +31,15 @@ export const MovieList: FC<Props> = ({
|
|||
const { movies } = useGlobalStore();
|
||||
|
||||
const filteredMovies = movies.filter((movie) => {
|
||||
if (onlySeen) return movie.seen === 1;
|
||||
if (onlyFavorites) return movie.favorite === 1;
|
||||
if (onlyUpcoming) return new Date(movie.releaseDate) > new Date();
|
||||
if (onlyReleased) return new Date(movie.releaseDate) < new Date();
|
||||
return true;
|
||||
let result = true;
|
||||
if (typeof filterSeen === "number") result = movie.seen === filterSeen;
|
||||
if (typeof filterFavorites === "number")
|
||||
result = result && movie.favorite === filterFavorites;
|
||||
if (typeof filterUpcoming === "number")
|
||||
result = result && new Date(movie.releaseDate) > new Date();
|
||||
if (typeof filterReleased === "number")
|
||||
result = result && new Date(movie.releaseDate) < new Date();
|
||||
return result;
|
||||
});
|
||||
|
||||
let sortedMovies = filteredMovies.sort((a, b) => {
|
||||
|
|
|
|||
|
|
@ -16,3 +16,10 @@ export const addMovie = async (movie: typeof movies.$inferInsert) => {
|
|||
export const deleteMovie = async (id: number) => {
|
||||
await db.delete(movies).where(eq(movies.id, id));
|
||||
};
|
||||
|
||||
export const updateMovie = async (
|
||||
id: number,
|
||||
movie: Partial<typeof movies.$inferInsert>
|
||||
) => {
|
||||
await db.update(movies).set(movie).where(eq(movies.id, id));
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue