Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
311 changes: 230 additions & 81 deletions src/components/EventPopularity/EventPopularity.jsx
Original file line number Diff line number Diff line change
@@ -1,155 +1,304 @@
'use client';

import { useState } from 'react';
import { useSelector } from 'react-redux';
import {
Bar,
BarChart,
CartesianGrid,
Legend,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
Tooltip,
Legend,
} from 'recharts';
import PropTypes from 'prop-types';
import styles from './EventPopularity.module.css';
import { useSelector } from 'react-redux';

// Sample data
const eventTypeData = [
{ name: 'Event Type 1', registered: 75 },
{ name: 'Event Type 2', registered: 60 },
{ name: 'Event Type 3', registered: 55 },
{ name: 'Event Type 4', registered: 50 },
{ name: 'Event Type 5', registered: 45 },
{ name: 'Event Type 6', registered: 40 },
{ name: 'Community Volunteer Day', registered: 75 },
{ name: 'Skill Development Workshop', registered: 60 },
{ name: 'Networking Mixer', registered: 55 },
{ name: 'Environmental Cleanup', registered: 50 },
{ name: 'Youth Membership Program', registered: 45 },
{ name: 'Cultural Exchange Event', registered: 40 },
];

const timeData = [
{ time: '9:00', registered: 8, attended: 12 },
{ time: '11:00', registered: 15, attended: 18 },
{ time: '13:00', registered: 20, attended: 25 },
{ time: '15:00', registered: 25, attended: 30 },
{ time: '17:00', registered: 18, attended: 20 },
{ time: '19:00', registered: 10, attended: 15 },
{ time: '21:00', registered: 5, attended: 8 },
{ time: '9:00 AM', registered: 8, attended: 12 },
{ time: '11:00 AM', registered: 15, attended: 18 },
{ time: '1:00 PM', registered: 20, attended: 25 },
{ time: '3:00 PM', registered: 25, attended: 30 },
{ time: '5:00 PM', registered: 18, attended: 20 },
{ time: '7:00 PM', registered: 10, attended: 15 },
{ time: '9:00 PM', registered: 5, attended: 8 },
];

const participationCards = [
{
title: '5+',
subtitle: 'Repeated participation',
title: '5+ Events',
subtitle: 'Highly Engaged Members',
description: 'Users who attended 5 or more events',
trend: '-10%',
trendType: 'negative',
participants: 3,
},
{
title: '2+',
subtitle: 'Repeated participation',
title: '2-4 Events',
subtitle: 'Regular Participants',
description: 'Users who attended 2 to 4 events',
trend: '+25%',
trendType: 'positive',
participants: 3,
},
{
title: '<1',
subtitle: 'Repeated participation',
title: '1 Event',
subtitle: 'New/One-Time Attendees',
description: 'First-time or one-time participants',
trend: '-5%',
trendType: 'negative',
participants: 3,
},
{
title: '420',
subtitle: 'Total Members',
title: '420 Users',
subtitle: 'Total Active Members',
description: 'Total users with at least one event attendance',
trend: '+20%',
trendType: 'positive',
},
];

const CustomTooltip = ({ active, payload, label }) => {
if (!active || !payload?.length) {
return null;
}

return (
<div className={styles.tooltipBox}>
<div className={styles.tooltipTitle}>{label}</div>

{payload.map(item => (
<div key={item.dataKey} className={styles.tooltipRow}>
<span>{item.name}: </span>
<strong>{item.value} users</strong>
</div>
))}
</div>
);
};

CustomTooltip.propTypes = {
active: PropTypes.bool,
payload: PropTypes.arrayOf(
PropTypes.shape({
dataKey: PropTypes.string,
name: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
}),
),
label: PropTypes.string,
};

const InfoTooltip = ({ text, children }) => {
const [showTooltip, setShowTooltip] = useState(false);

return (
<button
type="button"
className={styles.infotooltipHover}
onMouseEnter={() => setShowTooltip(true)}
onMouseLeave={() => setShowTooltip(false)}
onFocus={() => setShowTooltip(true)}
onBlur={() => setShowTooltip(false)}
>
{children}

{showTooltip && <div className={styles.infotooltipheading}>{text}</div>}
</button>
);
};

InfoTooltip.propTypes = {
text: PropTypes.string.isRequired,
children: PropTypes.node.isRequired,
};

