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 /> |       <SearchMovies /> | ||||||
|       <MovieList |       <MovieList | ||||||
|         heading="Upcoming" |         heading="Upcoming" | ||||||
|         onlyUpcoming |         filterUpcoming={1} | ||||||
|         sort="releaseDate" |         sort="releaseDate" | ||||||
|         sortDirection="desc" |         sortDirection="desc" | ||||||
|       /> |       /> | ||||||
|       <MovieList heading="My Watchlist" onlyReleased showFilters /> |       <MovieList | ||||||
|       <MovieList heading="Seen" onlySeen /> |         heading="My Watchlist" | ||||||
|       <MovieList heading="Favorites" onlyFavorites /> |         filterReleased={1} | ||||||
|  |         filterSeen={0} | ||||||
|  |         showFilters | ||||||
|  |       /> | ||||||
|  |       <MovieList heading="Seen" filterSeen={1} /> | ||||||
|  |       <MovieList heading="Favorites" filterFavorites={1} /> | ||||||
|     </main> |     </main> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,16 +2,20 @@ | ||||||
| import { movies } from "@/lib/db/schema"; | import { movies } from "@/lib/db/schema"; | ||||||
| import { createContext, FC, use, useState } from "react"; | import { createContext, FC, use, useState } from "react"; | ||||||
| 
 | 
 | ||||||
|  | type Movie = typeof movies.$inferSelect; | ||||||
|  | 
 | ||||||
