teaser-card.tsx 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. "use client";
  2. import { useState } from "react";
  3. import { getTMDBImageUrl, TMDB_GENRE_MAP } from "@/types/tmdb";
  4. import type { TMDBMovie } from "@/types/tmdb";
  5. import { MoreInfoModal } from "./more-info-modal";
  6. interface TeaserCardProps {
  7. movie: TMDBMovie;
  8. }
  9. export function TeaserCard({ movie }: TeaserCardProps) {
  10. const [modalOpen, setModalOpen] = useState(false);
  11. const posterUrl = getTMDBImageUrl(movie.poster_path, "grid");
  12. const genres = movie.genre_ids
  13. .map((id) => TMDB_GENRE_MAP[id])
  14. .filter(Boolean)
  15. .slice(0, 3);
  16. const year = movie.release_date ? movie.release_date.slice(0, 4) : "";
  17. return (
  18. <>
  19. <div className="flex w-40 flex-col items-center rounded-xl bg-background/95 p-3 shadow-2xl ring-1 ring-foreground/10 backdrop-blur">
  20. {posterUrl ? (
  21. /* eslint-disable-next-line @next/next/no-img-element */
  22. <img
  23. src={posterUrl}
  24. alt={`Movie poster for ${movie.title}`}
  25. loading="lazy"
  26. className="h-auto w-36 rounded-lg"
  27. />
  28. ) : (
  29. <div className="flex h-52 w-36 items-center justify-center rounded-lg bg-foreground/10 text-xs text-foreground/40">
  30. No poster
  31. </div>
  32. )}
  33. <h3 className="mt-2 text-center text-sm font-semibold leading-tight line-clamp-2">
  34. {movie.title}
  35. {year && <span className="ml-1 text-xs font-normal text-foreground/50">({year})</span>}
  36. </h3>
  37. <button
  38. onClick={() => setModalOpen(true)}
  39. aria-label={`More info about ${movie.title}`}
  40. className="mt-1.5 flex h-6 w-6 items-center justify-center rounded-full border border-foreground/30 text-xs font-serif italic text-foreground/70 transition-colors hover:bg-foreground/5 hover:text-foreground"
  41. >
  42. i
  43. </button>
  44. {genres.length > 0 && (
  45. <div className="mt-1.5 flex flex-wrap justify-center gap-1">
  46. {genres.slice(0, 2).map((genre) => (
  47. <span
  48. key={genre}
  49. className="rounded-full bg-foreground/10 px-2 py-0.5 text-[10px] text-foreground/70"
  50. >
  51. {genre}
  52. </span>
  53. ))}
  54. </div>
  55. )}
  56. </div>
  57. {modalOpen && <MoreInfoModal movie={movie} onClose={() => setModalOpen(false)} />}
  58. </>
  59. );
  60. }