export default function EventDashboard() {
const darkMode = useSelector(state => state.theme?.darkMode);

const currentDate = new Date();

const thirtyDaysAgo = new Date(currentDate.getTime() - 30 * 24 * 60 * 60 * 1000);

const dateRangeLabel = `${thirtyDaysAgo.toLocaleDateString()} - ${currentDate.toLocaleDateString()}`;

return (
<div className={`${styles.eventpopularity} ${darkMode ? styles.dark : ''}`}>
<h1 className={styles.epheader}>Event Attendance Trend</h1>
<div className={`${styles.dashboardContainer} ${darkMode ? styles.dark : ''}`}>
<div className={styles.headerSection}>
<h1 className={styles.pageTitle}>Event Attendance Dashboard</h1>

<div className={styles.timePeriod}>
<strong>Time Period:</strong> Last 30 days ({dateRangeLabel})
</div>

<div className={styles.epgrid}>
{/* Event Registration Trend (Type) */}
<div className={styles.epCard}>
<h2>Event Registration Trend (Type)</h2>
<div className={styles.subHeader}>
All metrics below reflect data from the selected time period
</div>
</div>

<div>
<div className={styles.epRowLabel}>
<div className={styles.dashboardGrid}>
{/* Registration by Type */}
<div className={styles.chartCard}>
<div className={styles.cardHeader}>
<h2 className={styles.cardTitle}>Event Registration by Type</h2>

<InfoTooltip text="Total users who registered for each event type">
<span className={styles.infoIcon}>?</span>
</InfoTooltip>
</div>

<div className={styles.eventList}>
<div className={styles.tableHeader}>
<span>Event Name</span>
<span>Registered Members</span>
<span>Registered Users</span>
</div>

{eventTypeData.map(event => (
<div key={event.name} className={styles.epRowLabel}>
<span className={styles.epEventName}>{event.name}</span>
<div key={event.name} className={styles.eventRow}>
<span className={styles.eventName}>{event.name}</span>

<div className={styles.epProgressBar}>
<div className={styles.progressBarBackground}>
<div
className={styles.epProgressFill}
style={{ width: `${(event.registered / 75) * 100}%` }}
className={styles.progressBarFill}
style={{
width: `${(event.registered / 75) * 100}%`,
}}
/>
</div>

<span>{event.registered}</span>
<span className={styles.eventCount}>{event.registered} users</span>
</div>
))}
</div>

<div className={styles.epStatsGrid}>
<div className={styles.epStatCard}>
<h3 style={{ color: 'var(--ep-primary)' }}>325</h3>
<p className={styles.epStatSubtitle}>Total Registered Members</p>
</div>
<div className={styles.statsGrid}>
{[
{
title: '325 Users',
subtitle: 'Total Registrations',
isPrimary: true,
},
{
title: 'Community Volunteer Day',
subtitle: 'Most Popular',
},
{
title: 'Cultural Exchange Event',
subtitle: 'Least Popular',
},
].map(card => (
<div key={card.title} className={styles.statCard}>
<h3 className={card.isPrimary ? styles.primaryStatTitle : styles.statTitle}>
{card.title}
</h3>

<div className={styles.epStatCard}>
<h3>Event Type 1</h3>
<p className={styles.epStatSubtitle}>Most Popular Event Type</p>
</div>

<div className={styles.epStatCard}>
<h3>Event Type 6</h3>
<p className={styles.epStatSubtitle}>Least Popular Event Type</p>
</div>
<p className={styles.statSubtitle}>{card.subtitle}</p>
</div>
))}
</div>
</div>

{/* Event Registration Trend (Time) */}
<div className={styles.epChartCard}>
<h2>Event Registration Trend (Time)</h2>

<ResponsiveContainer width="100%" height={200}>
<BarChart data={timeData}>
<CartesianGrid stroke="var(--ep-grid-stroke)" strokeDasharray="3 3" />
<XAxis dataKey="time" stroke="var(--ep-chart-tick)" />
<YAxis stroke="var(--ep-chart-tick)" />
<Tooltip
contentStyle={{
background: 'var(--ep-card-bg)',
color: 'var(--ep-text-color)',
{/* Attendance by Time */}
<div className={styles.chartCard}>
<div className={styles.cardHeader}>
<h2 className={styles.cardTitle}>Event Attendance by Time Slot</h2>

<InfoTooltip text="Registered = sign-ups | Attended = actual participants">
<span className={styles.infoIcon}>?</span>
</InfoTooltip>
</div>

<div className={styles.chartWrapper}>
<ResponsiveContainer width="100%" height={250}>
<BarChart
data={timeData}
margin={{
top: 20,
right: 20,
left: 80,
bottom: 40,
}}
/>
<Legend />
<Bar dataKey="registered" fill="var(--ep-primary)" />
<Bar dataKey="attended" fill="var(--ep-primary-2)" />
</BarChart>
</ResponsiveContainer>

<div className={styles.epParticipationGrid}>
>
<CartesianGrid strokeDasharray="3 3" stroke="var(--ep-grid-stroke, #d1d5db)" />

<XAxis
dataKey="time"
interval={0}
angle={-35}
textAnchor="end"
height={60}
stroke="var(--ep-chart-tick, #64748b)"
/>

<YAxis
width={80}
tick={{ fontSize: 12 }}
stroke="var(--ep-chart-tick, #64748b)"
label={{
value: 'Number of Users',
angle: -90,
position: 'insideLeft',
dx: -25,
style: {
textAnchor: 'middle',
fill: 'var(--ep-chart-tick, #64748b)',
fontSize: 12,
},
}}
/>

<Tooltip content={<CustomTooltip />} />

<Legend
wrapperStyle={{
paddingTop: '20px',
}}
/>

<Bar
dataKey="registered"
name="Registered Users"
fill="var(--ep-primary, #4A90E2)"
/>

<Bar dataKey="attended" name="Attended Users" fill="var(--ep-primary-2, #82B7FF)" />
</BarChart>
</ResponsiveContainer>
</div>

<div className={styles.participationGrid}>
{participationCards.map(card => (
<div key={card.title} className={styles.epParticipationCard}>
<h3>{card.title}</h3>
<p className={styles.epStatSubtitle}>{card.subtitle}</p>
<div key={card.title} className={styles.participationCard}>
<div className={styles.participationHeader}>
<h3 className={styles.participationTitle}>{card.title}</h3>

<InfoTooltip text={card.description}>
<span className={styles.smallInfoIcon}>?</span>
</InfoTooltip>
</div>

<p className={styles.participationSubtitle}>{card.subtitle}</p>

{!!card.participants && <div> +{card.participants}</div>}
{Boolean(card.participants) && (
<div className={styles.participantCount}>👥 {card.participants} users</div>
)}

<p
className={
card.trendType === 'positive' ? styles.trendPositive : styles.trendNegative
card.trendType === 'positive' ? styles.positiveTrend : styles.negativeTrend
}
>
{card.trend} Monthly
{card.trend} vs last month
</p>
</div>
))}
Expand Down
Loading
Loading