11"use client"
22
3- import { useState , useEffect } from "react"
3+ import { useState , useEffect , useRef } from "react"
44import { Card , CardContent , CardDescription , CardHeader , CardTitle } from "@/components/ui/card"
55import { Badge } from "@/components/ui/badge"
66import { Button } from "@/components/ui/button"
@@ -19,6 +19,9 @@ import {
1919 ArrowUpRight ,
2020 ArrowDownRight ,
2121} from "lucide-react"
22+ import { createClient } from "@/lib/supabase/client"
23+ import type { BlogPost } from "@/components/data/blog-posts"
24+ import { RealtimeChannel } from "@supabase/supabase-js"
2225
2326type SupabaseUser = {
2427 created_at : string ;
@@ -94,37 +97,6 @@ const recentActivities = [
9497 }
9598]
9699
97- const topContent = [
98- {
99- title : "Building Scalable APIs with Node.js" ,
100- views : "12.5K" ,
101- engagement : "94%" ,
102- author : "Akshay Kumar" ,
103- status : "published" ,
104- } ,
105- {
106- title : "React 18 Features You Should Know" ,
107- views : "8.2K" ,
108- engagement : "87%" ,
109- author : "Akshay Kumar" ,
110- status : "published" ,
111- } ,
112- {
113- title : "Advanced TypeScript Patterns" ,
114- views : "6.8K" ,
115- engagement : "91%" ,
116- author : "Akshay Kumar" ,
117- status : "draft" ,
118- } ,
119- {
120- title : "Machine Learning with Python" ,
121- views : "9.1K" ,
122- engagement : "89%" ,
123- author : "Akshay Kumar" ,
124- status : "published" ,
125- } ,
126- ]
127-
128100const systemHealth = [
129101 {
130102 service : "API Server" ,
@@ -158,6 +130,12 @@ export default function AdminDashboard() {
158130 const [ totalUsers , setTotalUsers ] = useState < string | null > ( null )
159131 const [ totalUsersChange , setTotalUsersChange ] = useState < string > ( "" )
160132 const [ totalUsersTrend , setTotalUsersTrend ] = useState < "up" | "down" > ( "up" )
133+ const [ pageViews , setPageViews ] = useState < string | null > ( null )
134+ const [ pageViewsChange , setPageViewsChange ] = useState < string > ( "" )
135+ const [ pageViewsTrend , setPageViewsTrend ] = useState < "up" | "down" > ( "up" )
136+ const [ topContent , setTopContent ] = useState < BlogPost [ ] > ( [ ] )
137+ const supabaseRef = useRef < ReturnType < typeof createClient > | null > ( null )
138+ const likesChannelRef = useRef < RealtimeChannel | null > ( null )
161139
162140 useEffect ( ( ) => {
163141 // Fetch real total users and previous month users
@@ -193,6 +171,70 @@ export default function AdminDashboard() {
193171 }
194172 }
195173 } )
174+ // Fetch total page views and previous month views
175+ fetch ( "/api/admin-page-views" )
176+ . then ( res => res . json ( ) )
177+ . then ( data => {
178+ if ( typeof data . totalViews === "number" ) {
179+ setPageViews ( data . totalViews . toLocaleString ( ) ) ;
180+ // Optionally, you can fetch previous month views for change/trend
181+ // For now, just set as N/A
182+ setPageViewsChange ( "N/A" ) ;
183+ setPageViewsTrend ( "up" ) ;
184+ }
185+ } )
186+ // Fetch top performing content (top 4 by views)
187+ const fetchTopContentWithLikes = async ( ) => {
188+ const supabase = createClient ( ) ;
189+ // Get top 4 blogs by views
190+ const { data : blogs , error } = await supabase
191+ . from ( "blogs" )
192+ . select ( "id, title, author, views, slug" )
193+ . order ( "views" , { ascending : false } )
194+ . limit ( 4 ) ;
195+ if ( ! error && blogs ) {
196+ const slugs : string [ ] = ( blogs as Pick < BlogPost , "slug" > [ ] ) ?. map ( ( b ) => b . slug )
197+ const { data : likesData } = await supabase
198+ . from ( "blog_likes" )
199+ . select ( "blog_slug" )
200+ . in ( "blog_slug" , slugs )
201+ const likesCount : Record < string , number > = { }
202+ if ( Array . isArray ( likesData ) ) {
203+ likesData . forEach ( ( like : { blog_slug : string } ) => {
204+ likesCount [ like . blog_slug ] = ( likesCount [ like . blog_slug ] || 0 ) + 1
205+ } )
206+ }
207+ setTopContent (
208+ ( blogs as ( Pick < BlogPost , "id" | "title" | "author" | "views" | "slug" > ) [ ] ) . map ( ( b ) => ( {
209+ ...b ,
210+ excerpt : "" ,
211+ content : "" ,
212+ date : "" ,
213+ readTime : "" ,
214+ category : "" ,
215+ tags : [ ] ,
216+ featured : false ,
217+ image : "" ,
218+ likes : likesCount [ b . slug ] || 0 ,
219+ } ) )
220+ )
221+ }
222+ }
223+ fetchTopContentWithLikes ( )
224+ // Setup realtime subscription
225+ const supabase = createClient ( )
226+ supabaseRef . current = supabase
227+ const channel = supabase . channel ( 'realtime:blog_likes' )
228+ . on ( 'postgres_changes' , { event : '*' , schema : 'public' , table : 'blog_likes' } , ( ) => {
229+ fetchTopContentWithLikes ( )
230+ } )
231+ . subscribe ( )
232+ likesChannelRef . current = channel
233+ return ( ) => {
234+ if ( likesChannelRef . current ) {
235+ supabase . removeChannel ( likesChannelRef . current )
236+ }
237+ }
196238 } , [ ] )
197239
198240 useEffect ( ( ) => {
@@ -241,12 +283,16 @@ export default function AdminDashboard() {
241283 }
242284 }
243285
244- // When rendering dashboardStats, override Total Users value if totalUsers is available
245- const statsToShow = dashboardStats . map ( stat =>
246- stat . title === "Total Users"
247- ? { ...stat , value : totalUsers ?? stat . value , change : totalUsersChange , trend : totalUsersTrend }
248- : stat
249- )
286+ // When rendering dashboardStats, override Total Users and Page Views value if available
287+ const statsToShow = dashboardStats . map ( stat => {
288+ if ( stat . title === "Total Users" ) {
289+ return { ...stat , value : totalUsers ?? stat . value , change : totalUsersChange , trend : totalUsersTrend }
290+ }
291+ if ( stat . title === "Page Views" ) {
292+ return { ...stat , value : pageViews ?? stat . value , change : pageViewsChange , trend : pageViewsTrend }
293+ }
294+ return stat
295+ } )
250296
251297 return (
252298 < div className = "bg-black space-y-8 md:space-y-14 min-h-screen px-4 py-8 md:px-8 lg:px-16 relative overflow-x-hidden" >
@@ -312,6 +358,8 @@ export default function AdminDashboard() {
312358 < div className = "text-2xl sm:text-3xl font-extrabold text-zinc-900 dark:text-white flex items-end gap-2 tracking-tight" >
313359 { stat . title === "Total Users" && totalUsers ? (
314360 < span > { totalUsers } </ span >
361+ ) : stat . title === "Page Views" && pageViews ? (
362+ < span > { pageViews } </ span >
315363 ) : stat . value . match ( / \d / ) ? (
316364 < span > { animatedStats [ i ] . toLocaleString ( ) } </ span >
317365 ) : (
@@ -421,14 +469,12 @@ export default function AdminDashboard() {
421469 < TableHead className = "text-zinc-700 dark:text-zinc-200 font-semibold text-xs sm:text-sm" > Title</ TableHead >
422470 < TableHead className = "text-zinc-700 dark:text-zinc-200 font-semibold text-xs sm:text-sm hidden sm:table-cell" > Author</ TableHead >
423471 < TableHead className = "text-zinc-700 dark:text-zinc-200 font-semibold text-xs sm:text-sm" > Views</ TableHead >
424- < TableHead className = "text-zinc-700 dark:text-zinc-200 font-semibold text-xs sm:text-sm hidden md:table-cell" > Engagement</ TableHead >
425- < TableHead className = "text-zinc-700 dark:text-zinc-200 font-semibold text-xs sm:text-sm" > Status</ TableHead >
426- < TableHead className = "text-right text-zinc-700 dark:text-zinc-200 font-semibold text-xs sm:text-sm" > Actions</ TableHead >
472+ < TableHead className = "text-zinc-700 dark:text-zinc-200 font-semibold text-xs sm:text-sm hidden md:table-cell" > Likes</ TableHead >
427473 </ TableRow >
428474 </ TableHeader >
429475 < TableBody >
430- { topContent . map ( ( content , index ) => (
431- < TableRow key = { index } className = "hover:bg-purple-700/10 transition-colors" >
476+ { topContent . map ( ( content ) => (
477+ < TableRow key = { content . id } className = "hover:bg-purple-700/10 transition-colors" >
432478 < TableCell className = "font-semibold text-zinc-900 dark:text-zinc-100 text-xs sm:text-sm" >
433479 < div className = "max-w-[150px] sm:max-w-none" >
434480 < div className = "truncate" > { content . title } </ div >
@@ -437,24 +483,7 @@ export default function AdminDashboard() {
437483 </ TableCell >
438484 < TableCell className = "text-zinc-800 dark:text-zinc-200 text-xs sm:text-sm hidden sm:table-cell" > { content . author } </ TableCell >
439485 < TableCell className = "text-zinc-800 dark:text-zinc-200 text-xs sm:text-sm" > { content . views } </ TableCell >
440- < TableCell className = "text-zinc-800 dark:text-zinc-200 text-xs sm:text-sm hidden md:table-cell" > { content . engagement } </ TableCell >
441- < TableCell >
442- < Badge
443- variant = { content . status === "published" ? "default" : "secondary" }
444- className = {
445- content . status === "published"
446- ? "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200 text-xs"
447- : "bg-zinc-700 text-zinc-200 text-xs"
448- }
449- >
450- { content . status }
451- </ Badge >
452- </ TableCell >
453- < TableCell className = "text-right" >
454- < Button variant = "ghost" size = "sm" className = "hover:bg-purple-700/20 text-purple-400 font-semibold text-xs sm:text-sm h-8 px-2 sm:px-3" >
455- Edit
456- </ Button >
457- </ TableCell >
486+ < TableCell className = "text-zinc-800 dark:text-zinc-200 text-xs sm:text-sm hidden md:table-cell" > { content . likes } </ TableCell >
458487 </ TableRow >
459488 ) ) }
460489 </ TableBody >
0 commit comments