feat: add ActorHero component and actor details page; implement API integration for fetching actor information and enhance MovieCast with links to actor profiles
This commit is contained in:
		
							parent
							
								
									7a7bc2575b
								
							
						
					
					
						commit
						cf7ec070fd
					
				|  | @ -0,0 +1,18 @@ | ||||||
|  | import { ActorHero } from "@/components/molecules/ActorHero"; | ||||||
|  | import { TMDB } from "@/lib/tmdb"; | ||||||
|  | 
 | ||||||
|  | export default async function Page({ | ||||||
|  |   params, | ||||||
|  | }: { | ||||||
|  |   params: Promise<{ id: string }>; | ||||||
|  | }) { | ||||||
|  |   const actorId = Number((await params).id); | ||||||
|  | 
 | ||||||
|  |   const personDetails = await TMDB.getPersonDetails(actorId); | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900"> | ||||||
|  |       <ActorHero personDetails={personDetails} /> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,270 @@ | ||||||
|  | "use client"; | ||||||
|  | 
 | ||||||
|  | import { BackButton } from "@/components/atoms/BackButton"; | ||||||
|  | import { PersonDetailsRich } from "@/lib/tmdb/types"; | ||||||
|  | import { FC } from "react"; | ||||||
|  | import { | ||||||
|  |   FaCalendarAlt, | ||||||
|  |   FaMapMarkerAlt, | ||||||
|  |   FaStar, | ||||||
|  |   FaTheaterMasks, | ||||||
|  |   FaImdb, | ||||||
|  |   FaFacebook, | ||||||
|  |   FaInstagram, | ||||||
|  |   FaTwitter, | ||||||
|  |   FaYoutube, | ||||||
|  |   FaTiktok, | ||||||
|  | } from "react-icons/fa"; | ||||||
|  | 
 | ||||||
|  | type Props = { | ||||||
|  |   personDetails: PersonDetailsRich; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const ActorHero: FC<Props> = ({ personDetails }) => { | ||||||
|  |   const formatDate = (dateString: string) => { | ||||||
|  |     return new Date(dateString).toLocaleDateString("pl-PL", { | ||||||
|  |       year: "numeric", | ||||||
|  |       month: "long", | ||||||
|  |       day: "numeric", | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   const calculateAge = (birthday: string, deathday?: string | null) => { | ||||||
|  |     const birth = new Date(birthday); | ||||||
|  |     const end = deathday ? new Date(deathday) : new Date(); | ||||||
|  |     const age = end.getFullYear() - birth.getFullYear(); | ||||||
|  |     const monthDiff = end.getMonth() - birth.getMonth(); | ||||||
|  | 
 | ||||||
|  |     if (monthDiff < 0 || (monthDiff === 0 && end.getDate() < birth.getDate())) { | ||||||
|  |       return age - 1; | ||||||
|  |     } | ||||||
|  |     return age; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   const getGenderText = (gender: number) => { | ||||||
|  |     switch (gender) { | ||||||
|  |       case 1: | ||||||
|  |         return "Kobieta"; | ||||||
|  |       case 2: | ||||||
|  |         return "Mężczyzna"; | ||||||
|  |       case 3: | ||||||
|  |         return "Niebinarne"; | ||||||
|  |       default: | ||||||
|  |         return "Nie określono"; | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <section className="pt-16 pb-8"> | ||||||
|  |       <div className="relative"> | ||||||
|  |         {/* Navigation */} | ||||||
|  |         <div className="absolute top-0 left-0 right-0 z-20 px-6"> | ||||||
|  |           <BackButton /> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         {/* Main content */} | ||||||
|  |         <div className="relative z-10 px-6 lg:px-8 mt-16"> | ||||||
|  |           <div className="max-w-7xl mx-auto"> | ||||||
|  |             <div className="flex flex-col lg:flex-row gap-8"> | ||||||
|  |               {/* Profile photo */} | ||||||
|  |               <div className="flex-shrink-0"> | ||||||
|  |                 <div className="relative group"> | ||||||
|  |                   <img | ||||||
|  |                     src={ | ||||||
|  |                       personDetails.profile_path | ||||||
|  |                         ? `https://image.tmdb.org/t/p/w500${personDetails.profile_path}` | ||||||
|  |                         : "/api/placeholder/400/600" | ||||||
|  |                     } | ||||||
|  |                     alt={personDetails.name} | ||||||
|  |                     className="w-80 h-auto rounded-2xl shadow-2xl shadow-purple-500/20 group-hover:shadow-purple-500/40 transition-all duration-500" | ||||||
|  |                   /> | ||||||
|  |                   <div className="absolute inset-0 rounded-2xl bg-gradient-to-t from-purple-600/20 to-transparent opacity-100" /> | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  | 
 | ||||||
|  |               {/* Actor details */} | ||||||
|  |               <div className="flex-1 text-white"> | ||||||
|  |                 <div className="space-y-6"> | ||||||
|  |                   {/* Name and known for */} | ||||||
|  |                   <div> | ||||||
|  |                     <h1 className="text-4xl lg:text-5xl font-bold bg-gradient-to-r from-white to-gray-300 bg-clip-text text-transparent"> | ||||||
|  |                       {personDetails.name} | ||||||
|  |                     </h1> | ||||||
|  |                     {personDetails.birthday && ( | ||||||
|  |                       <span className="text-sm text-gray-400"> | ||||||
|  |                         ( | ||||||
|  |                         {calculateAge( | ||||||
|  |                           personDetails.birthday, | ||||||
|  |                           personDetails.deathday | ||||||
|  |                         )}{" "} | ||||||
|  |                         lat | ||||||
|  |                         {personDetails.deathday && " w chwili śmierci"}) | ||||||
|  |                       </span> | ||||||
|  |                     )} | ||||||
|  | 
 | ||||||
|  |                     <div className="flex items-center gap-4 mb-4 mt-4"> | ||||||
|  |                       <div className="flex items-center gap-2"> | ||||||
|  |                         <FaTheaterMasks className="text-purple-400" /> | ||||||
|  |                         <span className="text-xl text-gray-300 font-medium"> | ||||||
|  |                           {personDetails.known_for_department} | ||||||
|  |                         </span> | ||||||
|  |                       </div> | ||||||
|  | 
 | ||||||
|  |                       <div className="flex items-center gap-2"> | ||||||
|  |                         <FaStar className="text-yellow-400" /> | ||||||
|  |                         <span className="text-lg text-gray-300"> | ||||||
|  |                           {Math.round(personDetails.popularity)} popularność | ||||||
|  |                         </span> | ||||||
|  |                       </div> | ||||||
|  |                     </div> | ||||||
|  | 
 | ||||||
|  |                     {/* Also known as */} | ||||||
|  |                     {personDetails.also_known_as.length > 0 && ( | ||||||
|  |                       <div className="mb-4"> | ||||||
|  |                         <p className="text-gray-400 text-sm"> | ||||||
|  |                           Znany również jako:{" "} | ||||||
|  |                           {personDetails.also_known_as.slice(0, 3).join(", ")} | ||||||
|  |                         </p> | ||||||
|  |                       </div> | ||||||
|  |                     )} | ||||||
|  |                   </div> | ||||||
|  | 
 | ||||||
|  |                   {/* Personal info */} | ||||||
|  |                   <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> | ||||||
|  |                     {/* Gender */} | ||||||
|  |                     <div className="flex items-center gap-2 text-gray-300"> | ||||||
|  |                       <span className="text-purple-400">Płeć:</span> | ||||||
|  |                       <span>{getGenderText(personDetails.gender)}</span> | ||||||
|  |                     </div> | ||||||
|  | 
 | ||||||
|  |                     {/* Birthday */} | ||||||
|  |                     {personDetails.birthday && ( | ||||||
|  |                       <div className="flex items-center gap-2 text-gray-300"> | ||||||
|  |                         <FaCalendarAlt className="text-purple-400" /> | ||||||
|  |                         <div className="flex"> | ||||||
|  |                           <span>{formatDate(personDetails.birthday)}</span> | ||||||
|  |                         </div> | ||||||
|  |                       </div> | ||||||
|  |                     )} | ||||||
|  | 
 | ||||||
|  |                     {/* Deathday */} | ||||||
|  |                     {personDetails.deathday && ( | ||||||
|  |                       <div className="flex items-center gap-2 text-gray-300"> | ||||||
|  |                         <FaCalendarAlt className="text-red-400" /> | ||||||
|  |                         <div className="flex flex-col"> | ||||||
|  |                           <span className="text-red-300">Data śmierci:</span> | ||||||
|  |                           <span>{formatDate(personDetails.deathday)}</span> | ||||||
|  |                         </div> | ||||||
|  |                       </div> | ||||||
|  |                     )} | ||||||
|  | 
 | ||||||
|  |                     {/* Place of birth */} | ||||||
|  |                     {personDetails.place_of_birth && ( | ||||||
|  |                       <div className="flex items-center gap-2 text-gray-300"> | ||||||
|  |                         <FaMapMarkerAlt className="text-purple-400" /> | ||||||
|  |                         <span>{personDetails.place_of_birth}</span> | ||||||
|  |                       </div> | ||||||
|  |                     )} | ||||||
|  |                   </div> | ||||||
|  | 
 | ||||||
|  |                   {/* Biography */} | ||||||
|  |                   {personDetails.biography && ( | ||||||
|  |                     <div> | ||||||
|  |                       <h3 className="text-lg font-semibold mb-3 text-purple-300"> | ||||||
|  |                         Biografia | ||||||
|  |                       </h3> | ||||||
|  |                       <div className="text-gray-300 leading-relaxed text-lg space-y-4"> | ||||||
|  |                         {personDetails.biography | ||||||
|  |                           .split("\n\n") | ||||||
|  |                           .map((paragraph, index) => ( | ||||||
|  |                             <p key={index}>{paragraph}</p> | ||||||
|  |                           ))} | ||||||
|  |                       </div> | ||||||
|  |                     </div> | ||||||
|  |                   )} | ||||||
|  | 
 | ||||||
|  |                   {/* External links */} | ||||||
|  |                   {personDetails.external_ids && ( | ||||||
|  |                     <div> | ||||||
|  |                       <h3 className="text-lg font-semibold mb-3 text-purple-300"> | ||||||
|  |                         Linki | ||||||
|  |                       </h3> | ||||||
|  |                       <div className="flex gap-4"> | ||||||
|  |                         {Object.entries(personDetails.external_ids).map( | ||||||
|  |                           ([key, value]) => { | ||||||
|  |                             if (!(key in externalIdsMap) || !value) { | ||||||
|  |                               return null; | ||||||
|  |                             } | ||||||
|  | 
 | ||||||
|  |                             const { label, icon, url } = | ||||||
|  |                               externalIdsMap[ | ||||||
|  |                                 key as keyof typeof externalIdsMap | ||||||
|  |                               ]; | ||||||
|  |                             return ( | ||||||
|  |                               <a | ||||||
|  |                                 href={url(value as string)} | ||||||
|  |                                 target="_blank" | ||||||
|  |                                 rel="noopener noreferrer" | ||||||
|  |                                 className="flex items-center gap-2 bg-white/10 hover:bg-white/20 px-4 py-2 rounded-xl transition-all duration-300 border border-white/20" | ||||||
|  |                               > | ||||||
|  |                                 {icon} | ||||||
|  |                                 {label} | ||||||
|  |                               </a> | ||||||
|  |                             ); | ||||||
|  |                           } | ||||||
|  |                         )} | ||||||
|  |                       </div> | ||||||
|  |                     </div> | ||||||
|  |                   )} | ||||||
|  |                 </div> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </section> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const externalIdsMap = { | ||||||
|  |   facebook_id: { | ||||||
|  |     label: "Facebook", | ||||||
|  |     icon: <FaFacebook />, | ||||||
|  |     url: (id: string) => `https://www.facebook.com/${id}`, | ||||||
|  |   }, | ||||||
|  |   instagram_id: { | ||||||
|  |     label: "Instagram", | ||||||
|  |     icon: <FaInstagram />, | ||||||
|  |     url: (id: string) => `https://www.instagram.com/${id}`, | ||||||
|  |   }, | ||||||
|  |   twitter_id: { | ||||||
|  |     label: "Twitter", | ||||||
|  |     icon: <FaTwitter />, | ||||||
|  |     url: (id: string) => `https://www.twitter.com/${id}`, | ||||||
|  |   }, | ||||||
|  |   tiktok_id: { | ||||||
|  |     label: "TikTok", | ||||||
|  |     icon: <FaTiktok />, | ||||||
|  |     url: (id: string) => `https://www.tiktok.com/${id}`, | ||||||
|  |   }, | ||||||
|  |   youtube_id: { | ||||||
|  |     label: "YouTube", | ||||||
|  |     icon: <FaYoutube />, | ||||||
|  |     url: (id: string) => `https://www.youtube.com/${id}`, | ||||||
|  |   }, | ||||||
|  |   imdb_id: { | ||||||
|  |     label: "IMDb", | ||||||
|  |     icon: <FaImdb />, | ||||||
|  |     url: (id: string) => `https://www.imdb.com/name/${id}`, | ||||||
|  |   }, | ||||||
|  |   tvrage_id: { | ||||||
|  |     label: "TVRage", | ||||||
|  |     icon: null, | ||||||
|  |     url: (id: string) => `https://www.tvrage.com/people/${id}`, | ||||||
|  |   }, | ||||||
|  |   wikidata_id: { | ||||||
|  |     label: "Wikidata", | ||||||
|  |     icon: null, | ||||||
|  |     url: (id: string) => `https://www.wikidata.org/wiki/${id}`, | ||||||
|  |   }, | ||||||
|  | }; | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| "use client"; | "use client"; | ||||||
|  | import Link from "next/link"; | ||||||
| import { Button } from "@/components/atoms/Button"; | import { Button } from "@/components/atoms/Button"; | ||||||
| import { MovieDetailsRich } from "@/lib/tmdb/types"; | import { MovieDetailsRich } from "@/lib/tmdb/types"; | ||||||
| import { FC, useState } from "react"; | import { FC, useState } from "react"; | ||||||
|  | @ -31,7 +32,11 @@ export const MovieCast: FC<Props> = ({ movieDetails }) => { | ||||||
|               <h2 className="text-2xl font-bold text-white mb-6">Obsada</h2> |               <h2 className="text-2xl font-bold text-white mb-6">Obsada</h2> | ||||||
|               <div className="grid grid-cols-2 md:grid-cols-4 gap-6"> |               <div className="grid grid-cols-2 md:grid-cols-4 gap-6"> | ||||||
|                 {mainCast.map((actor) => ( |                 {mainCast.map((actor) => ( | ||||||
|                   <div key={actor.id} className="text-center group"> |                   <Link | ||||||
|  |                     key={actor.id} | ||||||
|  |                     href={`/aktor/${actor.id}`} | ||||||
|  |                     className="text-center group block cursor-pointer" | ||||||
|  |                   > | ||||||
|                     <div className="relative overflow-hidden rounded-xl mb-3"> |                     <div className="relative overflow-hidden rounded-xl mb-3"> | ||||||
|                       <img |                       <img | ||||||
|                         style={{ |                         style={{ | ||||||
|  | @ -46,10 +51,19 @@ export const MovieCast: FC<Props> = ({ movieDetails }) => { | ||||||
|                         className="w-full object-cover group-hover:scale-110 transition-transform duration-500 bg-gradient-to-br from-purple-500/20 to-cyan-500/20" |                         className="w-full object-cover group-hover:scale-110 transition-transform duration-500 bg-gradient-to-br from-purple-500/20 to-cyan-500/20" | ||||||
|                       /> |                       /> | ||||||
|                       <div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" /> |                       <div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" /> | ||||||
|  | 
 | ||||||
|  |                       {/* Hover overlay with link indication */} | ||||||
|  |                       <div className="absolute inset-0 bg-purple-600/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center"> | ||||||
|  |                         <div className="bg-white/20 backdrop-blur-sm px-3 py-1 rounded-full text-white text-sm font-medium"> | ||||||
|  |                           Zobacz profil | ||||||
|                         </div> |                         </div> | ||||||
|                     <h4 className="font-semibold text-white">{actor.name}</h4> |                       </div> | ||||||
|  |                     </div> | ||||||
|  |                     <h4 className="font-semibold text-white group-hover:text-purple-300 transition-colors duration-300"> | ||||||
|  |                       {actor.name} | ||||||
|  |                     </h4> | ||||||
|                     <p className="text-sm text-gray-400">{actor.character}</p> |                     <p className="text-sm text-gray-400">{actor.character}</p> | ||||||
|                   </div> |                   </Link> | ||||||
|                 ))} |                 ))} | ||||||
|               </div> |               </div> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,12 +2,12 @@ import { | ||||||
|   search, |   search, | ||||||
|   getMovieDetails, |   getMovieDetails, | ||||||
|   getMovieCredits, |   getMovieCredits, | ||||||
|   getMovieDetailsRich, |   getPersonDetails, | ||||||
| } from "./server"; | } from "./server"; | ||||||
| 
 | 
 | ||||||
| export const TMDB = { | export const TMDB = { | ||||||
|   search, |   search, | ||||||
|   getMovieDetails, |   getMovieDetails, | ||||||
|   getMovieCredits, |   getMovieCredits, | ||||||
|   getMovieDetailsRich, |   getPersonDetails, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -5,6 +5,10 @@ import { | ||||||
|   MovieDetails, |   MovieDetails, | ||||||
|   MovieCredits, |   MovieCredits, | ||||||
|   MovieDetailsRich, |   MovieDetailsRich, | ||||||
|  |   PersonDetails, | ||||||
|  |   PersonMovieCredits, | ||||||
|  |   PersonImages, | ||||||
|  |   PersonDetailsRich, | ||||||
| } from "./types"; | } from "./types"; | ||||||
| 
 | 
 | ||||||
| const url = "https://api.themoviedb.org/3"; | const url = "https://api.themoviedb.org/3"; | ||||||
|  | @ -65,11 +69,7 @@ export async function getUpcomingMovies(): Promise<SearchResult> { | ||||||
|   return await fetchTmbd("/movie/upcoming?language=pl®ion=PL"); |   return await fetchTmbd("/movie/upcoming?language=pl®ion=PL"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export async function getMovieDetails(movieId: number): Promise<MovieDetails> { | export async function getMovieDetails( | ||||||
|   return await fetchTmbd(`/movie/${movieId}?language=pl&`); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export async function getMovieDetailsRich( |  | ||||||
|   movieId: number |   movieId: number | ||||||
| ): Promise<MovieDetailsRich> { | ): Promise<MovieDetailsRich> { | ||||||
|   return await fetchTmbd( |   return await fetchTmbd( | ||||||
|  | @ -80,3 +80,11 @@ export async function getMovieDetailsRich( | ||||||
| export async function getMovieCredits(movieId: number): Promise<MovieCredits> { | export async function getMovieCredits(movieId: number): Promise<MovieCredits> { | ||||||
|   return await fetchTmbd(`/movie/${movieId}/credits?language=pl`); |   return await fetchTmbd(`/movie/${movieId}/credits?language=pl`); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | export async function getPersonDetails( | ||||||
|  |   personId: number | ||||||
|  | ): Promise<PersonDetailsRich> { | ||||||
|  |   return await fetchTmbd( | ||||||
|  |     `/person/${personId}?language=pl&append_to_response=movie_credits,images,external_ids` | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -111,6 +111,98 @@ export type MovieCredits = { | ||||||
|   crew: CrewMember[]; |   crew: CrewMember[]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | export type PersonDetails = { | ||||||
|  |   adult: boolean; | ||||||
|  |   also_known_as: string[]; | ||||||
|  |   biography: string; | ||||||
|  |   birthday: string | null; | ||||||
|  |   deathday: string | null; | ||||||
|  |   gender: number; | ||||||
|  |   homepage: string | null; | ||||||
|  |   id: number; | ||||||
|  |   imdb_id: string | null; | ||||||
|  |   known_for_department: string; | ||||||
|  |   name: string; | ||||||
|  |   place_of_birth: string | null; | ||||||
|  |   popularity: number; | ||||||
|  |   profile_path: string | null; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export type PersonExternalIds = { | ||||||
|  |   id: number; | ||||||
|  |   freebase_mid: string | null; | ||||||
|  |   freebase_id: string | null; | ||||||
|  |   imdb_id: string | null; | ||||||
|  |   tvrage_id: number | null; | ||||||
|  |   wikidata_id: string | null; | ||||||
|  |   facebook_id: string | null; | ||||||
|  |   instagram_id: string | null; | ||||||
|  |   twitter_id: string | null; | ||||||
|  |   tiktok_id: string | null; | ||||||
|  |   youtube_id: string | null; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export type PersonMovieCredits = { | ||||||
|  |   cast: { | ||||||
|  |     adult: boolean; | ||||||
|  |     backdrop_path: string | null; | ||||||
|  |     genre_ids: number[]; | ||||||
|  |     id: number; | ||||||
|  |     original_language: string; | ||||||
|  |     original_title: string; | ||||||
|  |     overview: string; | ||||||
|  |     popularity: number; | ||||||
|  |     poster_path: string | null; | ||||||
|  |     release_date: string; | ||||||
|  |     title: string; | ||||||
|  |     video: boolean; | ||||||
|  |     vote_average: number; | ||||||
|  |     vote_count: number; | ||||||
|  |     character: string; | ||||||
|  |     credit_id: string; | ||||||
|  |     order: number; | ||||||
|  |   }[]; | ||||||
|  |   crew: { | ||||||
|  |     adult: boolean; | ||||||
|  |     backdrop_path: string | null; | ||||||
|  |     genre_ids: number[]; | ||||||
|  |     id: number; | ||||||
|  |     original_language: string; | ||||||
|  |     original_title: string; | ||||||
|  |     overview: string; | ||||||
|  |     popularity: number; | ||||||
|  |     poster_path: string | null; | ||||||
|  |     release_date: string; | ||||||
|  |     title: string; | ||||||
|  |     video: boolean; | ||||||
|  |     vote_average: number; | ||||||
|  |     vote_count: number; | ||||||
|  |     credit_id: string; | ||||||
|  |     department: string; | ||||||
|  |     job: string; | ||||||
|  |   }[]; | ||||||
|  |   id: number; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export type PersonImages = { | ||||||
|  |   id: number; | ||||||
|  |   profiles: { | ||||||
|  |     aspect_ratio: number; | ||||||
|  |     height: number; | ||||||
|  |     iso_639_1: string | null; | ||||||
|  |     file_path: string; | ||||||
|  |     vote_average: number; | ||||||
|  |     vote_count: number; | ||||||
|  |     width: number; | ||||||
|  |   }[]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export type PersonDetailsRich = PersonDetails & { | ||||||
|  |   movie_credits: PersonMovieCredits; | ||||||
|  |   images: PersonImages; | ||||||
|  |   external_ids: PersonExternalIds; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| export type MovieDetailsRich = MovieDetails & { | export type MovieDetailsRich = MovieDetails & { | ||||||
|   credits: MovieCredits; |   credits: MovieCredits; | ||||||
|   similar: SearchResult; |   similar: SearchResult; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue