@@ -15,7 +15,7 @@ import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/components/
1515import * as LucideIcons from "lucide-react" ;
1616import { Table , TableBody , TableCell , TableHead , TableHeader , TableRow } from "@/components/ui/table" ;
1717import { Badge } from "@/components/ui/badge" ;
18- import type { View , DashboardTab , UserRole , AppUser , BlogPost , EducationProgram } from "@/app/types" ;
18+ import type { View , DashboardTab , UserRole , AppUser , BlogPost , EducationProgram , NewsletterSubscriber } from "@/app/types" ;
1919import { ScrollArea } from "@/components/ui/scroll-area" ;
2020import { Button } from "@/components/ui/button" ;
2121import { Form , FormControl , FormField , FormItem , FormLabel , FormMessage } from "@/components/ui/form" ;
@@ -92,6 +92,9 @@ export default function DashboardView({ isOpen, onOpenChange, user, userRole, ha
9292 const [ userToDelete , setUserToDelete ] = useState < AppUser | null > ( null ) ;
9393 const [ userToBan , setUserToBan ] = useState < AppUser | null > ( null ) ;
9494
95+ const [ subscribers , setSubscribers ] = useState < NewsletterSubscriber [ ] > ( [ ] ) ;
96+ const [ isLoadingSubscribers , setIsLoadingSubscribers ] = useState ( false ) ;
97+
9598 const [ blogPosts , setBlogPosts ] = useState < BlogPost [ ] > ( [ ] ) ;
9699 const [ educationPrograms , setEducationPrograms ] = useState < EducationProgram [ ] > ( [ ] ) ;
97100
@@ -126,11 +129,25 @@ export default function DashboardView({ isOpen, onOpenChange, user, userRole, ha
126129 else toast ( { variant : 'destructive' , title : 'Failed to fetch users' } ) ;
127130 } catch ( error ) { toast ( { variant : 'destructive' , title : 'Network Error' } ) ; } finally { setIsLoadingUsers ( false ) ; }
128131 } ;
132+
133+ const fetchSubscribers = async ( ) => {
134+ setIsLoadingSubscribers ( true ) ;
135+ const token = localStorage . getItem ( 'token' ) ;
136+ if ( ! token ) { toast ( { variant : 'destructive' , title : 'Authentication Error' } ) ; setIsLoadingSubscribers ( false ) ; return ; }
137+ try {
138+ const response = await fetch ( `${ API_BASE_URL } /api/subscribers` , { headers : { 'Authorization' : `Bearer ${ token } ` } } ) ;
139+ if ( response . ok ) setSubscribers ( await response . json ( ) ) ;
140+ else toast ( { variant : 'destructive' , title : 'Failed to fetch subscribers' } ) ;
141+ } catch ( error ) { toast ( { variant : 'destructive' , title : 'Network Error' } ) ; } finally { setIsLoadingSubscribers ( false ) ; }
142+ } ;
129143
130144 useEffect ( ( ) => {
131- if ( activeTab === 'users' && userRole === 'admin' ) fetchUsers ( ) ;
132- if ( activeTab === 'blog' && userRole === 'admin' ) fetchBlogPosts ( ) ;
133- if ( activeTab === 'sessions' && userRole === 'admin' ) fetchEducationPrograms ( ) ;
145+ if ( userRole === 'admin' ) {
146+ if ( activeTab === 'users' ) fetchUsers ( ) ;
147+ if ( activeTab === 'blog' ) fetchBlogPosts ( ) ;
148+ if ( activeTab === 'sessions' ) fetchEducationPrograms ( ) ;
149+ if ( activeTab === 'subscribers' ) fetchSubscribers ( ) ;
150+ }
134151 } , [ activeTab , userRole ] ) ;
135152
136153 const handleApiResponse = async ( response : Response , successMessage : string , errorMessage : string ) => {
@@ -210,7 +227,7 @@ export default function DashboardView({ isOpen, onOpenChange, user, userRole, ha
210227 }
211228 }
212229
213- const adminTabs = [ "overview" , "users" , "blog" , "sessions" , "settings" ] ;
230+ const adminTabs = [ "overview" , "users" , "subscribers" , " blog", "sessions" , "settings" ] ;
214231 const founderTabs = [ "overview" , "msmes" , "incubators" , "mentors" , "submission" , "settings" ] ;
215232 const availableTabs = userRole === 'admin' ? adminTabs : founderTabs ;
216233 const pendingApprovalCount = users . filter ( u => ! u . is_confirmed ) . length ;
@@ -224,9 +241,9 @@ export default function DashboardView({ isOpen, onOpenChange, user, userRole, ha
224241 </ DialogHeader >
225242 < div className = "flex-grow flex flex-col min-h-0 p-6 pt-0" >
226243 < Tabs value = { activeTab } onValueChange = { ( tab ) => setActiveTab ( tab as DashboardTab ) } className = "flex flex-col flex-grow min-h-0" >
227- < TabsList className = { userRole === 'admin' ? 'grid w-full grid-cols-5 ' : 'grid w-full grid-cols-6' } >
244+ < TabsList className = { userRole === 'admin' ? 'grid w-full grid-cols-6 ' : 'grid w-full grid-cols-6' } >
228245 { availableTabs . map ( tab => {
229- const Icon = LucideIcons [ tab === 'overview' ? 'LayoutDashboard' : tab === 'msmes' ? 'Briefcase' : tab === 'incubators' ? 'Lightbulb' : tab === 'mentors' ? 'Users' : tab === 'submission' ? 'FileText' : tab === 'settings' ? 'Settings' : tab === 'users' ? 'User' : tab === 'blog' ? 'Newspaper' : 'BookOpen' as keyof typeof LucideIcons ] || LucideIcons . HelpCircle ;
246+ const Icon = LucideIcons [ tab === 'overview' ? 'LayoutDashboard' : tab === 'msmes' ? 'Briefcase' : tab === 'incubators' ? 'Lightbulb' : tab === 'mentors' ? 'Users' : tab === 'submission' ? 'FileText' : tab === 'settings' ? 'Settings' : tab === 'users' ? 'User' : tab === 'subscribers' ? 'Mail' : tab === ' blog' ? 'Newspaper' : 'BookOpen' as keyof typeof LucideIcons ] || LucideIcons . HelpCircle ;
230247 return (
231248 < TabsTrigger value = { tab } key = { tab } className = "capitalize" >
232249 < Icon className = "mr-2 h-4 w-4" /> { tab === 'mentors' ? 'My Mentors' : tab }
@@ -254,6 +271,26 @@ export default function DashboardView({ isOpen, onOpenChange, user, userRole, ha
254271 </ TableBody > </ Table > ) }
255272 </ CardContent > </ Card >
256273 </ TabsContent >
274+ < TabsContent value = "subscribers" className = "mt-0" >
275+ < Card className = "bg-card/50 backdrop-blur-sm border-border/50" >
276+ < CardHeader > < CardTitle > Newsletter Subscribers</ CardTitle > < CardDescription > List of all users subscribed to the newsletter.</ CardDescription > </ CardHeader >
277+ < CardContent >
278+ { isLoadingSubscribers ? < div className = "flex justify-center items-center h-48" > < LucideIcons . Loader2 className = "h-8 w-8 animate-spin" /> </ div > : (
279+ < Table > < TableHeader > < TableRow > < TableHead > Email</ TableHead > < TableHead > Subscribed Date</ TableHead > </ TableRow > </ TableHeader > < TableBody >
280+ { subscribers . map ( sub => (
281+ < TableRow key = { sub . id } >
282+ < TableCell className = "font-medium" > { sub . email } </ TableCell >
283+ < TableCell > { new Date ( sub . subscribed_at ) . toLocaleDateString ( ) } </ TableCell >
284+ </ TableRow >
285+ ) ) }
286+ </ TableBody > </ Table >
287+ ) }
288+ { subscribers . length === 0 && ! isLoadingSubscribers && (
289+ < p className = "text-center text-muted-foreground py-8" > There are no newsletter subscribers yet.</ p >
290+ ) }
291+ </ CardContent >
292+ </ Card >
293+ </ TabsContent >
257294 < TabsContent value = "blog" className = "mt-0 space-y-6" >
258295 < Card > < CardHeader > < CardTitle > Create New Blog Post</ CardTitle > </ CardHeader > < CardContent > < Form { ...blogForm } > < form onSubmit = { blogForm . handleSubmit ( onBlogSubmit ) } className = "space-y-4" > < FormField control = { blogForm . control } name = "title" render = { ( { field } ) => ( < FormItem > < FormLabel > Title</ FormLabel > < FormControl > < Input { ...field } /> </ FormControl > < FormMessage /> </ FormItem > ) } /> < FormField control = { blogForm . control } name = "excerpt" render = { ( { field } ) => ( < FormItem > < FormLabel > Excerpt</ FormLabel > < FormControl > < Textarea { ...field } /> </ FormControl > < FormMessage /> </ FormItem > ) } /> < FormField control = { blogForm . control } name = "content" render = { ( { field } ) => ( < FormItem > < FormLabel > Content</ FormLabel > < FormControl > < Textarea rows = { 8 } { ...field } /> </ FormControl > < FormMessage /> </ FormItem > ) } /> < FormField control = { blogForm . control } name = "image" render = { ( { field } ) => ( < FormItem > < FormLabel > Image URL</ FormLabel > < FormControl > < Input { ...field } /> </ FormControl > < FormMessage /> </ FormItem > ) } /> < FormField control = { blogForm . control } name = "hint" render = { ( { field } ) => ( < FormItem > < FormLabel > Image Hint</ FormLabel > < FormControl > < Input { ...field } /> </ FormControl > < FormMessage /> </ FormItem > ) } /> < Button type = "submit" > Publish Post</ Button > </ form > </ Form > </ CardContent > </ Card >
259296 </ TabsContent >
0 commit comments