| 1 | import React from 'react'
|
|---|
| 2 | import { useNavigate } from 'react-router-dom'
|
|---|
| 3 | import { Heart, MessageCircle, Eye, Lock } from 'lucide-react'
|
|---|
| 4 | import { Story } from '../../types'
|
|---|
| 5 | import { useAuthStore } from '../../store/authStore'
|
|---|
| 6 | import { StatusBadge, GenreBadge } from './Badge'
|
|---|
| 7 | import { getGenreGradient } from '../story/GenreBadge'
|
|---|
| 8 |
|
|---|
| 9 | interface StoryCardProps {
|
|---|
| 10 | story: Story
|
|---|
| 11 | showStatus?: boolean
|
|---|
| 12 | }
|
|---|
| 13 |
|
|---|
| 14 | export const StoryCard: React.FC<StoryCardProps> = ({ story, showStatus = false }) => {
|
|---|
| 15 | const navigate = useNavigate()
|
|---|
| 16 | const { currentUser, showMatureContent } = useAuthStore()
|
|---|
| 17 |
|
|---|
| 18 | const isMature = story.mature_content
|
|---|
| 19 | const isGuest = !currentUser
|
|---|
| 20 | const blurContent = isMature && isGuest && !showMatureContent
|
|---|
| 21 |
|
|---|
| 22 | const primaryGenre = story.genres[0] || 'Fantasy'
|
|---|
| 23 | const gradient = getGenreGradient(primaryGenre)
|
|---|
| 24 |
|
|---|
| 25 | return (
|
|---|
| 26 | <div
|
|---|
| 27 | className="relative group cursor-pointer rounded-xl overflow-hidden bg-slate-800 border border-slate-700 hover:border-indigo-500/50 transition-all duration-300 hover:scale-105 hover:shadow-xl hover:shadow-indigo-500/10"
|
|---|
| 28 | onClick={() => navigate(`/story/${story.story_id}`)}
|
|---|
| 29 | >
|
|---|
| 30 | {/* Cover */}
|
|---|
| 31 | <div className={`relative h-40 bg-gradient-to-br ${gradient} flex items-end p-4`}>
|
|---|
| 32 | {/* Mature ribbon */}
|
|---|
| 33 | {isMature && (
|
|---|
| 34 | <div className="absolute top-2 right-2 bg-rose-500/90 text-white text-xs px-2 py-0.5 rounded-full font-medium flex items-center gap-1">
|
|---|
| 35 | <Lock size={10} />
|
|---|
| 36 | 18+
|
|---|
| 37 | </div>
|
|---|
| 38 | )}
|
|---|
| 39 | {/* Status badge */}
|
|---|
| 40 | {showStatus && (
|
|---|
| 41 | <div className="absolute top-2 left-2">
|
|---|
| 42 | <StatusBadge status={story.status} />
|
|---|
| 43 | </div>
|
|---|
| 44 | )}
|
|---|
| 45 | {/* Blur overlay for mature on guest */}
|
|---|
| 46 | {blurContent && (
|
|---|
| 47 | <div className="absolute inset-0 backdrop-blur-md bg-slate-900/60 flex items-center justify-center">
|
|---|
| 48 | <div className="text-center">
|
|---|
| 49 | <Lock size={24} className="text-slate-400 mx-auto mb-1" />
|
|---|
| 50 | <p className="text-xs text-slate-400">Mature Content</p>
|
|---|
| 51 | <p className="text-xs text-slate-500">Login to view</p>
|
|---|
| 52 | </div>
|
|---|
| 53 | </div>
|
|---|
| 54 | )}
|
|---|
| 55 | {/* Genre decorative circles */}
|
|---|
| 56 | <div className="absolute top-4 left-4 w-20 h-20 rounded-full bg-white/5 blur-xl" />
|
|---|
| 57 | <div className="absolute bottom-2 right-8 w-12 h-12 rounded-full bg-white/5 blur-lg" />
|
|---|
| 58 | </div>
|
|---|
| 59 |
|
|---|
| 60 | {/* Content */}
|
|---|
| 61 | <div className="p-4">
|
|---|
| 62 | <h3 className="font-serif font-semibold text-white text-sm leading-tight mb-1 line-clamp-2 group-hover:text-indigo-300 transition-colors">
|
|---|
| 63 | {story.title}
|
|---|
| 64 | </h3>
|
|---|
| 65 | <p className="text-slate-500 text-xs mb-2">by {story.author_username}</p>
|
|---|
| 66 | <p className="text-slate-400 text-xs line-clamp-2 mb-3">{story.short_description}</p>
|
|---|
| 67 |
|
|---|
| 68 | {/* Genre badges */}
|
|---|
| 69 | <div className="flex flex-wrap gap-1 mb-3">
|
|---|
| 70 | {story.genres.slice(0, 2).map(g => (
|
|---|
| 71 | <GenreBadge key={g} genre={g} />
|
|---|
| 72 | ))}
|
|---|
| 73 | {story.genres.length > 2 && (
|
|---|
| 74 | <span className="text-xs text-slate-500">+{story.genres.length - 2}</span>
|
|---|
| 75 | )}
|
|---|
| 76 | </div>
|
|---|
| 77 |
|
|---|
| 78 | {/* Stats */}
|
|---|
| 79 | <div className="flex items-center gap-3 text-slate-500 text-xs">
|
|---|
| 80 | <span className="flex items-center gap-1">
|
|---|
| 81 | <Heart size={12} />
|
|---|
| 82 | {story.total_likes.toLocaleString()}
|
|---|
| 83 | </span>
|
|---|
| 84 | <span className="flex items-center gap-1">
|
|---|
| 85 | <MessageCircle size={12} />
|
|---|
| 86 | {story.total_comments}
|
|---|
| 87 | </span>
|
|---|
| 88 | <span className="flex items-center gap-1 ml-auto">
|
|---|
| 89 | <Eye size={12} />
|
|---|
| 90 | {story.total_views.toLocaleString()}
|
|---|
| 91 | </span>
|
|---|
| 92 | </div>
|
|---|
| 93 | </div>
|
|---|
| 94 | </div>
|
|---|
| 95 | )
|
|---|
| 96 | }
|
|---|