feat: add TrackedMovies component to display upcoming and currently showing films; introduce MovieRow for individual movie representation
This commit is contained in:
parent
7aafeb8343
commit
64bb4cd4e4
|
|
@ -1,8 +1,10 @@
|
||||||
import { MovieList } from "@/components/molecules/MovieList";
|
import { MovieList } from "@/components/molecules/MovieList";
|
||||||
|
import { TrackedMovies } from "@/components/molecules/TrackedMovies";
|
||||||
|
|
||||||
export default async function Home() {
|
export default async function Home() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<TrackedMovies />
|
||||||
<MovieList
|
<MovieList
|
||||||
heading="Nadchodzące"
|
heading="Nadchodzące"
|
||||||
filterUpcoming={1}
|
filterUpcoming={1}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { formatter } from "@/helpers/formater";
|
||||||
|
import { Movie } from "@/types/global";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { FC } from "react";
|
||||||
|
import { FaCalendar, FaClock, FaStar } from "react-icons/fa";
|
||||||
|
|
||||||
|
export const MovieRow: FC<{ movie: Movie; isUpcoming: boolean }> = ({
|
||||||
|
movie,
|
||||||
|
isUpcoming,
|
||||||
|
}) => {
|
||||||
|
const daysSinceRelease = Math.abs(
|
||||||
|
Math.floor(
|
||||||
|
(new Date().getTime() - new Date(movie.release_date).getTime()) /
|
||||||
|
(1000 * 60 * 60 * 24)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
href={`/film/${movie.id}`}
|
||||||
|
className="flex items-center gap-4 p-3 rounded-lg bg-gray-800/30 hover:bg-gray-800/50 transition-colors group"
|
||||||
|
>
|
||||||
|
<div className="relative w-12 h-16 rounded overflow-hidden flex-shrink-0">
|
||||||
|
<img
|
||||||
|
src={`https://image.tmdb.org/t/p/w154${movie.poster_path}`}
|
||||||
|
alt={movie.title}
|
||||||
|
className="object-cover inset-0"
|
||||||
|
sizes="48px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<h3 className="text-white font-medium text-sm truncate group-hover:text-blue-400 transition-colors">
|
||||||
|
{movie.title}
|
||||||
|
</h3>
|
||||||
|
<div className="flex items-center gap-3 mt-1">
|
||||||
|
<div className="flex items-center gap-1 text-gray-400 text-xs">
|
||||||
|
{isUpcoming ? (
|
||||||
|
<FaCalendar className="w-3 h-3" />
|
||||||
|
) : (
|
||||||
|
<FaClock className="w-3 h-3" />
|
||||||
|
)}
|
||||||
|
<span>{formatter.formatDate(movie.release_date)}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!!movie.vote_average && (
|
||||||
|
<div className="flex items-center gap-1 text-yellow-400 text-xs">
|
||||||
|
<FaStar className="w-3 h-3 fill-current" />
|
||||||
|
<span>{movie.vote_average.toFixed(1)}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{movie.favorite && (
|
||||||
|
<div className="w-2 h-2 bg-red-500 rounded-full" title="Ulubione" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`text-xs px-2 py-1 rounded-full font-medium ${
|
||||||
|
isUpcoming
|
||||||
|
? "bg-blue-500/20 text-blue-400"
|
||||||
|
: "bg-green-500/20 text-green-400"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{isUpcoming
|
||||||
|
? `za ${daysSinceRelease} dni`
|
||||||
|
: `od ${daysSinceRelease} dni`}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
"use client";
|
||||||
|
import { FC } from "react";
|
||||||
|
import { useGlobalStore } from "@/app/store/globalStore";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { FaCalendar, FaClock, FaStar } from "react-icons/fa";
|
||||||
|
import { MovieRow } from "@/components/atoms/MovieRow";
|
||||||
|
|
||||||
|
export const TrackedMovies: FC = () => {
|
||||||
|
const { movies } = useGlobalStore();
|
||||||
|
|
||||||
|
if (movies.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
const upcoming = movies.filter(
|
||||||
|
(movie) => new Date(movie.release_date) > today
|
||||||
|
);
|
||||||
|
const inCinema = movies.filter((movie) => {
|
||||||
|
const daysSinceRelease = Math.floor(
|
||||||
|
(new Date().getTime() - new Date(movie.release_date).getTime()) /
|
||||||
|
(1000 * 60 * 60 * 24)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
new Date(movie.release_date) <= today &&
|
||||||
|
daysSinceRelease <= 30 &&
|
||||||
|
!movie.seen
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sort by release date.
|
||||||
|
const sortedUpcoming = upcoming.sort(
|
||||||
|
(a, b) =>
|
||||||
|
new Date(a.release_date).getTime() - new Date(b.release_date).getTime()
|
||||||
|
);
|
||||||
|
const sortedInCinema = inCinema.sort(
|
||||||
|
(a, b) =>
|
||||||
|
new Date(b.release_date).getTime() - new Date(a.release_date).getTime()
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="bg-gray-900/50 border-b border-gray-800">
|
||||||
|
<div className="container py-6">
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||||
|
{/* Currently in cinemas. */}
|
||||||
|
{sortedInCinema.length > 0 && (
|
||||||
|
<div>
|
||||||
|
<h3 className="text-green-400 font-medium text-sm mb-3 flex items-center gap-2">
|
||||||
|
<FaClock className="w-4 h-4" />
|
||||||
|
Aktualnie w kinach ({sortedInCinema.length})
|
||||||
|
</h3>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{sortedInCinema.map((movie) => (
|
||||||
|
<MovieRow key={movie.id} movie={movie} isUpcoming={false} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* Upcoming premieres. */}
|
||||||
|
{sortedUpcoming.length > 0 && (
|
||||||
|
<div>
|
||||||
|
<h3 className="text-blue-400 font-medium text-sm mb-3 flex items-center gap-2">
|
||||||
|
<FaCalendar className="w-4 h-4" />
|
||||||
|
Nadchodzące premiery ({sortedUpcoming.length})
|
||||||
|
</h3>
|
||||||
|
<div className="space-y-2">
|
||||||
|
{sortedUpcoming.map((movie) => (
|
||||||
|
<MovieRow key={movie.id} movie={movie} isUpcoming />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{sortedUpcoming.length === 0 && sortedInCinema.length === 0 && (
|
||||||
|
<p className="text-gray-400 text-sm text-center py-4">
|
||||||
|
Wszystkie śledzone filmy zostały już obejrzane
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue