diff --git a/src/components/atoms/MovieCard/index.tsx b/src/components/atoms/MovieCard/index.tsx index 6bbac81..3a8b429 100644 --- a/src/components/atoms/MovieCard/index.tsx +++ b/src/components/atoms/MovieCard/index.tsx @@ -1,14 +1,14 @@ "use client"; -import { FC, useState } from "react"; -import { ReadMore } from "../ReadMore"; +import { FC } from "react"; import { addMovie, deleteMovie, updateMovie } from "@/lib/db"; import { useGlobalStore } from "@/app/store/globalStore"; -import { MdFavorite, MdFavoriteBorder, MdOutlinePostAdd } from "react-icons/md"; -import { RxEyeClosed, RxEyeOpen } from "react-icons/rx"; -import { IoMdRemoveCircleOutline } from "react-icons/io"; import { Movie } from "@/types/global"; -import { FaAngleUp, FaArrowUp, FaFire, FaTrash } from "react-icons/fa"; -import { RiCalendarCheckLine, RiCalendarScheduleLine } from "react-icons/ri"; +import { + AuroraLayout, + MinimalLayout, + ZeusLayout, + DefaultLayout, +} from "./layouts"; type Props = Movie & { layout?: "default" | "zeus" | "minimal" | "aurora"; @@ -16,38 +16,26 @@ type Props = Movie & { simpleToggle?: boolean; }; -const buttonClass = - "p-4 text-sm transition-colors cursor-pointer text-center group/toggle"; - export const MovieCard: FC = ({ layout = "minimal", showDayCounter = true, simpleToggle = false, ...movie }) => { - const [isExpanded, setIsExpanded] = useState(false); const { movies, addMovie: addMovieToStore, deleteMovie: deleteMovieFromStore, updateMovie: updateMovieInStore, } = useGlobalStore(); - const { - id, - title, - overview, - popularity, - release_date, - poster_path, - vote_average, - } = movie; + + const { id } = movie; const alreadyInStore = movies.find((m) => m.id === id); - const isReleased = new Date(release_date) < new Date(); + const isReleased = new Date(movie.release_date) < new Date(); const iconSize = 48; - - const seen = movie.seen; - const favorite = movie.favorite; + const buttonClass = + "p-4 text-sm transition-colors cursor-pointer text-center group/toggle"; const handleAdd = async () => { await addMovie(movie); @@ -60,519 +48,46 @@ export const MovieCard: FC = ({ }; const handleSeen = async () => { - await updateMovie(id, { seen: !seen }); - updateMovieInStore(id, { seen: !seen }); + await updateMovie(id, { seen: !movie.seen }); + updateMovieInStore(id, { seen: !movie.seen }); }; const handleFavorite = async () => { - await updateMovie(id, { favorite: !favorite }); - updateMovieInStore(id, { favorite: !favorite }); + await updateMovie(id, { favorite: !movie.favorite }); + updateMovieInStore(id, { favorite: !movie.favorite }); }; - const releaseDate = new Date(release_date); + const releaseDate = new Date(movie.release_date); const daysSinceRelease = Math.abs( Math.floor( (new Date().getTime() - releaseDate.getTime()) / (1000 * 60 * 60 * 24) ) ); - // Aurora theme - creative and visually stunning. - if (layout === "aurora") { - const scoreColor = - vote_average >= 8 - ? "from-emerald-400 to-teal-400" - : vote_average >= 6 - ? "from-yellow-400 to-orange-400" - : "from-red-400 to-pink-400"; + const commonProps = { + ...movie, + alreadyInStore, + isReleased, + handleAdd, + handleRemove, + handleSeen, + handleFavorite, + daysSinceRelease, + releaseDate, + showDayCounter, + simpleToggle, + buttonClass, + iconSize, + }; - return ( -
setIsExpanded(false)} - > - {/* Aurora background effect */} -
-
- - {/* Main card container */} -
- {/* Image section with sophisticated overlay */} -
- {title} - - {/* Gradient overlays for depth */} -
-
- - {/* Floating rating badge */} - {!!vote_average && ( -
-
-
- - {vote_average.toFixed(1)} -
-
-
- )} - - {/* Popularity indicator */} -
-
- - - {Math.round(popularity)} - -
-
- - {/* x Days left to release */} - {(!isReleased || daysSinceRelease < 35) && ( -
-
- - {isReleased && - daysSinceRelease < 35 && - `od ${daysSinceRelease} dni`} - {!isReleased && `za ${daysSinceRelease} dni`} - -
-
- )} - - {/* Status indicators */} - {alreadyInStore && ( -
-
- -
-
- -
-
- -
-
- )} - - {/* Magical action overlay */} - {!alreadyInStore && ( -
- -
- )} -
- - {/* Content section with glowing effects */} -
- {/* Subtle glow effect */} -
- -
-

- {title} -

-

setIsExpanded(!isExpanded)} - > - {overview} -

-
- - {/* Bottom section with enhanced styling */} -
-
-
- {isReleased ? ( - - ) : ( - - )} - - {releaseDate.toLocaleDateString("pl-PL", { - day: "numeric", - month: "short", - year: "numeric", - })} - -
-
- - {alreadyInStore && ( -
- {seen && ( -
- )} - {favorite && ( -
- )} -
- )} -
-
- - {/* Decorative border glow */} -
-
- - {isExpanded && ( -
setIsExpanded(false)} - > -

{overview}

-
- )} -
- ); + switch (layout) { + case "aurora": + return ; + case "minimal": + return ; + case "zeus": + return ; + default: + return ; } - - // Minimal modern theme. - if (layout === "minimal") { - return ( -
-
- {title} - - {/* Rating badge */} - {!!vote_average && ( -
- - ★ {vote_average.toFixed(1)} - -
- )} - - {/* Action overlay */} -
- {!alreadyInStore ? ( - - ) : ( -
- {isReleased && ( - - )} - - -
- )} -
-
- - {/* Content section */} -
-
-

