Changeset a8f4a2d


Ignore:
Timestamp:
06/24/26 16:51:52 (11 days ago)
Author:
kikisrbinoska <srbinoskakristina07@…>
Branches:
main
Children:
3ae4bab
Parents:
99c1e45
Message:

Fixed views count

Location:
chapterx-frontend/src
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • chapterx-frontend/src/components/admin/PlatformStats.tsx

    r99c1e45 ra8f4a2d  
    11import React, { useEffect } from 'react'
    2 import { Users, BookOpen, Heart, MessageCircle } from 'lucide-react'
     2import { Users, BookOpen, Heart, MessageCircle, Eye } from 'lucide-react'
    33import { useAuthStore } from '../../store/authStore'
    44import { useStoryStore } from '../../store/storyStore'
     
    1212  const totalLikes = stories.reduce((acc, s) => acc + s.total_likes, 0)
    1313  const totalComments = stories.reduce((acc, s) => acc + s.total_comments, 0)
     14  const totalViews = stories.reduce((acc, s) => acc + s.total_views, 0)
    1415  const published = stories.filter(s => s.status === 'published').length
    1516
     
    1718    { icon: <Users size={24} className="text-blue-300" />, label: 'Total Users', value: allUsers.length, color: 'bg-blue-500/20', change: `${allUsers.filter(u => u.role === 'writer').length} writers` },
    1819    { icon: <BookOpen size={24} className="text-violet-300" />, label: 'Total Stories', value: stories.length, color: 'bg-violet-500/20', change: `${published} published` },
     20    { icon: <Eye size={24} className="text-cyan-300" />, label: 'Total Views', value: totalViews.toLocaleString(), color: 'bg-cyan-500/20', change: 'Chapter reads' },
    1921    { icon: <Heart size={24} className="text-rose-300" />, label: 'Total Likes', value: totalLikes.toLocaleString(), color: 'bg-rose-500/20', change: 'Across all stories' },
    2022    { icon: <MessageCircle size={24} className="text-emerald-300" />, label: 'Total Comments', value: totalComments.toLocaleString(), color: 'bg-emerald-500/20', change: 'Platform-wide' },
     
    2224
    2325  return (
    24     <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
     26    <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4">
    2527      {stats.map(stat => (
    2628        <div key={stat.label} className="bg-slate-800 border border-slate-700 rounded-2xl p-6">
  • chapterx-frontend/src/components/writer/StoryAnalytics.tsx

    r99c1e45 ra8f4a2d  
    99  ResponsiveContainer,
    1010} from 'recharts'
    11 import { Heart, MessageCircle, BookOpen } from 'lucide-react'
     11import { Heart, MessageCircle, BookOpen, Eye } from 'lucide-react'
    1212import { Story } from '../../types'
    1313
     
    5050  const totalComments = stories.reduce((a, s) => a + s.total_comments, 0)
    5151  const totalChapters = stories.reduce((a, s) => a + s.total_chapters, 0)
     52  const totalViews = stories.reduce((a, s) => a + s.total_views, 0)
    5253
    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  }))
    6169
    6270  if (published.length === 0) {
     
    7179    <div className="space-y-6">
    7280      {/* 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" />
    7483        <StatCard icon={<Heart size={18} className="text-rose-300" />} label="Total Likes" value={totalLikes} color="bg-rose-500/20" />
    7584        <StatCard icon={<MessageCircle size={18} className="text-violet-300" />} label="Total Comments" value={totalComments} color="bg-violet-500/20" />
    7685        <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        )}
    77107      </div>
    78108
     
    109139                  <span className="text-slate-300 truncate max-w-xs">{s.title}</span>
    110140                  <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>
    111142                    <span className="flex items-center gap-1"><Heart size={12} /> {s.total_likes}</span>
    112143                    <span className="flex items-center gap-1"><MessageCircle size={12} /> {s.total_comments}</span>
  • chapterx-frontend/src/pages/profile/ProfilePage.tsx

    r99c1e45 ra8f4a2d  
    11import React, { useState, useEffect } from 'react'
    22import { useParams, useNavigate } from 'react-router-dom'
    3 import { BookOpen, Heart, Calendar, MessageCircle } from 'lucide-react'
     3import { BookOpen, Heart, Calendar, MessageCircle, Eye } from 'lucide-react'
    44import { useAuthStore } from '../../store/authStore'
    55import { useStoryStore } from '../../store/storyStore'
     
    7676  const totalLikes = userStories.reduce((acc, s) => acc + s.total_likes, 0)
    7777  const totalComments = userStories.reduce((acc, s) => acc + s.total_comments, 0)
     78  const totalViews = userStories.reduce((acc, s) => acc + s.total_views, 0)
    7879  const allGenres = [...new Set(userStories.flatMap(s => s.genres))]
    7980
     
    120121
    121122      {/* Stats */}
    122       <div className="grid grid-cols-3 gap-4 mb-8">
     123      <div className="grid grid-cols-2 sm:grid-cols-4 gap-4 mb-8">
    123124        {[
    124125          { icon: <BookOpen size={16} className="text-indigo-400" />, value: userStories.length, label: 'Stories' },
    125126          { icon: <Heart size={16} className="text-rose-400" />, value: totalLikes.toLocaleString(), label: 'Likes' },
    126127          { icon: <MessageCircle size={16} className="text-amber-400" />, value: totalComments, label: 'Comments' },
     128          { icon: <Eye size={16} className="text-cyan-400" />, value: totalViews.toLocaleString(), label: 'Views' },
    127129        ].map(s => (
    128130          <div key={s.label} className="bg-slate-800 border border-slate-700 rounded-xl p-4 text-center">
  • chapterx-frontend/src/pages/story/ChapterReadPage.tsx

    r99c1e45 ra8f4a2d  
    11import React, { useEffect, useState } from 'react'
    22import { useParams, useNavigate } from 'react-router-dom'
    3 import { ArrowLeft, ArrowRight, BookOpen, Eye, ChevronLeft, ChevronRight } from 'lucide-react'
     3import { ArrowLeft, ChevronLeft, ChevronRight } from 'lucide-react'
     4import axios from 'axios'
    45import { useStoryStore } from '../../store/storyStore'
     6import { Chapter } from '../../types'
    57import { Button } from '../../components/ui/Button'
     8
     9const API = 'https://localhost:7125/api'
     10
     11function mapChapter(c: any): Chapter {
     12  return {
     13    chapter_id: c.id ?? c.chapter_id,
     14    story_id: c.storyId ?? c.story_id,
     15    title: c.title ?? c.name ?? '',
     16    content: c.content ?? '',
     17    chapter_number: c.number ?? c.chapter_number ?? 0,
     18    word_count: c.wordCount ?? c.word_count ?? 0,
     19    view_count: c.viewCount ?? c.view_count ?? 0,
     20    is_published: true,
     21    created_at: c.createdAt ?? c.created_at ?? '',
     22    updated_at: c.updatedAt ?? c.updated_at ?? '',
     23  }
     24}
    625
    726export const ChapterReadPage: React.FC = () => {
     
    1130  const [scrollProgress, setScrollProgress] = useState(0)
    1231  const [viewed, setViewed] = useState(false)
     32  const [chapter, setChapter] = useState<Chapter | null>(null)
     33  const [loading, setLoading] = useState(true)
     34
     35  useEffect(() => {
     36    setLoading(true)
     37    setViewed(false)
     38    setScrollProgress(0)
     39    axios.get(`${API}/chapters/${chapterId}`)
     40      .then(res => {
     41        const data = res.data?.chapter ?? res.data
     42        setChapter(mapChapter(data))
     43      })
     44      .catch(() => {
     45        const fallback = chapters.find(c => c.chapter_id === Number(chapterId))
     46        if (fallback) setChapter(fallback)
     47      })
     48      .finally(() => setLoading(false))
     49  }, [chapterId])
    1350
    1451  const story = stories.find(s => s.story_id === Number(storyId))
    15   const chapter = chapters.find(c => c.chapter_id === Number(chapterId))
    1652  const storyChapters = chapters
    1753    .filter(c => c.story_id === Number(storyId) && c.is_published)
     
    3773    return () => window.removeEventListener('scroll', handler)
    3874  }, [chapter, viewed, incrementViewCount])
     75
     76  if (loading) {
     77    return (
     78      <div className="max-w-4xl mx-auto px-4 py-20 text-center">
     79        <p className="text-slate-400">Loading chapter...</p>
     80      </div>
     81    )
     82  }
    3983
    4084  if (!story || !chapter) {
     
    73117          </div>
    74118
    75           <div className="flex items-center gap-1 text-slate-400 text-xs">
    76             <Eye size={12} />
    77             {scrollProgress.toFixed(0)}%
    78           </div>
     119          <div />
    79120        </div>
    80121      </div>
     
    90131            {chapter.title}
    91132          </h1>
    92           <div className="flex items-center justify-center gap-4 text-slate-500 text-sm">
    93             <span className="flex items-center gap-1">
    94               <BookOpen size={13} />
    95               {chapter.word_count.toLocaleString()} words
    96             </span>
    97             <span className="flex items-center gap-1">
    98               <Eye size={13} />
    99               {chapter.view_count.toLocaleString()} reads
    100             </span>
    101           </div>
    102133          <div className="mt-4 w-16 h-px bg-gradient-to-r from-transparent via-indigo-500 to-transparent mx-auto" />
    103134        </div>
  • chapterx-frontend/src/pages/story/StoryDetailPage.tsx

    r99c1e45 ra8f4a2d  
    217217                <span className="text-slate-400">Comments</span>
    218218                <span className="text-white">{liveComments ?? story.total_comments}</span>
     219              </div>
     220              <div className="flex justify-between">
     221                <span className="text-slate-400">Views</span>
     222                <span className="text-white">{story.total_views.toLocaleString()}</span>
    219223              </div>
    220224              <div className="flex justify-between">
  • chapterx-frontend/src/pages/writer/WriterDashboard.tsx

    r99c1e45 ra8f4a2d  
    11import React, { useEffect } from 'react'
    22import { useNavigate } from 'react-router-dom'
    3 import { Plus, BookOpen, Heart, MessageCircle, TrendingUp, Bell } from 'lucide-react'
     3import { Plus, BookOpen, Heart, MessageCircle, TrendingUp, Bell, Eye } from 'lucide-react'
    44import { useAuthStore } from '../../store/authStore'
    55import { useStoryStore } from '../../store/storyStore'
     
    3737  const drafts = myStories.filter(s => s.status === 'draft')
    3838  const totalLikes = myStories.reduce((acc, s) => acc + s.total_likes, 0)
     39  const totalViews = myStories.reduce((acc, s) => acc + s.total_views, 0)
    3940
    4041  const recentNotifs = notifications.slice(0, 5)
     
    5758
    5859      {/* Stats */}
    59       <div className="grid grid-cols-2 lg:grid-cols-3 gap-4 mb-8">
     60      <div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-8">
    6061        {[
    6162          { icon: <BookOpen size={20} className="text-indigo-300" />, label: 'Total Stories', value: myStories.length, sub: `${published.length} published`, color: 'bg-indigo-500/20' },
    6263          { icon: <Heart size={20} className="text-rose-300" />, label: 'Total Likes', value: totalLikes.toLocaleString(), sub: 'Across all stories', color: 'bg-rose-500/20' },
     64          { icon: <Eye size={20} className="text-cyan-300" />, label: 'Total Views', value: totalViews.toLocaleString(), sub: 'Chapter reads', color: 'bg-cyan-500/20' },
    6365          { icon: <TrendingUp size={20} className="text-emerald-300" />, label: 'Drafts', value: drafts.length, sub: 'In progress', color: 'bg-emerald-500/20' },
    6466        ].map(s => (
     
    110112                        <span className="flex items-center gap-1"><Heart size={11} /> {story.total_likes}</span>
    111113                        <span className="flex items-center gap-1"><MessageCircle size={11} /> {story.total_comments}</span>
     114                        <span className="flex items-center gap-1"><Eye size={11} /> {story.total_views.toLocaleString()}</span>
    112115                        <span>{story.total_chapters} chapters</span>
    113116                      </div>
Note: See TracChangeset for help on using the changeset viewer.