import { cn } from "@/lib/utils"; import { cva, type VariantProps } from "class-variance-authority"; import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { BookOpen, Heart, Bookmark, Share2, Calendar, Clock, Globe, MoreHorizontal, ChevronDown, ChevronUp, MessageSquare } from "lucide-react"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Separator } from "@/components/ui/separator"; /** * Work Header component for displaying work information at the top of work detail pages * * @example * ```tsx * console.log("Liked")} * onBookmark={() => console.log("Bookmarked")} * /> * ``` */ const workHeaderVariants = cva( "w-full", { variants: { variant: { default: "py-6", compact: "py-4", detailed: "py-8", minimal: "py-3", }, border: { none: "", bottom: "border-b", }, }, defaultVariants: { variant: "default", border: "bottom", }, } ); export interface Author { id: number | string; name: string; avatar?: string; slug?: string; } export interface WorkStats { likes?: number; bookmarks?: number; comments?: number; views?: number; reads?: number; } export interface Work { id: number | string; title: string; subtitle?: string; slug?: string; author: Author; coverImage?: string; publicationYear?: number; language?: string; genres?: string[]; translators?: string[]; description?: string; stats?: WorkStats; isLiked?: boolean; isBookmarked?: boolean; status?: 'published' | 'draft' | 'review' | 'archived'; translationCount?: number; readingTime?: number; } export interface WorkHeaderProps extends React.HTMLAttributes, VariantProps { /** * Work data to display */ work: Work; /** * Whether to show action buttons */ showActions?: boolean; /** * Whether to show the like button */ showLike?: boolean; /** * Whether to show the bookmark button */ showBookmark?: boolean; /** * Whether to show the share button */ showShare?: boolean; /** * Whether to collapse the description by default */ collapseDescription?: boolean; /** * Maximum length of description before truncating */ descriptionLength?: number; /** * Maximum number of genres to display */ maxGenres?: number; /** * Click handler for like button */ onLike?: (work: Work) => void; /** * Click handler for bookmark button */ onBookmark?: (work: Work) => void; /** * Click handler for share button */ onShare?: (work: Work) => void; /** * Click handler for author */ onAuthorClick?: (author: Author) => void; /** * Whether the component is in loading state */ isLoading?: boolean; } export function WorkHeader({ className, variant, border, work, showActions = true, showLike = true, showBookmark = true, showShare = true, collapseDescription = true, descriptionLength = 280, maxGenres = 5, onLike, onBookmark, onShare, onAuthorClick, isLoading = false, ...props }: WorkHeaderProps) { const [isLiked, setIsLiked] = useState(work.isLiked || false); const [isBookmarked, setIsBookmarked] = useState(work.isBookmarked || false); const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(!collapseDescription); // Handle truncation of description const hasLongDescription = work.description && work.description.length > descriptionLength; const displayDescription = hasLongDescription && !isDescriptionExpanded && work.description ? `${work.description.substring(0, descriptionLength)}...` : work.description; // Handle like button click const handleLike = () => { setIsLiked(!isLiked); onLike?.(work); }; // Handle bookmark button click const handleBookmark = () => { setIsBookmarked(!isBookmarked); onBookmark?.(work); }; // Handle share button click const handleShare = () => { onShare?.(work); }; // Handle author click const handleAuthorClick = () => { onAuthorClick?.(work.author); }; // Handle description toggle const toggleDescription = () => { setIsDescriptionExpanded(!isDescriptionExpanded); }; return (
{/* Main title section */}
{/* Title and actions */}
{/* Work title */}

{work.title}

{/* Work subtitle */} {work.subtitle && (

{work.subtitle}

)}
{/* Action buttons (desktop) */} {showActions && variant !== "minimal" && (
{showLike && ( )} {showBookmark && ( )} {showShare && ( )} Add to collection Download Report issue
)}
{/* Author information */}
{work.author.name.charAt(0)} by {work.author.name} {/* Publication year, language */}
{work.publicationYear && (
{work.publicationYear}
)} {work.language && (
{work.language}
)} {work.readingTime && (
{work.readingTime} min read
)} {work.translationCount && work.translationCount > 0 && (
{work.translationCount} translation{work.translationCount !== 1 ? 's' : ''}
)}
{/* Work status badge */} {work.status && work.status !== 'published' && ( {work.status} )} {/* Genres */} {work.genres && work.genres.length > 0 && variant !== "minimal" && (
{work.genres.slice(0, maxGenres).map((genre, index) => ( {genre} ))} {work.genres.length > maxGenres && ( +{work.genres.length - maxGenres} more )}
)} {/* Description */} {work.description && variant === "detailed" && (

{displayDescription}

{hasLongDescription && ( )}
)} {/* Stats bar and mobile actions */} {(showActions || (work.stats && (work.stats.likes || work.stats.comments || work.stats.bookmarks))) && (
{/* Stats */} {work.stats && (
{typeof work.stats.likes === 'number' && (
{work.stats.likes}
)} {typeof work.stats.comments === 'number' && (
{work.stats.comments}
)} {typeof work.stats.bookmarks === 'number' && (
{work.stats.bookmarks}
)}
)} {/* Mobile actions */} {showActions && (
{showLike && ( )} {showBookmark && ( )} {showShare && ( )}
)}
)}
); } export default WorkHeader;