/** * Component: Audiobook Card * Documentation: documentation/frontend/components.md */ 'use client'; import React, { useState } from 'react'; import Image from 'next/image'; import { Button } from '@/components/ui/Button'; import { StatusBadge } from '@/components/requests/StatusBadge'; import { AudiobookDetailsModal } from '@/components/audiobooks/AudiobookDetailsModal'; import { useCreateRequest } from '@/lib/hooks/useRequests'; import { useAuth } from '@/contexts/AuthContext'; import { Audiobook } from '@/lib/hooks/useAudiobooks'; interface AudiobookCardProps { audiobook: Audiobook; isRequested?: boolean; requestStatus?: string; onRequestSuccess?: () => void; } export function AudiobookCard({ audiobook, isRequested = false, requestStatus, onRequestSuccess, }: AudiobookCardProps) { const { user } = useAuth(); const { createRequest, isLoading } = useCreateRequest(); const [showToast, setShowToast] = useState(false); const [error, setError] = useState(null); const [showModal, setShowModal] = useState(false); const handleRequest = async () => { if (!user) { setError('Please log in to request audiobooks'); return; } try { await createRequest(audiobook); setShowToast(true); setTimeout(() => setShowToast(false), 3000); onRequestSuccess?.(); } catch (err) { setError(err instanceof Error ? err.message : 'Failed to create request'); setTimeout(() => setError(null), 5000); } }; const formatDuration = (minutes?: number) => { if (!minutes) return null; const hours = Math.floor(minutes / 60); const mins = minutes % 60; return `${hours}h ${mins}m`; }; return ( <>
{/* Cover Art - Clickable */}
setShowModal(true)} > {audiobook.coverArtUrl ? ( {`Cover ) : (
)} {/* Hover overlay for click hint */}
{/* Availability Badge */} {audiobook.isAvailable && (
Available
)}
{/* Content */}
{/* Title - Clickable */}

setShowModal(true)} > {audiobook.title}

{/* Author */}

By {audiobook.author}

{/* Narrator */} {audiobook.narrator && (

Narrated by {audiobook.narrator}

)} {/* Metadata Row - Fixed height for alignment */}
{/* Rating - Only show if > 0 (0 means no rating) */} {audiobook.rating && audiobook.rating > 0 && (
{audiobook.rating.toFixed(1)}
)}
{/* Status or Action */}
{(() => { // Check if book is already available in Plex or completed/available status if (audiobook.isAvailable || audiobook.requestStatus === 'completed') { return (
In Your Library
); } // Check if book is requested and in progress (non-re-requestable statuses) const inProgressStatuses = ['pending', 'awaiting_search', 'searching', 'downloading', 'processing', 'awaiting_import']; if (audiobook.isRequested && audiobook.requestStatus && inProgressStatuses.includes(audiobook.requestStatus)) { // Show who requested it const buttonText = audiobook.requestedByUsername ? `Requested by ${audiobook.requestedByUsername}` : 'Requested'; return ( ); } // For failed/warn/cancelled or no request - show Request button return ( ); })()}
{/* Error Message */} {error && (

{error}

)} {/* Success Toast */} {showToast && (

✓ Request created successfully!

)}
{/* Details Modal */} setShowModal(false)} onRequestSuccess={onRequestSuccess} isRequested={audiobook.isRequested} requestStatus={audiobook.requestStatus} isAvailable={audiobook.isAvailable} requestedByUsername={audiobook.requestedByUsername} /> ); }