| type GlobalStore = { | type GlobalStore = { | ||||||
|   movies: (typeof movies.$inferSelect)[]; |   movies: Movie[]; | ||||||
|   addMovie: (movie: typeof movies.$inferSelect) => void; |   addMovie: (movie: Movie) => void; | ||||||
|   deleteMovie: (id: number) => void; |   deleteMovie: (id: number) => void; | ||||||
|  |   updateMovie: (id: number, movie: Partial<Movie>) => void; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const globalStore = createContext<GlobalStore>({ | const globalStore = createContext<GlobalStore>({ | ||||||
|   movies: [], |   movies: [], | ||||||
|   addMovie: () => {}, |   addMovie: () => {}, | ||||||
|   deleteMovie: () => {}, |   deleteMovie: () => {}, | ||||||
|  |   updateMovie: () => {}, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| type Props = { | type Props = { | ||||||
|  | @ -22,7 +26,7 @@ type Props = { | ||||||
| export const GlobalStoreProvider: FC<Props> = ({ children, initialMovies }) => { | export const GlobalStoreProvider: FC<Props> = ({ children, initialMovies }) => { | ||||||
|   const [movies, setMovies] = useState<GlobalStore["movies"]>(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; |     if (movies.find((m) => m.id === movie.id)) return; | ||||||
| 
 | 
 | ||||||
|     setMovies((prev) => [...prev, movie]); |     setMovies((prev) => [...prev, movie]); | ||||||
|  | @ -32,8 +36,16 @@ export const GlobalStoreProvider: FC<Props> = ({ children, initialMovies }) => { | ||||||
|     setMovies((prev) => prev.filter((m) => m.id !== id)); |     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 ( |   return ( | ||||||
|     <globalStore.Provider value={{ movies, addMovie, deleteMovie }}> |     <globalStore.Provider | ||||||
|  |       value={{ movies, addMovie, deleteMovie, updateMovie }} | ||||||
|  |     > | ||||||
|       {children} |       {children} | ||||||
|     </globalStore.Provider> |     </globalStore.Provider> | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| "use client"; | "use client"; | ||||||
| import { FC, useState } from "react"; | import { FC } from "react"; | ||||||
| import { ReadMore } from "../ReadMore"; | import { ReadMore } from "../ReadMore"; | ||||||
| import { addMovie, deleteMovie } from "@/lib/db"; | import { addMovie, deleteMovie, updateMovie } from "@/lib/db"; | ||||||
| import { useGlobalStore } from "@/app/store/globalStore"; | import { useGlobalStore } from "@/app/store/globalStore"; | ||||||
| 
 | 
 | ||||||
| type Props = { | type Props = { | ||||||
|  | @ -16,6 +16,8 @@ type Props = { | ||||||
|   notes?: string; |   notes?: string; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | const buttonClass = "px-2 py-6 text-sm w-full transition-colors cursor-pointer"; | ||||||
|  | 
 | ||||||
| export const MovieCard: FC<Props> = ({ | export const MovieCard: FC<Props> = ({ | ||||||
|   id, |   id, | ||||||
|   title, |   title, | ||||||
|  | @ -31,9 +33,12 @@ export const MovieCard: FC<Props> = ({ | ||||||
|     movies, |     movies, | ||||||
|     addMovie: addMovieToStore, |     addMovie: addMovieToStore, | ||||||
|     deleteMovie: deleteMovieFromStore, |     deleteMovie: deleteMovieFromStore, | ||||||
|  |     updateMovie: updateMovieInStore, | ||||||
|   } = useGlobalStore(); |   } = useGlobalStore(); | ||||||
|   const alreadyInStore = movies.find((m) => m.id === id); |   const alreadyInStore = movies.find((m) => m.id === id); | ||||||
| 
 | 
 | ||||||
|  |   const isReleased = new Date(releaseDate) < new Date(); | ||||||
|  | 
 | ||||||
|   const handleAdd = async () => { |   const handleAdd = async () => { | ||||||
|     const movie = { |     const movie = { | ||||||
|       id, |       id, | ||||||
|  | @ -55,6 +60,16 @@ export const MovieCard: FC<Props> = ({ | ||||||
|     deleteMovieFromStore(id); |     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 ( |   return ( | ||||||
|     <div className="flex w-full shadow-md rounded-lg overflow-hidden mx-auto group/card"> |     <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"> |       <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> | ||||||
|         </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 && ( |           {!alreadyInStore && ( | ||||||
|             <button |             <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} |               onClick={handleAdd} | ||||||
|             > |             > | ||||||
|               Add to watchlist |               Add to watchlist | ||||||
|             </button> |             </button> | ||||||
|           )} |           )} | ||||||
|           {alreadyInStore && ( |           {alreadyInStore && ( | ||||||
|  |             <> | ||||||
|  |               {isReleased && ( | ||||||
|                 <button |                 <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-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} |                 onClick={handleRemove} | ||||||
|               > |               > | ||||||
|                 Remove from watchlist |                 Remove from watchlist | ||||||
|               </button> |               </button> | ||||||
|  |             </> | ||||||
|           )} |           )} | ||||||
|         </div> |         </div> | ||||||
|         <figure className="absolute inset-0 w-full bottom-[20%]"> |         <figure className="absolute inset-0 w-full bottom-[20%]"> | ||||||
|  |  | ||||||
|  | @ -5,10 +5,10 @@ import { useGlobalStore } from "@/app/store/globalStore"; | ||||||
| 
 | 
 | ||||||
| type Props = { | type Props = { | ||||||
|   heading: string; |   heading: string; | ||||||
|   onlySeen?: boolean; |   filterSeen?: 0 | 1; | ||||||
|   onlyFavorites?: boolean; |   filterFavorites?: 0 | 1; | ||||||
|   onlyUpcoming?: boolean; |   filterUpcoming?: 0 | 1; | ||||||
|   onlyReleased?: boolean; |   filterReleased?: 0 | 1; | ||||||
| 
 | 
 | ||||||
|   showFilters?: boolean; |   showFilters?: boolean; | ||||||
|   sort?: "title" | "releaseDate" | "popularity"; |   sort?: "title" | "releaseDate" | "popularity"; | ||||||
|  | @ -17,10 +17,10 @@ type Props = { | ||||||
| 
 | 
 | ||||||
| export const MovieList: FC<Props> = ({ | export const MovieList: FC<Props> = ({ | ||||||
|   heading, |   heading, | ||||||
|   onlyFavorites, |   filterSeen, | ||||||
|   onlySeen, |   filterFavorites, | ||||||
|   onlyUpcoming, |   filterUpcoming, | ||||||
|   onlyReleased, |   filterReleased, | ||||||
|   showFilters = false, |   showFilters = false, | ||||||
|   sort = "title", |   sort = "title", | ||||||
|   sortDirection = "asc", |   sortDirection = "asc", | ||||||
|  | @ -31,11 +31,15 @@ export const MovieList: FC<Props> = ({ | ||||||
|   const { movies } = useGlobalStore(); |   const { movies } = useGlobalStore(); | ||||||
| 
 | 
 | ||||||
|   const filteredMovies = movies.filter((movie) => { |   const filteredMovies = movies.filter((movie) => { | ||||||
|     if (onlySeen) return movie.seen === 1; |     let result = true; | ||||||
|     if (onlyFavorites) return movie.favorite === 1; |     if (typeof filterSeen === "number") result = movie.seen === filterSeen; | ||||||
|     if (onlyUpcoming) return new Date(movie.releaseDate) > new Date(); |     if (typeof filterFavorites === "number") | ||||||
|     if (onlyReleased) return new Date(movie.releaseDate) < new Date(); |       result = result && movie.favorite === filterFavorites; | ||||||
|     return true; |     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) => { |   let sortedMovies = filteredMovies.sort((a, b) => { | ||||||
|  |  | ||||||
|  | @ -16,3 +16,10 @@ export const addMovie = async (movie: typeof movies.$inferInsert) => { | ||||||
| export const deleteMovie = async (id: number) => { | export const deleteMovie = async (id: number) => { | ||||||
|   await db.delete(movies).where(eq(movies.id, id)); |   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