diff --git a/components/dashboard/AnalyticsCharts.tsx b/components/dashboard/AnalyticsCharts.tsx
index cdd081f4..47e1bc1c 100644
--- a/components/dashboard/AnalyticsCharts.tsx
+++ b/components/dashboard/AnalyticsCharts.tsx
@@ -20,7 +20,7 @@ import { Button } from '@/components/ui/button'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { CompanyAnalytics } from '@/types/company'
import { format } from 'date-fns'
-import { TrendingUp, Eye, MousePointerClick, Users, Calendar, Download } from 'lucide-react'
+import { TrendingUp, Eye, MousePointerClick, Users, Calendar, Download, Trophy } from 'lucide-react'
interface AnalyticsChartsProps {
analytics: CompanyAnalytics[]
@@ -40,6 +40,8 @@ export function AnalyticsCharts({ analytics, dateRange, onExport }: AnalyticsCha
registrations: record.total_registrations,
eventsCreated: record.events_created,
eventsPublished: record.events_published,
+ hackathonsCreated: record.hackathons_created,
+ hackathonsPublished: record.hackathons_published,
}))
// Calculate totals
@@ -50,8 +52,10 @@ export function AnalyticsCharts({ analytics, dateRange, onExport }: AnalyticsCha
registrations: acc.registrations + record.total_registrations,
eventsCreated: acc.eventsCreated + record.events_created,
eventsPublished: acc.eventsPublished + record.events_published,
+ hackathonsCreated: acc.hackathonsCreated + record.hackathons_created,
+ hackathonsPublished: acc.hackathonsPublished + record.hackathons_published,
}),
- { views: 0, clicks: 0, registrations: 0, eventsCreated: 0, eventsPublished: 0 }
+ { views: 0, clicks: 0, registrations: 0, eventsCreated: 0, eventsPublished: 0, hackathonsCreated: 0, hackathonsPublished: 0 }
)
// Calculate averages
@@ -70,7 +74,7 @@ export function AnalyticsCharts({ analytics, dateRange, onExport }: AnalyticsCha
return (
{/* Summary Stats */}
-
+
+
@@ -131,8 +142,11 @@ export function AnalyticsCharts({ analytics, dateRange, onExport }: AnalyticsCha
- {/* Event Performance Comparison */}
-
+ {/* Event and Hackathon Performance Comparison */}
+
+
+
+
{/* Main Charts */}
@@ -187,8 +201,9 @@ export function AnalyticsCharts({ analytics, dateRange, onExport }: AnalyticsCha
- Engagement
+ Overall Engagement
Events
+ Hackathons
@@ -304,10 +319,10 @@ export function AnalyticsCharts({ analytics, dateRange, onExport }: AnalyticsCha
@@ -320,7 +335,7 @@ export function AnalyticsCharts({ analytics, dateRange, onExport }: AnalyticsCha
} />
-
+
) : (
@@ -342,8 +357,8 @@ export function AnalyticsCharts({ analytics, dateRange, onExport }: AnalyticsCha
@@ -351,6 +366,78 @@ export function AnalyticsCharts({ analytics, dateRange, onExport }: AnalyticsCha
)}
+
+
+ {chartData.length === 0 ? (
+
+
No data available for the selected date range
+
+ ) : chartType === 'line' ? (
+
+
+
+
+
+ } />
+
+
+
+
+
+ ) : chartType === 'bar' ? (
+
+
+
+
+
+ } />
+
+
+
+
+
+ ) : (
+
+
+
+
+
+ } />
+
+
+
+
+
+ )}
+
@@ -440,11 +527,27 @@ function EventPerformanceComparison({ analytics }: EventPerformanceComparisonPro
const avgRegistrationsPerEvent = totalEvents > 0 ? Math.round(totalRegistrations / totalEvents) : 0
if (totalEvents === 0) {
- return null
+ return (
+
+
+
+
+ Event Performance Metrics
+
+ Average performance per published event
+
+
+
+
+
No events published in this period
+
+
+
+ )
}
return (
-
+
@@ -454,38 +557,38 @@ function EventPerformanceComparison({ analytics }: EventPerformanceComparisonPro
-
+
{avgViewsPerEvent.toLocaleString()}
- {totalViews.toLocaleString()} total views across {totalEvents} events
+ {totalViews.toLocaleString()} total views across {totalEvents} {totalEvents === 1 ? 'event' : 'events'}
-
+
{avgClicksPerEvent.toLocaleString()}
- {totalClicks.toLocaleString()} total clicks across {totalEvents} events
+ {totalClicks.toLocaleString()} total clicks across {totalEvents} {totalEvents === 1 ? 'event' : 'events'}
-
+
Avg Registrations per Event
-
+
{avgRegistrationsPerEvent.toLocaleString()}
- {totalRegistrations.toLocaleString()} total registrations across {totalEvents} events
+ {totalRegistrations.toLocaleString()} total registrations across {totalEvents} {totalEvents === 1 ? 'event' : 'events'}
@@ -511,3 +614,109 @@ function EventPerformanceComparison({ analytics }: EventPerformanceComparisonPro
)
}
+
+// Hackathon Performance Comparison Component
+interface HackathonPerformanceComparisonProps {
+ analytics: CompanyAnalytics[]
+}
+
+function HackathonPerformanceComparison({ analytics }: HackathonPerformanceComparisonProps) {
+ // Calculate performance metrics
+ const totalHackathons = analytics.reduce((sum, record) => sum + record.hackathons_published, 0)
+ const totalViews = analytics.reduce((sum, record) => sum + record.total_views, 0)
+ const totalClicks = analytics.reduce((sum, record) => sum + record.total_clicks, 0)
+ const totalRegistrations = analytics.reduce((sum, record) => sum + record.total_registrations, 0)
+
+ // Calculate averages per hackathon
+ const avgViewsPerHackathon = totalHackathons > 0 ? Math.round(totalViews / totalHackathons) : 0
+ const avgClicksPerHackathon = totalHackathons > 0 ? Math.round(totalClicks / totalHackathons) : 0
+ const avgRegistrationsPerHackathon = totalHackathons > 0 ? Math.round(totalRegistrations / totalHackathons) : 0
+
+ if (totalHackathons === 0) {
+ return (
+
+
+
+
+ Hackathon Performance Metrics
+
+ Average performance per published hackathon
+
+
+
+
+
No hackathons published in this period
+
+
+
+ )
+ }
+
+ return (
+
+
+
+
+ Hackathon Performance Metrics
+
+ Average performance per published hackathon
+
+
+
+
+
+
Avg Views per Hackathon
+
+
+
{avgViewsPerHackathon.toLocaleString()}
+
+ {totalViews.toLocaleString()} total views across {totalHackathons} {totalHackathons === 1 ? 'hackathon' : 'hackathons'}
+
+
+
+
+
+
Avg Clicks per Hackathon
+
+
+
{avgClicksPerHackathon.toLocaleString()}
+
+ {totalClicks.toLocaleString()} total clicks across {totalHackathons} {totalHackathons === 1 ? 'hackathon' : 'hackathons'}
+
+
+
+
+
+
Avg Registrations per Hackathon
+
+
+
+ {avgRegistrationsPerHackathon.toLocaleString()}
+
+
+ {totalRegistrations.toLocaleString()} total registrations across {totalHackathons} {totalHackathons === 1 ? 'hackathon' : 'hackathons'}
+
+
+
+
+
+
+
+
+
Performance Insights
+
+ {totalHackathons === 1
+ ? 'You have published 1 hackathon in this period.'
+ : `You have published ${totalHackathons} hackathons in this period.`}{' '}
+ {avgViewsPerHackathon > 150 && 'Your hackathons are attracting strong interest! '}
+ {avgRegistrationsPerHackathon > 15 && 'Excellent team registration rates! '}
+ {avgClicksPerHackathon < avgViewsPerHackathon * 0.1 &&
+ 'Consider enhancing your hackathon descriptions to boost engagement.'}
+
+
+
+
+
+
+ )
+}
diff --git a/components/dashboard/CompanyDashboard.tsx b/components/dashboard/CompanyDashboard.tsx
index f46407d6..d1906eb1 100644
--- a/components/dashboard/CompanyDashboard.tsx
+++ b/components/dashboard/CompanyDashboard.tsx
@@ -32,6 +32,16 @@ interface CompanyDashboardStats {
totalViews: number
totalClicks: number
pendingApprovals: number
+ eventMetrics: {
+ views: number
+ registrations: number
+ clicks: number
+ }
+ hackathonMetrics: {
+ views: number
+ registrations: number
+ clicks: number
+ }
recentChange?: {
events: number
registrations: number
@@ -41,7 +51,7 @@ interface CompanyDashboardStats {
interface RecentActivity {
id: string
- type: 'event_created' | 'event_approved' | 'event_rejected' | 'registration' | 'member_joined'
+ type: 'event_created' | 'event_approved' | 'event_rejected' | 'hackathon_created' | 'hackathon_approved' | 'hackathon_rejected' | 'registration' | 'member_joined'
title: string
description: string
timestamp: string
@@ -60,6 +70,17 @@ interface UpcomingEvent {
approval_status: string
}
+interface UpcomingHackathon {
+ id: string
+ title: string
+ slug: string
+ date: string
+ mode: string
+ registrations: number
+ max_team_size: number
+ approval_status: string
+}
+
interface CompanyDashboardProps {
company: Company
}
@@ -69,6 +90,7 @@ export function CompanyDashboard({ company }: CompanyDashboardProps) {
const [stats, setStats] = useState
(null)
const [recentActivity, setRecentActivity] = useState([])
const [upcomingEvents, setUpcomingEvents] = useState([])
+ const [upcomingHackathons, setUpcomingHackathons] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
@@ -94,6 +116,8 @@ export function CompanyDashboard({ company }: CompanyDashboardProps) {
const analyticsData = await analyticsResponse.json()
+ console.log('Analytics data:', analyticsData)
+
// Fetch events for upcoming events and pending approvals
const eventsResponse = await fetch(
`/api/companies/${company.slug}/events?limit=100`
@@ -116,6 +140,9 @@ export function CompanyDashboard({ company }: CompanyDashboardProps) {
const hackathonsData = await hackathonsResponse.json()
+ console.log('Hackathons data:', hackathonsData)
+ console.log('Hackathons array:', hackathonsData.hackathons)
+
// Calculate stats
/* eslint-disable @typescript-eslint/no-explicit-any */
const pendingEvents = eventsData.events?.filter(
@@ -138,6 +165,16 @@ export function CompanyDashboard({ company }: CompanyDashboardProps) {
.sort((a: any, b: any) => new Date(a.date).getTime() - new Date(b.date).getTime())
.slice(0, 5) || []
+ const upcomingHackathonsData = hackathonsData.hackathons
+ ?.filter((h: any) => {
+ const hackathonDate = new Date(h.date)
+ return hackathonDate > new Date() && h.approval_status === 'approved'
+ })
+ .sort((a: any, b: any) => new Date(a.date).getTime() - new Date(b.date).getTime())
+ .slice(0, 5) || []
+
+ console.log('Upcoming hackathons data:', upcomingHackathonsData)
+
// Calculate total registrations from both events and hackathons
const eventRegistrations = eventsData.events?.reduce(
(sum: number, e: any) => sum + (e.registered || 0),
@@ -152,13 +189,34 @@ export function CompanyDashboard({ company }: CompanyDashboardProps) {
const totalRegistrations = eventRegistrations + hackathonRegistrations
/* eslint-enable @typescript-eslint/no-explicit-any */
+ // Calculate separate metrics for events and hackathons from analytics
+ // Note: The analytics API doesn't currently separate views/clicks by type
+ // For now, we'll use the registration counts from the actual data
+ // and split views/clicks proportionally based on the number of each type
+ const totalItems = approvedEvents.length + approvedHackathons.length
+ const eventRatio = totalItems > 0 ? approvedEvents.length / totalItems : 0.5
+ const hackathonRatio = totalItems > 0 ? approvedHackathons.length / totalItems : 0.5
+
+ const totalViews = analyticsData.summary?.total_views || 0
+ const totalClicks = analyticsData.summary?.total_clicks || 0
+
setStats({
totalEvents: approvedEvents.length,
totalHackathons: approvedHackathons.length,
totalRegistrations: totalRegistrations,
- totalViews: analyticsData.summary?.total_views || 0,
- totalClicks: analyticsData.summary?.total_clicks || 0,
+ totalViews: totalViews,
+ totalClicks: totalClicks,
pendingApprovals: pendingEvents.length,
+ eventMetrics: {
+ views: Math.round(totalViews * eventRatio),
+ registrations: eventRegistrations,
+ clicks: Math.round(totalClicks * eventRatio),
+ },
+ hackathonMetrics: {
+ views: Math.round(totalViews * hackathonRatio),
+ registrations: hackathonRegistrations,
+ clicks: Math.round(totalClicks * hackathonRatio),
+ },
recentChange: {
events: 0, // Could calculate from analytics
registrations: 0,
@@ -167,8 +225,9 @@ export function CompanyDashboard({ company }: CompanyDashboardProps) {
})
setUpcomingEvents(upcomingEventsData)
+ setUpcomingHackathons(upcomingHackathonsData)
- // Generate recent activity from events
+ // Generate recent activity from events and hackathons
const activities: RecentActivity[] = []
// Add recent events
@@ -207,6 +266,42 @@ export function CompanyDashboard({ company }: CompanyDashboardProps) {
})
}
})
+
+ // Add recent hackathons
+ const recentHackathons = hackathonsData.hackathons?.slice(0, 3) || []
+ recentHackathons.forEach((hackathon: any) => {
+ if (hackathon.approval_status === 'approved') {
+ activities.push({
+ id: hackathon.id as string,
+ type: 'hackathon_approved',
+ title: 'Hackathon Approved',
+ description: `${hackathon.title} was approved and is now live`,
+ timestamp: (hackathon.approved_at || hackathon.created_at) as string,
+ icon: Trophy,
+ iconColor: 'text-orange-400',
+ })
+ } else if (hackathon.approval_status === 'pending') {
+ activities.push({
+ id: hackathon.id as string,
+ type: 'hackathon_created',
+ title: 'Hackathon Created',
+ description: `${hackathon.title} is pending approval`,
+ timestamp: hackathon.created_at as string,
+ icon: Clock,
+ iconColor: 'text-yellow-400',
+ })
+ } else if (hackathon.approval_status === 'rejected') {
+ activities.push({
+ id: hackathon.id as string,
+ type: 'hackathon_rejected',
+ title: 'Hackathon Rejected',
+ description: `${hackathon.title} was rejected`,
+ timestamp: hackathon.updated_at as string,
+ icon: AlertCircle,
+ iconColor: 'text-red-400',
+ })
+ }
+ })
/* eslint-enable @typescript-eslint/no-explicit-any */
// Sort by timestamp
@@ -257,7 +352,7 @@ export function CompanyDashboard({ company }: CompanyDashboardProps) {
return (
- {/* Stats Cards */}
+ {/* Overview Stats Cards */}
+ {/* Separate Event and Hackathon Metrics */}
+
+ {/* Event Metrics Card */}
+
+
+
+
+ Event Metrics
+
+ Performance data for your events
+
+
+
+
+
+
+ Views
+
+
+ {stats.eventMetrics.views.toLocaleString()}
+
+
+
+
+
+ Registrations
+
+
+ {stats.eventMetrics.registrations.toLocaleString()}
+
+
+
+
+
+ {stats.eventMetrics.clicks.toLocaleString()}
+
+
+ {stats.eventMetrics.views > 0 && (
+
+
+ Conversion Rate
+
+ {((stats.eventMetrics.registrations / stats.eventMetrics.views) * 100).toFixed(1)}%
+
+
+
+ )}
+
+
+
+
+ {/* Hackathon Metrics Card */}
+
+
+
+
+ Hackathon Metrics
+
+ Performance data for your hackathons
+
+
+
+
+
+
+ Views
+
+
+ {stats.hackathonMetrics.views.toLocaleString()}
+
+
+
+
+
+ Registrations
+
+
+ {stats.hackathonMetrics.registrations.toLocaleString()}
+
+
+
+
+
+ {stats.hackathonMetrics.clicks.toLocaleString()}
+
+
+ {stats.hackathonMetrics.views > 0 && (
+
+
+ Conversion Rate
+
+ {((stats.hackathonMetrics.registrations / stats.hackathonMetrics.views) * 100).toFixed(1)}%
+
+
+
+ )}
+
+
+
+
+
{/* Main Content Grid */}
{/* Recent Activity */}
@@ -311,7 +513,7 @@ export function CompanyDashboard({ company }: CompanyDashboardProps) {
Recent Activity
-
Latest updates and events
+
Latest updates from events and hackathons
@@ -320,7 +522,7 @@ export function CompanyDashboard({ company }: CompanyDashboardProps) {
No recent activity
-
Create your first event to get started
+
Create your first event or hackathon to get started
) : (
@@ -376,6 +578,50 @@ export function CompanyDashboard({ company }: CompanyDashboardProps) {
+ {/* Upcoming Hackathons */}
+
+
+
+
+
+
+ Upcoming Hackathons
+
+ Your next scheduled hackathons
+
+
+
+ View All
+
+
+
+
+
+ {upcomingHackathons.length === 0 ? (
+
+
+
No upcoming hackathons
+
+
+
+ Create Hackathon
+
+
+
+ ) : (
+
+ {upcomingHackathons.map((hackathon) => (
+
+ ))}
+
+ )}
+
+
+
{/* Quick Actions */}
@@ -537,6 +783,43 @@ function UpcomingEventItem({ event }: UpcomingEventItemProps) {
)
}
+// Upcoming Hackathon Item Component
+interface UpcomingHackathonItemProps {
+ hackathon: UpcomingHackathon
+ companySlug: string
+}
+
+function UpcomingHackathonItem({ hackathon, companySlug }: UpcomingHackathonItemProps) {
+ return (
+
+
+
+
+
+
+
+ {hackathon.title}
+
+
+ {format(new Date(hackathon.date), 'MMM dd, yyyy')}
+
+
+
+ {hackathon.mode || 'Online'}
+
+
+ {hackathon.registrations || 0} teams registered
+
+
+
+
+
+ )
+}
+
// Quick Action Button Component
interface QuickActionButtonProps {
href: string