| [b62cefc] | 1 | import React from 'react'
|
|---|
| 2 | import { useNavigate } from 'react-router-dom'
|
|---|
| 3 | import { Eye, BookOpen, Lock } from 'lucide-react'
|
|---|
| 4 | import { Chapter } from '../../types'
|
|---|
| 5 |
|
|---|
| 6 | interface ChapterListProps {
|
|---|
| 7 | chapters: Chapter[]
|
|---|
| 8 | storyId: number
|
|---|
| 9 | showEditLinks?: boolean
|
|---|
| 10 | onEdit?: (chapterId: number) => void
|
|---|
| 11 | }
|
|---|
| 12 |
|
|---|
| 13 | function formatDate(str: string) {
|
|---|
| 14 | return new Date(str).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
|
|---|
| 15 | }
|
|---|
| 16 |
|
|---|
| 17 | export const ChapterList: React.FC<ChapterListProps> = ({ chapters, storyId, showEditLinks, onEdit }) => {
|
|---|
| 18 | const navigate = useNavigate()
|
|---|
| 19 | const sorted = [...chapters].sort((a, b) => a.chapter_number - b.chapter_number)
|
|---|
| 20 |
|
|---|
| 21 | if (sorted.length === 0) {
|
|---|
| 22 | return (
|
|---|
| 23 | <div className="flex flex-col items-center py-10 text-slate-500">
|
|---|
| 24 | <BookOpen size={36} className="mb-3 opacity-40" />
|
|---|
| 25 | <p>No chapters yet.</p>
|
|---|
| 26 | </div>
|
|---|
| 27 | )
|
|---|
| 28 | }
|
|---|
| 29 |
|
|---|
| 30 | return (
|
|---|
| 31 | <div className="space-y-2">
|
|---|
| 32 | {sorted.map(chapter => (
|
|---|
| 33 | <div
|
|---|
| 34 | key={chapter.chapter_id}
|
|---|
| 35 | className={`flex items-center gap-4 p-4 rounded-xl bg-slate-800 border border-slate-700 transition-all ${
|
|---|
| 36 | chapter.is_published
|
|---|
| 37 | ? 'hover:border-indigo-500/40 hover:bg-slate-700/50 cursor-pointer'
|
|---|
| 38 | : 'opacity-60'
|
|---|
| 39 | }`}
|
|---|
| 40 | onClick={() => {
|
|---|
| 41 | if (chapter.is_published) navigate(`/story/${storyId}/chapter/${chapter.chapter_id}`)
|
|---|
| 42 | }}
|
|---|
| 43 | >
|
|---|
| 44 | {/* Number */}
|
|---|
| 45 | <div className="w-10 h-10 rounded-lg bg-slate-700 flex items-center justify-center text-indigo-400 font-semibold text-sm flex-shrink-0">
|
|---|
| 46 | {chapter.chapter_number}
|
|---|
| 47 | </div>
|
|---|
| 48 |
|
|---|
| 49 | {/* Info */}
|
|---|
| 50 | <div className="flex-1 min-w-0">
|
|---|
| 51 | <div className="flex items-center gap-2">
|
|---|
| 52 | <h4 className="text-white text-sm font-medium truncate">{chapter.title}</h4>
|
|---|
| 53 | {!chapter.is_published && (
|
|---|
| 54 | <span className="flex items-center gap-1 text-amber-400 text-xs">
|
|---|
| 55 | <Lock size={11} />
|
|---|
| 56 | Draft
|
|---|
| 57 | </span>
|
|---|
| 58 | )}
|
|---|
| 59 | </div>
|
|---|
| 60 | <div className="flex items-center gap-3 text-slate-500 text-xs mt-0.5">
|
|---|
| 61 | <span>{chapter.word_count.toLocaleString()} words</span>
|
|---|
| 62 | <span>{formatDate(chapter.created_at)}</span>
|
|---|
| 63 | </div>
|
|---|
| 64 | </div>
|
|---|
| 65 |
|
|---|
| 66 | {/* Views */}
|
|---|
| 67 | <div className="flex items-center gap-1 text-slate-500 text-xs flex-shrink-0">
|
|---|
| 68 | <Eye size={12} />
|
|---|
| 69 | {chapter.view_count.toLocaleString()}
|
|---|
| 70 | </div>
|
|---|
| 71 |
|
|---|
| 72 | {/* Edit button */}
|
|---|
| 73 | {showEditLinks && onEdit && (
|
|---|
| 74 | <button
|
|---|
| 75 | onClick={(e) => { e.stopPropagation(); onEdit(chapter.chapter_id) }}
|
|---|
| 76 | className="text-indigo-400 hover:text-indigo-300 text-xs px-2 py-1 rounded bg-indigo-500/10 hover:bg-indigo-500/20 transition-colors flex-shrink-0"
|
|---|
| 77 | >
|
|---|
| 78 | Edit
|
|---|
| 79 | </button>
|
|---|
| 80 | )}
|
|---|
| 81 | </div>
|
|---|
| 82 | ))}
|
|---|
| 83 | </div>
|
|---|
| 84 | )
|
|---|
| 85 | }
|
|---|