- {title} -

-
- -
-
- -
- - {releaseDate.toLocaleDateString("pl-PL", { - day: "numeric", - month: "long", - year: "numeric", - })} - - {alreadyInStore && ( -
- {seen && ( -
- )} - {favorite && ( -
- )} -
- )} -
-
-
- ); - } - - if (layout === "zeus") { - return ( -
-
- - - {!alreadyInStore && ( - - )} - {alreadyInStore && ( -
- <> - {isReleased && !simpleToggle && ( - - )} - - {!simpleToggle && ( - - )} - - - -
- )} -
- -

- - {popularity} -

-
-
-
- {!!vote_average && ( -

- - {vote_average.toFixed(1)}/10 -

- )} - -
-

{title}

-
- -

- {isReleased ? : } - - {releaseDate.toLocaleDateString("pl-PL", { - day: "numeric", - month: "long", - year: "numeric", - })} - -

- {showDayCounter && ( - - {isReleased - ? `${daysSinceRelease} dni od premiery` - : `${daysSinceRelease} dni do premiery`} - - )} -
- -
-
-
- ); - } - - return ( -
-
-
-
-
-
-
-

- {title} -

-
- -
-
-
-
-
Popularity:
-
{popularity}
-
-
-
Release date:
-
{release_date}
-
-
-
-
- {/*
*/} -
- {!alreadyInStore && ( - - )} - {alreadyInStore && ( - <> - {isReleased && ( - - )} - - - - - - )} -
-
- -
-
-
- ); }; diff --git a/src/components/atoms/MovieCard/layouts/AuroraLayout.tsx b/src/components/atoms/MovieCard/layouts/AuroraLayout.tsx new file mode 100644 index 0000000..7e9b638 --- /dev/null +++ b/src/components/atoms/MovieCard/layouts/AuroraLayout.tsx @@ -0,0 +1,230 @@ +"use client"; +import { FC, useState } from "react"; +import { MdFavorite } from "react-icons/md"; +import { RxEyeOpen } from "react-icons/rx"; +import { FaFire, FaTrash } from "react-icons/fa"; +import { RiCalendarCheckLine, RiCalendarScheduleLine } from "react-icons/ri"; +import { Movie } from "@/types/global"; + +interface AuroraLayoutProps extends Movie { + showDayCounter?: boolean; + simpleToggle?: boolean; + alreadyInStore?: Movie | undefined; + isReleased: boolean; + handleAdd: () => void; + handleRemove: () => void; + handleSeen: () => void; + handleFavorite: () => void; + daysSinceRelease: number; + releaseDate: Date; +} + +export const AuroraLayout: FC = ({ + title, + overview, + popularity, + release_date, + poster_path, + vote_average, + seen, + favorite, + alreadyInStore, + isReleased, + handleAdd, + handleRemove, + handleSeen, + handleFavorite, + daysSinceRelease, + releaseDate, +}) => { + const [isExpanded, setIsExpanded] = useState(false); + + const scoreColor = + vote_average >= 8 + ? "from-emerald-400 to-teal-400" + : vote_average >= 6 + ? "from-yellow-400 to-orange-400" + : "from-red-400 to-pink-400"; + + return ( +
setIsExpanded(false)} + > + {/* Aurora background effect */} +
+
+ + {/* Main card container */} +
+ {/* Image section with sophisticated overlay */} +
+ {title} + + {/* Gradient overlays for depth */} +
+
+ + {/* Floating rating badge */} + {!!vote_average && ( +
+
+
+ + {vote_average.toFixed(1)} +
+
+
+ )} + + {/* Popularity indicator */} +
+
+ + + {Math.round(popularity)} + +
+
+ + {/* Days left to release */} + {(!isReleased || daysSinceRelease < 35) && ( +
+
+ + {isReleased && + daysSinceRelease < 35 && + `od ${daysSinceRelease} dni`} + {!isReleased && `za ${daysSinceRelease} dni`} + +
+
+ )} + + {/* Status indicators */} + {alreadyInStore && ( +
+
+ +
+
+ +
+
+ +
+
+ )} + + {/* Magical action overlay */} + {!alreadyInStore && ( +
+ +
+ )} +
+ + {/* Content section with glowing effects */} +
+ {/* Subtle glow effect */} +
+ +
+

+ {title} +

+

setIsExpanded(!isExpanded)} + > + {overview} +

