import React from 'react'
import { motion } from 'framer-motion'
import { useParams, useNavigate } from 'react-router-dom'
import {
ArrowLeftIcon,
PlayIcon,
CalendarIcon,
UserIcon,
FilmIcon,
PlusIcon,
CheckIcon,
ExclamationTriangleIcon,
LockClosedIcon
} from '@heroicons/react/24/outline'
import { StarIcon as StarIconSolid } from '@heroicons/react/24/solid'
import { useAdult } from '../hooks/useApi'
import { Tooltip } from '../components/MicroInteractions'
class AdultDetailErrorBoundary extends React.Component<
{ children: React.ReactNode; navigate: (to: string) => void },
{ hasError: boolean; error?: Error }
> {
constructor(props: any) {
super(props)
this.state = { hasError: false }
}
static getDerivedStateFromError(error: Error) {
return { hasError: true, error }
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('AdultDetail error:', error, errorInfo)
}
render() {
if (this.state.hasError) {
return (
Content Error
{this.state.error?.message || 'Failed to load content details'}
)
}
return this.props.children
}
}
export default function AdultDetail() {
const { id } = useParams<{ id: string }>()
const navigate = useNavigate()
// Validate ID and handle invalid cases
const contentId = id ? Number(id) : null
const isValidId = contentId && !isNaN(contentId) && contentId > 0
// Early return for invalid ID
if (!isValidId) {
return (
🔒
Invalid Content ID
The content ID is invalid or missing.
)
}
const { data: adultContent, isLoading, error } = useAdult(contentId)
if (isLoading) {
return (
)
}
if (error || !adultContent) {
return (
🔒
Content Not Found
The content you're looking for doesn't exist or has been removed.
)
}
const posterUrl = adultContent.poster_url || adultContent.screenshot_url ?
((adultContent.poster_url || adultContent.screenshot_url).startsWith('http') ? (adultContent.poster_url || adultContent.screenshot_url) : `${(import.meta as any).env?.VITE_API_URL?.replace('/api', '') || ''}${adultContent.poster_url || adultContent.screenshot_url}`) :
null
const backdropUrl = adultContent.backdrop_url ?
(adultContent.backdrop_url.startsWith('http') ? adultContent.backdrop_url : `${(import.meta as any).env?.VITE_API_URL?.replace('/api', '') || ''}${adultContent.backdrop_url}`) :
null
const getPosterAspectRatio = () => {
// Check for poster_aspect_ratio first, then fall back to 2/3
return adultContent.poster_aspect_ratio || 2/3
}
const formatYear = (date?: string) => {
if (!date) return 'Unknown'
return new Date(date).getFullYear()
}
const formatDate = (date?: string) => {
if (!date) return 'Unknown'
return new Date(date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
})
}
const formatRuntime = (minutes?: number) => {
if (!minutes) return 'Unknown'
const hours = Math.floor(minutes / 60)
const mins = minutes % 60
return `${hours}h ${mins}m`
}
return (
{/* Hero Section - Compact */}
{backdropUrl ? (
<>
>
) : (
)}
{/* Back Button */}
navigate(-1)}
className="absolute top-4 left-4 p-2 bg-black/50 dark:bg-black/70 backdrop-blur-sm rounded-lg text-white hover:bg-black/70 dark:hover:bg-black/80 transition-all duration-200"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
{/* Play Button - Compact */}
{/* Main Content - Compact */}
{/* Poster and Basic Info - Compact */}
{posterUrl ? (
{
console.error('Failed to load poster:', posterUrl, 'for adult content:', adultContent.title)
// Set a fallback background
const target = e.target as HTMLImageElement
target.style.display = 'none'
const parent = target.parentElement
if (parent) {
parent.className = 'w-full bg-gray-200 dark:bg-gray-800 rounded-lg flex items-center justify-center mb-4'
parent.style.aspectRatio = getPosterAspectRatio().toString()
const iconDiv = document.createElement('div')
iconDiv.className = 'text-gray-400 dark:text-gray-600'
iconDiv.innerHTML = ''
parent.appendChild(iconDiv)
}
}}
/>
) : (
)}
{adultContent.title}
{adultContent.watched && (
)}
{formatYear(adultContent.release_date)}
Adult Content
{adultContent.rating && (
{[...Array(5)].map((_, i) => (
))}
{Number(adultContent.rating)?.toFixed(1)}
)}
{/* Details - Compact */}
Description
{adultContent.overview || 'No description available for this content.'}
Details
Release Date
{formatDate(adultContent.release_date)}
Duration
{adultContent.runtime_minutes ? formatRuntime(adultContent.runtime_minutes) : 'Unknown'}
Rating
{adultContent.rating ? `${adultContent.rating}/10` : 'Not Rated'}
Source
{adultContent.source_name || 'Unknown'}
Cast & Actors
{adultContent.actors && adultContent.actors.length > 0 ? (
{adultContent.actors.map((actor: any) => (
navigate(`/actors/${actor.id}`)}
className="bg-gray-50 dark:bg-gray-800 rounded-lg p-3 border border-gray-200 dark:border-gray-700 hover:shadow-lg transition-all cursor-pointer"
>
{actor.thumbnail_path ? (
)
{
console.error('Failed to load actor thumbnail:', actor.thumbnail_path, 'for actor:', actor.name)
const target = e.target as HTMLImageElement
target.style.display = 'none'
const parent = target.parentElement
if (parent) {
parent.innerHTML = '
'
}
}}
/>
) : (
)}
{actor.name}
{actor.metadata?.age && (
Age: {actor.metadata.age}
)}
))}
) : (
No actor information available for this content.
)}
Content Warning
Adult Content - 18+ Only
This content is intended for mature audiences only. Viewer discretion is advised.
)
}