poster-card.tsx 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. "use client";
  2. import type { Database } from "@/types/database";
  3. import { getTMDBImageUrl } from "@/types/tmdb";
  4. type MovieRow = Database["public"]["Tables"]["movies"]["Row"];
  5. interface PosterCardProps {
  6. movie: MovieRow;
  7. avatarColor?: string | null;
  8. onInfo: (movie: MovieRow) => void;
  9. }
  10. export function PosterCard({ movie, avatarColor, onInfo }: PosterCardProps) {
  11. const posterUrl = getTMDBImageUrl(movie.poster_path, "grid");
  12. const altText = `${movie.title} (${movie.year}) poster`;
  13. return (
  14. <button
  15. type="button"
  16. onClick={() => onInfo(movie)}
  17. aria-label={`More info about ${movie.title}`}
  18. className="w-full text-left rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500"
  19. >
  20. <div className="relative aspect-[2/3] overflow-hidden rounded-lg bg-gray-800">
  21. {posterUrl ? (
  22. /* eslint-disable-next-line @next/next/no-img-element */
  23. <img
  24. src={posterUrl}
  25. alt={altText}
  26. loading="lazy"
  27. className="h-full w-full object-cover"
  28. />
  29. ) : (
  30. <div className="flex h-full w-full items-center justify-center p-2 text-center text-sm text-gray-400">
  31. {movie.title}
  32. </div>
  33. )}
  34. {movie.watched && (
  35. <span
  36. className="absolute top-1.5 left-1.5 flex h-7 w-7 items-center justify-center rounded-full bg-green-600 text-white shadow-lg ring-2 ring-white/80"
  37. aria-label="Watched"
  38. >
  39. <svg
  40. xmlns="http://www.w3.org/2000/svg"
  41. viewBox="0 0 24 24"
  42. fill="none"
  43. stroke="currentColor"
  44. strokeWidth={3}
  45. strokeLinecap="round"
  46. strokeLinejoin="round"
  47. className="h-4 w-4"
  48. aria-hidden="true"
  49. >
  50. <polyline points="20 6 9 17 4 12" />
  51. </svg>
  52. </span>
  53. )}
  54. {avatarColor && (
  55. <span
  56. className="absolute top-1.5 right-1.5 block h-5 w-5 rounded-full border-2 border-white/80"
  57. style={{ backgroundColor: avatarColor }}
  58. aria-hidden="true"
  59. />
  60. )}
  61. <span
  62. aria-hidden="true"
  63. className="pointer-events-none absolute bottom-1.5 right-1.5 flex h-9 w-9 items-center justify-center rounded-full bg-black/65 text-base font-serif italic text-white shadow-lg ring-1 ring-white/30"
  64. >
  65. i
  66. </span>
  67. </div>
  68. <p className="mt-1 truncate text-sm font-medium text-gray-100">{movie.title}</p>
  69. </button>
  70. );
  71. }