- Timestamp:
- 06/24/26 16:51:52 (11 days ago)
- Branches:
- main
- Children:
- 3ae4bab
- Parents:
- 99c1e45
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
chapterx-frontend/src/components/writer/StoryAnalytics.tsx
r99c1e45 ra8f4a2d 9 9 ResponsiveContainer, 10 10 } from 'recharts' 11 import { Heart, MessageCircle, BookOpen } from 'lucide-react'11 import { Heart, MessageCircle, BookOpen, Eye } from 'lucide-react' 12 12 import { Story } from '../../types' 13 13 … … 50 50 const totalComments = stories.reduce((a, s) => a + s.total_comments, 0) 51 51 const totalChapters = stories.reduce((a, s) => a + s.total_chapters, 0) 52 const totalViews = stories.reduce((a, s) => a + s.total_views, 0) 52 53 53 // Likes per story (sorted by created_at) 54 const likesData = [...published] 55 .sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()) 56 .map(s => ({ 57 date: new Date(s.created_at).toLocaleDateString('en-US', { month: 'short', year: '2-digit' }), 58 likes: s.total_likes, 59 story: s.title, 60 })) 54 const sortedPublished = [...published].sort( 55 (a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime() 56 ) 57 58 const likesData = sortedPublished.map(s => ({ 59 date: new Date(s.created_at).toLocaleDateString('en-US', { month: 'short', year: '2-digit' }), 60 likes: s.total_likes, 61 story: s.title, 62 })) 63 64 const viewsData = sortedPublished.map(s => ({ 65 date: new Date(s.created_at).toLocaleDateString('en-US', { month: 'short', year: '2-digit' }), 66 views: s.total_views, 67 story: s.title, 68 })) 61 69 62 70 if (published.length === 0) { … … 71 79 <div className="space-y-6"> 72 80 {/* Stat cards */} 73 <div className="grid grid-cols-2 lg:grid-cols-3 gap-4"> 81 <div className="grid grid-cols-2 lg:grid-cols-4 gap-4"> 82 <StatCard icon={<Eye size={18} className="text-cyan-300" />} label="Total Views" value={totalViews} color="bg-cyan-500/20" /> 74 83 <StatCard icon={<Heart size={18} className="text-rose-300" />} label="Total Likes" value={totalLikes} color="bg-rose-500/20" /> 75 84 <StatCard icon={<MessageCircle size={18} className="text-violet-300" />} label="Total Comments" value={totalComments} color="bg-violet-500/20" /> 76 85 <StatCard icon={<BookOpen size={18} className="text-emerald-300" />} label="Total Chapters" value={totalChapters} color="bg-emerald-500/20" /> 86 </div> 87 88 {/* Views chart */} 89 <div className="bg-slate-800 border border-slate-700 rounded-2xl p-6"> 90 <div className="flex items-center gap-2 mb-4"> 91 <Eye size={16} className="text-cyan-400" /> 92 <h3 className="text-white font-semibold">Views per Story</h3> 93 </div> 94 {viewsData.length > 0 ? ( 95 <ResponsiveContainer width="100%" height={200}> 96 <BarChart data={viewsData}> 97 <CartesianGrid strokeDasharray="3 3" stroke="#334155" /> 98 <XAxis dataKey="date" tick={{ fill: '#64748b', fontSize: 11 }} tickLine={false} axisLine={false} /> 99 <YAxis tick={{ fill: '#64748b', fontSize: 11 }} tickLine={false} axisLine={false} /> 100 <Tooltip content={<CustomTooltip />} /> 101 <Bar dataKey="views" fill="#06b6d4" radius={[4, 4, 0, 0]} name="Views" /> 102 </BarChart> 103 </ResponsiveContainer> 104 ) : ( 105 <p className="text-slate-500 text-sm text-center py-8">No views yet</p> 106 )} 77 107 </div> 78 108 … … 109 139 <span className="text-slate-300 truncate max-w-xs">{s.title}</span> 110 140 <div className="flex items-center gap-4 text-slate-400 flex-shrink-0"> 141 <span className="flex items-center gap-1"><Eye size={12} /> {s.total_views.toLocaleString()}</span> 111 142 <span className="flex items-center gap-1"><Heart size={12} /> {s.total_likes}</span> 112 143 <span className="flex items-center gap-1"><MessageCircle size={12} /> {s.total_comments}</span>
Note:
See TracChangeset
for help on using the changeset viewer.