+
+ + {/* Bottom section with enhanced styling */} +
+
+
+ {isReleased ? ( + + ) : ( + + )} + + {releaseDate.toLocaleDateString("pl-PL", { + day: "numeric", + month: "short", + year: "numeric", + })} + +
+
+ + {alreadyInStore && ( +
+ {seen && ( +
+ )} + {favorite && ( +
+ )} +
+ )} +
+
+ + {/* Decorative border glow */} +
+
+ + {isExpanded && ( +
setIsExpanded(false)} + > +

{overview}

+
+ )} +
+ ); +}; diff --git a/src/components/atoms/MovieCard/layouts/DefaultLayout.tsx b/src/components/atoms/MovieCard/layouts/DefaultLayout.tsx new file mode 100644 index 0000000..0b52771 --- /dev/null +++ b/src/components/atoms/MovieCard/layouts/DefaultLayout.tsx @@ -0,0 +1,109 @@ +"use client"; +import { FC } from "react"; +import { ReadMore } from "../../ReadMore"; +import { Movie } from "@/types/global"; + +interface DefaultLayoutProps extends Movie { + showDayCounter?: boolean; + simpleToggle?: boolean; + alreadyInStore?: Movie | undefined; + isReleased: boolean; + handleAdd: () => void; + handleRemove: () => void; + handleSeen: () => void; + handleFavorite: () => void; + buttonClass: string; +} + +export const DefaultLayout: FC = ({ + title, + overview, + popularity, + release_date, + poster_path, + alreadyInStore, + isReleased, + handleAdd, + handleRemove, + handleSeen, + handleFavorite, + buttonClass, +}) => { + return ( +
+
+
+
+
+
+
+

+ {title} +

+
+ +
+
+
+
+
Popularity:
+
{popularity}
+
+
+
Release date:
+
{release_date}
+
+
+
+
+
+ {!alreadyInStore && ( + + )} + {alreadyInStore && ( + <> + {isReleased && ( + + )} + + + + + + )} +
+
+ +
+
+
+ ); +}; diff --git a/src/components/atoms/MovieCard/layouts/MinimalLayout.tsx b/src/components/atoms/MovieCard/layouts/MinimalLayout.tsx new file mode 100644 index 0000000..1a6d573 --- /dev/null +++ b/src/components/atoms/MovieCard/layouts/MinimalLayout.tsx @@ -0,0 +1,141 @@ +"use client"; +import { FC } from "react"; +import { ReadMore } from "../../ReadMore"; +import { MdFavorite, MdFavoriteBorder } from "react-icons/md"; +import { RxEyeOpen, RxEyeClosed } from "react-icons/rx"; +import { IoMdRemoveCircleOutline } from "react-icons/io"; +import { Movie } from "@/types/global"; + +interface MinimalLayoutProps extends Movie { + showDayCounter?: boolean; + simpleToggle?: boolean; + alreadyInStore?: Movie | undefined; + isReleased: boolean; + handleAdd: () => void; + handleRemove: () => void; + handleSeen: () => void; + handleFavorite: () => void; + releaseDate: Date; +} + +export const MinimalLayout: FC = ({ + title, + overview, + poster_path, + vote_average, + seen, + favorite, + alreadyInStore, + isReleased, + handleAdd, + handleRemove, + handleSeen, + handleFavorite, + releaseDate, +}) => { + return ( +
+
+ {title} + + {/* Rating badge */} + {!!vote_average && ( +
+ + ★ {vote_average.toFixed(1)} + +
+ )} + + {/* Action overlay */} +
+ {!alreadyInStore ? ( + + ) : ( +
+ {isReleased && ( + + )} + + +
+ )} +
+
+ + {/* Content section */} +
+
+

+ {title} +

+
+ +
+
+ +
+ + {releaseDate.toLocaleDateString("pl-PL", { + day: "numeric", + month: "long", + year: "numeric", + })} + + {alreadyInStore && ( +
+ {seen && ( +
+ )} + {favorite && ( +
+ )} +
+ )} +
+
+
+ ); +}; diff --git a/src/components/atoms/MovieCard/layouts/ZeusLayout.tsx b/src/components/atoms/MovieCard/layouts/ZeusLayout.tsx new file mode 100644 index 0000000..2b43e6d --- /dev/null +++ b/src/components/atoms/MovieCard/layouts/ZeusLayout.tsx @@ -0,0 +1,166 @@ +"use client"; +import { FC } from "react"; +import { ReadMore } from "../../ReadMore"; +import { MdFavorite, MdFavoriteBorder, MdOutlinePostAdd } from "react-icons/md"; +import { RxEyeOpen, RxEyeClosed } from "react-icons/rx"; +import { IoMdRemoveCircleOutline } from "react-icons/io"; +import { FaFire } from "react-icons/fa"; +import { RiCalendarCheckLine, RiCalendarScheduleLine } from "react-icons/ri"; +import { Movie } from "@/types/global"; + +interface ZeusLayoutProps extends Movie { + showDayCounter?: boolean; + simpleToggle?: boolean; + alreadyInStore?: Movie | undefined; + isReleased: boolean; + handleAdd: () => void; + handleRemove: () => void; + handleSeen: () => void; + handleFavorite: () => void; + daysSinceRelease: number; + releaseDate: Date; + buttonClass: string; + iconSize: number; +} + +export const ZeusLayout: FC = ({ + title, + overview, + popularity, + poster_path, + vote_average, + seen, + favorite, + showDayCounter, + simpleToggle, + alreadyInStore, + isReleased, + handleAdd, + handleRemove, + handleSeen, + handleFavorite, + daysSinceRelease, + releaseDate, + buttonClass, + iconSize, +}) => { + return ( +
+
+ + + {!alreadyInStore && ( + + )} + {alreadyInStore && ( +
+ <> + {isReleased && !simpleToggle && ( + + )} + + {!simpleToggle && ( + + )} + + + +
+ )} +
+ +

+ + {popularity} +

+
+
+
+ {!!vote_average && ( +

+ + {vote_average.toFixed(1)}/10 +

+ )} + +
+

{title}

+
+ +

+ {isReleased ? : } + + {releaseDate.toLocaleDateString("pl-PL", { + day: "numeric", + month: "long", + year: "numeric", + })} + +

+ {showDayCounter && ( + + {isReleased + ? `${daysSinceRelease} dni od premiery` + : `${daysSinceRelease} dni do premiery`} + + )} +
+ +
+
+
+ ); +}; diff --git a/src/components/atoms/MovieCard/layouts/index.ts b/src/components/atoms/MovieCard/layouts/index.ts new file mode 100644 index 0000000..fac78a3 --- /dev/null +++ b/src/components/atoms/MovieCard/layouts/index.ts @@ -0,0 +1,4 @@ +export { AuroraLayout } from "./AuroraLayout"; +export { MinimalLayout } from "./MinimalLayout"; +export { ZeusLayout } from "./ZeusLayout"; +export { DefaultLayout } from "./DefaultLayout";