diff --git a/src/components/CommunityPortal/Reports/Participation/ChartsSection.jsx b/src/components/CommunityPortal/Reports/Participation/ChartsSection.jsx new file mode 100644 index 0000000000..03e1ac1932 --- /dev/null +++ b/src/components/CommunityPortal/Reports/Participation/ChartsSection.jsx @@ -0,0 +1,159 @@ +import { useSelector } from 'react-redux'; +import { + BarChart, + Bar, + XAxis, + YAxis, + Tooltip, + ResponsiveContainer, + LineChart, + Line, + PieChart, + Pie, + Cell, + Legend, +} from 'recharts'; + +import mockEvents from './mockData'; +import styles from './ChartsSection.module.css'; + +function ChartsSection() { + const darkMode = useSelector(state => state.theme.darkMode); + + // Group data by event type + const eventTypeStats = []; + const groups = {}; + + mockEvents.forEach(evt => { + const key = evt.eventType; + + if (!groups[key]) { + groups[key] = { count: 0, noShowSum: 0, dropSum: 0 }; + } + + groups[key].count++; + groups[key].noShowSum += Number.parseInt(evt.noShowRate, 10); + groups[key].dropSum += Number.parseInt(evt.dropOffRate, 10); + }); + + Object.entries(groups).forEach(([key, stats]) => { + eventTypeStats.push({ + eventType: key, + avgNoShow: Math.round(stats.noShowSum / stats.count), + avgDrop: Math.round(stats.dropSum / stats.count), + }); + }); + + // Monthly trend + const monthlyTrend = {}; + + mockEvents.forEach(evt => { + const m = new Date(evt.eventDate).getMonth(); + + if (!monthlyTrend[m]) { + monthlyTrend[m] = { count: 0, noShowSum: 0 }; + } + + monthlyTrend[m].count++; + monthlyTrend[m].noShowSum += Number.parseInt(evt.noShowRate, 10); + }); + + const trendData = Object.keys(monthlyTrend).map(m => ({ + month: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][m], + avgNoShow: Math.round(monthlyTrend[m].noShowSum / monthlyTrend[m].count), + })); + + // Location distribution + const locationGroups = {}; + + mockEvents.forEach(evt => { + const loc = evt.location; + if (!locationGroups[loc]) locationGroups[loc] = 0; + locationGroups[loc]++; + }); + + const locationData = Object.keys(locationGroups).map(loc => ({ + name: loc, + value: locationGroups[loc], + })); + + const pieColors = ['#007bff', '#00b894', '#e17055', '#6c5ce7', '#fdcb6e']; + + // Dark-mode-aware tooltip styling so tooltips stay readable in both themes + const tooltipProps = { + contentStyle: { + backgroundColor: darkMode ? '#2c2f33' : '#fff', + border: `1px solid ${darkMode ? '#555' : '#ccc'}`, + borderRadius: 4, + }, + labelStyle: { color: darkMode ? '#fff' : '#333' }, + itemStyle: { color: darkMode ? '#fff' : '#333' }, + cursor: { fill: darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)' }, + }; + + return ( +
+

Comparative Charts

+ + {/* Row 1 — Bar Charts */} +
+ {/* No-Show Chart */} +
+

No-show rate by event type

+ + + + + + + + +
+ + {/* Drop-Off Chart */} +
+

Drop-off rate by event type

+ + + + + + + + +
+
+ + {/* Row 2 — Line Chart */} +
+
Monthly no-show trend
+ + + + + + + + +
+ + {/* Row 3 — Pie Chart */} +
+
Participation by location
+ + + + {locationData.map((entry, index) => ( + + ))} + + + + + +
+
+ ); +} + +export default ChartsSection; diff --git a/src/components/CommunityPortal/Reports/Participation/ChartsSection.module.css b/src/components/CommunityPortal/Reports/Participation/ChartsSection.module.css new file mode 100644 index 0000000000..56402d6839 --- /dev/null +++ b/src/components/CommunityPortal/Reports/Participation/ChartsSection.module.css @@ -0,0 +1,57 @@ +.chartsSection { + margin-top: 30px; + background: #fff; + padding: 25px; + border-radius: 10px; + box-shadow: 0 2px 8px rgb(0 0 0 / 8%); +} + +.chartsSectionDark { + background: #1c2541; + color: #fff; + border: 1px solid #333; +} + +.sectionTitle { + font-size: 1.3rem; + font-weight: 600; + margin-bottom: 20px; + color: #333; +} + +.row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; +} + +.chartBox { + background: #f8f9fa; + padding: 15px; + border-radius: 10px; + border: 1px solid #ddd; +} + +.chartsSectionDark .chartBox { + background: #3a506b; + border: 1px solid #555; +} + +.chartBox h4 { + margin-bottom: 10px; + font-size: 1rem; + font-weight: 600; +} + +.chartBoxFull { + margin-top: 20px; + background: #f8f9fa; + padding: 15px; + border-radius: 10px; + border: 1px solid #ddd; +} + +.chartsSectionDark .chartBoxFull { + background: #3a506b; + border: 1px solid #555; +} diff --git a/src/components/CommunityPortal/Reports/Participation/Demographics.jsx b/src/components/CommunityPortal/Reports/Participation/Demographics.jsx new file mode 100644 index 0000000000..a5b925f627 --- /dev/null +++ b/src/components/CommunityPortal/Reports/Participation/Demographics.jsx @@ -0,0 +1,37 @@ +import { useSelector } from 'react-redux'; +import { useHistory } from 'react-router-dom'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'; +import styles from './Participation.module.css'; + +function Demographics() { + const darkMode = useSelector(state => state.theme.darkMode); + const history = useHistory(); + + return ( +
+ + +

+ Demographics Overview +

+ +
+
+

Charts and breakdowns for age, gender, and location demographics will appear here.

+
+
+
+ ); +} + +export default Demographics; diff --git a/src/components/CommunityPortal/Reports/Participation/EventParticipation.jsx b/src/components/CommunityPortal/Reports/Participation/EventParticipation.jsx index 351417ab19..86a7bf9367 100644 --- a/src/components/CommunityPortal/Reports/Participation/EventParticipation.jsx +++ b/src/components/CommunityPortal/Reports/Participation/EventParticipation.jsx @@ -1,15 +1,18 @@ /* eslint-disable testing-library/no-node-access */ import { useSelector } from 'react-redux'; import { useRef, useState, useCallback } from 'react'; +import { useHistory } from 'react-router-dom'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faFilePdf } from '@fortawesome/free-solid-svg-icons'; import MyCases from './MyCases'; import DropOffTracking from './DropOffTracking'; import NoShowInsights from './NoShowInsights'; import styles from './Participation.module.css'; +import ChartsSection from './ChartsSection'; function EventParticipation() { const darkMode = useSelector(state => state.theme.darkMode); + const history = useHistory(); const exportRef = useRef(null); const [exporting, setExporting] = useState(false); const [selectedOrganizer, setSelectedOrganizer] = useState('All Organizers'); @@ -23,22 +26,22 @@ function EventParticipation() { // Expand "More" so all visible items are included const moreBtn = document.querySelector('.more-btn-global'); - const toggled = moreBtn?.textContent?.toLowerCase().includes('more') ?? false; - if (toggled) moreBtn.click(); + const shouldExpand = moreBtn?.textContent?.toLowerCase().includes('more'); + if (shouldExpand) moreBtn.click(); const prevTitle = document.title; document.title = 'event_participation'; setTimeout(() => { - globalThis.print(); + globalThis.window.print(); setTimeout(() => { - if (toggled) moreBtn.click(); + if (shouldExpand) moreBtn.click(); delete document.documentElement.dataset.exporting; document.title = prevTitle; setExporting(false); - }, 100); + }, 120); }, 500); }, [exporting]); @@ -92,13 +95,63 @@ function EventParticipation() { +
+ + +
+
+ + + {/* ACTIONABLE INSIGHTS SECTION */} +
+

Actionable insights

+ +
+
+

High no-show rate detected

+

+ Yoga Class events show an unusual increase in no-show percentage this month. +

+ ↑ 12% +
+ +
+

Weekend events perform better

+

+ Attendance is consistently higher on Saturdays compared to weekdays. +

+ ↑ 8% +
- {/* Print-only footer note */} +
+ Generated from Event Participation +
+

Drop-off rate reduction opportunity

+

+ Average event drop-off decreases when host reminders are sent earlier. +

+ ↓ 5% +
+
+
+
); } diff --git a/src/components/CommunityPortal/Reports/Participation/MyCases.jsx b/src/components/CommunityPortal/Reports/Participation/MyCases.jsx index 85cb0bf5b0..f5bc2b1d0b 100644 --- a/src/components/CommunityPortal/Reports/Participation/MyCases.jsx +++ b/src/components/CommunityPortal/Reports/Participation/MyCases.jsx @@ -1,5 +1,6 @@ -import { useState } from 'react'; +import { useState, useMemo } from 'react'; import { useSelector } from 'react-redux'; +import Calendar from 'react-calendar'; import styles from './MyCases.module.css'; import mockEvents from './mockData'; import CreateEventModal from './CreateEventModal'; @@ -10,6 +11,7 @@ function MyCases() { const [filter, setFilter] = useState('All Time'); const [expanded, setExpanded] = useState(false); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); + const [calendarDate, setCalendarDate] = useState(new Date()); const isExporting = typeof document !== 'undefined' && document.documentElement?.dataset?.exporting === 'true'; @@ -106,11 +108,82 @@ function MyCases() { ); - const renderCalendarView = () => ( -
-

Calendar View is under construction...

-
- ); + const eventsByDate = useMemo(() => { + const map = {}; + filteredEvents.forEach(event => { + const baseDate = new Date(event.eventDate || event.eventTime); + const key = baseDate.toISOString().slice(0, 10); + if (!map[key]) map[key] = []; + map[key].push(event); + }); + return map; + }, [filteredEvents]); + + const renderCalendarTileContent = ({ date, view: tileView }) => { + if (tileView !== 'month') return null; + + const tileKey = date.toISOString().slice(0, 10); + const dayEvents = eventsByDate[tileKey]; + + if (!dayEvents || dayEvents.length === 0) return null; + + return
{dayEvents.length}
; + }; + + const renderCalendarView = () => { + const selectedKey = calendarDate.toISOString().slice(0, 10); + const selectedEvents = eventsByDate[selectedKey] || []; + + const formattedSelectedDate = calendarDate.toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + }); + + return ( +
+
+ + {calendarDate.toLocaleString('en-US', { month: 'long', year: 'numeric' })} + +
+ + + +
+

Events on {formattedSelectedDate}

+ + {selectedEvents.length === 0 && ( +

No events scheduled for this day.

+ )} + + {selectedEvents.map(event => ( +
+
+ {event.eventName} + {event.eventType} +
+
+ {event.eventTime} + {event.location} + {`+${event.attendees} attendees`} +
+
+ ))} +
+
+ ); + }; return (
div { + font-weight: 600; + text-align: center; + font-size: 16px; + box-shadow: 0 2px 4px rgb(0 0 0 / 10%); + padding: 4px 0; + color: #333 !important; +} + +.myCasesPageDark .calendarGrid > div { + color: #fff !important; +} + +.calendarCell { + border: 1px solid #ccc; + min-height: 90px; + padding: 6px; + border-radius: 8px; + background: #fff; + color: #333 !important; +} + +.myCasesPageDark .calendarCell { + background: #3a506b; + border: 1px solid #777; + color: #fff !important; +} + +/* ---------- Calendar Events inside Tiles ---------- */ +.calendarEvent { + font-size: 11px; + background: #e8f1ff; + padding: 2px 4px; + border-radius: 4px; + margin-bottom: 3px; + color: #003c8f; +} + +.calendarEventDark { + background: #244879; + color: #fff; +} + +.reactCalendar { + border: none; + width: 100%; +} + +/* Fix text inside tiles (events & numbers) for light mode */ +.reactCalendar :global(.react-calendar__tile) { + color: #333 !important; +} + +/* ============================================================ + DARK MODE — react-calendar overrides + react-calendar ships its own stylesheet (background: white, + dark tile text). Without these overrides the whole widget + renders identically to light mode. + ============================================================ */ + +/* Calendar surface */ +.myCasesPageDark .reactCalendar, +.myCasesPageDark .reactCalendar :global(.react-calendar__viewContainer), +.myCasesPageDark .reactCalendar :global(.react-calendar__month-view) { + background-color: transparent !important; + color: #fff !important; +} + +/* Tiles (day cells) */ +.myCasesPageDark .reactCalendar :global(.react-calendar__tile), +.myCasesPageDark .reactCalendar :global(.react-calendar__tile abbr) { + color: #fff !important; + background-color: transparent !important; +} + +/* Neighbouring-month days — dimmed */ +.myCasesPageDark .reactCalendar :global(.react-calendar__month-view__days__day--neighboringMonth), +.myCasesPageDark + .reactCalendar + :global(.react-calendar__month-view__days__day--neighboringMonth abbr) { + color: #9aa7b8 !important; +} + +/* Tile hover / focus */ +.myCasesPageDark .reactCalendar :global(.react-calendar__tile:enabled:hover), +.myCasesPageDark .reactCalendar :global(.react-calendar__tile:enabled:focus) { + background-color: #2c3e50 !important; +} + +/* Today's tile */ +.myCasesPageDark .reactCalendar :global(.react-calendar__tile--now) { + background-color: #5a6f8c !important; +} + +.myCasesPageDark .reactCalendar :global(.react-calendar__tile--now:enabled:hover), +.myCasesPageDark .reactCalendar :global(.react-calendar__tile--now:enabled:focus) { + background-color: #6b80a0 !important; +} + +/* Selected / active tile */ +.myCasesPageDark .reactCalendar :global(.react-calendar__tile--active), +.myCasesPageDark .reactCalendar :global(.react-calendar__tile--active:enabled:hover), +.myCasesPageDark .reactCalendar :global(.react-calendar__tile--active:enabled:focus) { + background-color: #1d6fa5 !important; +} + +/* Navigation bar (month/year + arrows) */ +.myCasesPageDark .reactCalendar :global(.react-calendar__navigation) { + background-color: transparent !important; +} + +.myCasesPageDark .reactCalendar :global(.react-calendar__navigation button) { + color: #fff !important; + background-color: transparent !important; +} + +.myCasesPageDark .reactCalendar :global(.react-calendar__navigation button:enabled:hover), +.myCasesPageDark .reactCalendar :global(.react-calendar__navigation button:enabled:focus) { + background-color: #2c3e50 !important; +} + +.myCasesPageDark .reactCalendar :global(.react-calendar__navigation button:disabled) { + background-color: transparent !important; + color: #b0b8c4 !important; +} + +/* Weekdays bar (Sun, Mon, ...) */ +.myCasesPageDark .reactCalendar :global(.react-calendar__month-view__weekdays), +.myCasesPageDark .reactCalendar :global(.react-calendar__month-view__weekdays__weekday) { + background-color: transparent !important; +} + +.myCasesPageDark .reactCalendar :global(.react-calendar__month-view__weekdays__weekday abbr) { + color: #fff !important; + text-decoration: none; +} + + /* ---------- More button ---------- */ .moreBtn { background-color: #007bff; @@ -601,3 +752,181 @@ color: #9b59b6 !important; } } + +/* ============================================================ + Selected-day events list (below the calendar) + ============================================================ */ +.calendarEventsList { + margin-top: 20px; + display: flex; + flex-direction: column; + gap: 10px; +} + +.calendarEventsTitle { + font-size: 16px; + font-weight: 600; + color: #1c2541; + margin: 0 0 4px; +} + +.myCasesPageDark .calendarEventsTitle { + color: #fff; +} + +.calendarEventsEmpty { + font-size: 14px; + color: #6b7280; + margin: 0; +} + +.myCasesPageDark .calendarEventsEmpty { + color: #9aa7b8; +} + +.calendarEventItem { + display: flex; + flex-direction: column; + gap: 6px; + padding: 12px 14px; + border: 1px solid #e2e8f0; + border-left: 4px solid #003c8f; + border-radius: 8px; + background: #f8fafc; +} + +.calendarEventItemDark { + background: #1c2541; + border-color: #2b3a55; + border-left-color: #4a78c2; +} + +.calendarEventItemHeader { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + flex-wrap: wrap; +} + +.calendarEventName { + font-size: 15px; + font-weight: 600; + color: #1c2541; +} + +.myCasesPageDark .calendarEventName { + color: #fff; +} + +.calendarEventType { + font-size: 12px; + font-weight: 500; + padding: 2px 10px; + border-radius: 999px; + background: #e8f1ff; + color: #003c8f; + white-space: nowrap; +} + +.myCasesPageDark .calendarEventType { + background: #244879; + color: #fff; +} + +.calendarEventMeta { + display: flex; + flex-wrap: wrap; + gap: 6px 16px; + font-size: 13px; + color: #4b5563; +} + +.myCasesPageDark .calendarEventMeta { + color: #c3ccda; +} + +.calendarEventMeta span { + display: inline-flex; + align-items: center; +} + +/* ============================================================ + Small mobile (≤480px, e.g. 375px) — relieve cramped layout + ============================================================ */ +@media (width <= 480px) { + .myCasesPage, + .myCasesPageDark { + padding: 12px; + } + + .sectionTitle, + .sectionTitleDark { + font-size: 20px; + text-align: center; + } + + /* View switcher fills the row with equal-width buttons */ + .viewSwitcher, + .viewSwitcherDarkMode { + width: 100%; + gap: 8px; + } + + .viewSwitcher button, + .viewSwitcherDarkMode button { + flex: 1; + padding: 8px 4px; + font-size: 13px; + } + + /* Header controls stretch full width so they aren't squeezed */ + .filterWrapper, + .createNew, + .createNewDarkMode, + .moreBtn { + width: 100%; + } + + .moreBtn { + text-align: center; + } + + /* Cards stack into a single column */ + .caseCards { + grid-template-columns: 1fr; + gap: 12px; + } + + /* List rows go from 4 columns to a compact 2-column layout */ + .caseListItem, + .caseListItemDark { + grid-template-columns: 1fr auto; + gap: 4px 10px; + padding: 12px; + } + + .caseListItem span, + .caseListItemDark span { + margin-right: 0; + } + + /* Calendar: trim padding and tile height so it isn't squeezed */ + .calendarView, + .calendarViewDark { + padding: 12px; + font-size: 14px; + } + + .calendarCell { + min-height: 64px; + padding: 4px; + } + + /* Selected-day event cards: type badge wraps below the name */ + .calendarEventItemHeader { + flex-direction: column; + align-items: flex-start; + gap: 4px; + } +} \ No newline at end of file diff --git a/src/components/CommunityPortal/Reports/Participation/Participation.module.css b/src/components/CommunityPortal/Reports/Participation/Participation.module.css index a94c206116..ea0c9ba735 100644 --- a/src/components/CommunityPortal/Reports/Participation/Participation.module.css +++ b/src/components/CommunityPortal/Reports/Participation/Participation.module.css @@ -9,16 +9,20 @@ } .participationLandingPage { - max-width: 90%; - margin: 0 auto; + width: 100%; + margin: 0; + padding: 0 5%; background: white; + box-sizing: border-box; } .participationLandingPageDark { - max-width: 90%; - margin: 0 auto; + width: 100%; + margin: 0; + padding: 0 5%; background-color: #1b2a41; color: #fff; + box-sizing: border-box; } /* ---------- Page Header ---------- */ @@ -338,16 +342,28 @@ display: inline-block; } +/* Color the value text itself (not just inner spans) so bare text like "+5%" + isn't left dark by a global p color rule in dark mode */ +.trackingRateDark .trackingRateValue, +.trackingRateDark .trackingRateSubheading { + color: #fff; +} + .trackingRate .trackingRateValue span.trackingRateValuePositive, .trackingRateDark .trackingRateValue span.trackingRateValuePositive { color: #f44336 !important; + font-weight: bold !important; } -.trackingRate .trackingRateValue span.trackingRateValueNegative, -.trackingRateDark .trackingRateValue span.trackingRateValueNegative { +.trackingRate .trackingRateValue span.trackingRateValueNegative { color: #008000 !important; } +/* Brighter green for dark mode — #008000 is too dark on the dark panel */ +.trackingRateDark .trackingRateValue span.trackingRateValueNegative { + color: #4ade80 !important; +} + /* Tables */ .trackingListContainer { @@ -414,11 +430,19 @@ .trackingRateGreen { color: green !important; +} + + +/* Brighter green for dark mode — plain green is too dark on the dark table */ +.trackingTableDark .trackingRateGreen { + color: #4ade80 !important; font-weight: bold; } -.trackingRateRed { - color: red !important; +/* Darker red for the drop-off rate in dark mode */ +.trackingTableDark .trackingRateRed { + color: #f92a2a !important; + font-weight: bold; } /* ---------- Insights Section ---------- */ @@ -428,14 +452,18 @@ margin-bottom: 15px; display: flex; justify-content: space-between; +} + +.trackingTableDark td, +.trackingTableDark th { + color: #fff; } -.insightsHeaderDark { +/* Keep row hover dark in dark mode (overrides global table hover) */ +.trackingTableDark tbody tr:hover, +.trackingTableDark tbody tr:hover td { + background-color: #253055; color: #fff; - font-weight: bold; - margin-bottom: 15px; - display: flex; - justify-content: space-between; } @media (width <= 1024px) { @@ -956,10 +984,6 @@ color: #9b59b6 !important; } - .trackingRateGreen { - color: green !important; - font-weight: bold !important; - } .trackingRateRed { color: red !important; @@ -1149,17 +1173,220 @@ border: 1px solid #3A506B; } -.insightsDark .insightsTab { - background-color: #3A506B; +/* ----- Actionable Insights ----- */ +.actionableSection { + margin-top: 30px; + background: #fff; + padding: 20px; + border-radius: 10px; + box-shadow: 0 2px 8px rgb(0 0 0 / 10%); +} + +.actionableSectionDark { + background: #1c2541; + border: 1px solid #333; color: #fff; - border-right: 1px solid #555; } -.insightsDark .insightsTab.activeTab { - background-color: #0056b3; +.actionableHeader { + font-size: 1.3rem; + font-weight: 600; + margin-bottom: 18px; + color: #333; +} + +.actionableSectionDark .actionableHeader { color: #fff; } +.actionableGrid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); + gap: 15px; +} + +.actionCard { + background: #f8f9fa; + border-radius: 10px; + padding: 15px; + border: 1px solid #ddd; +} + +.actionCard:hover { + background: #f1f1f1; +} + +.actionableSectionDark .actionCard { + background: #3a506b; + border: 1px solid #555; + color: #fff; +} + +.actionTitle { + font-size: 1rem; + font-weight: 600; + margin-bottom: 10px; +} + +.actionDescription { + font-size: 0.85rem; + color: #555; + margin-bottom: 10px; +} + +.actionableSectionDark .actionDescription { + color: #e0e0e0; +} + +.actionTrendUp { + font-size: 0.85rem; + font-weight: bold; + color: #28a745; +} + +.actionTrendDown { + font-size: 0.85rem; + font-weight: bold; + color: #e74c3c; +} + +/* ---------- Demographics Page ---------- */ +.demographicsPage { + padding: 20px; + background: #fff; + min-height: 100vh; + box-sizing: border-box; +} + +.demographicsPageDark { + background: #1c2541; + color: #fff; +} + +.demographicsHeader { + font-size: 22px; + font-weight: bold; + color: #333; + margin-bottom: 20px; +} + +.demographicsHeaderDark { + color: #fff; +} + +.demographicsContent { + display: flex; + justify-content: center; +} + +.placeholderBox { + width: 100%; + padding: 20px; + border: 2px dashed #ccc; + border-radius: 10px; + text-align: center; + color: #555; +} + +.demographicsPageDark .placeholderBox, +.demographicsPageDark .placeholderBox p { + border-color: #888; + color: #ddd; +} + +.subPageNav { + margin: 15px 0; + display: flex; + gap: 10px; +} + +.subPageBtn { + padding: 8px 16px; + border-radius: 6px; + border: 1px solid #ccc; + background: #f1f1f1; + cursor: pointer; + font-weight: 500; + color: #333; +} + +.subPageBtn:hover { + background: #e1e1e1; +} + +.subPageBtnDark { + background: #2b3a55; + border-color: #666; + color: #fff; +} + +.subPageBtnDark:hover { + background: #3a4b66; +} + +.backBtn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 16px; + margin-bottom: 16px; + border-radius: 6px; + border: 1px solid #ccc; + background: #f1f1f1; + cursor: pointer; + font-weight: 500; + color: #333; +} + +.backBtn:hover { + background: #e1e1e1; +} + +.backBtnDark { + background: #2b3a55; + border-color: #666; + color: #fff; +} + +.backBtnDark:hover { + background: #3a4b66; +} + +/* ---------- Personalization Page ---------- */ +.personalizationPage { + padding: 20px; + background: #fff; + min-height: 100vh; + box-sizing: border-box; +} + +.personalizationPageDark { + background: #1c2541; + color: #fff; +} + +.personalizationHeader { + font-size: 22px; + font-weight: bold; + color: #333; + margin-bottom: 20px; +} + +.personalizationHeaderDark { + color: #fff; +} + +.personalizationContent { + display: flex; + justify-content: center; +} + +.personalizationPageDark .placeholderBox, +.personalizationPageDark .placeholderBox p { + border-color: #888; + color: #ddd; +} + /* ---------- Info Tooltip Icons ---------- */ .infoIcon { margin-left: 6px; @@ -1179,4 +1406,4 @@ .trackingTableDark .infoIcon:hover { color: #fff; -} \ No newline at end of file +} diff --git a/src/components/CommunityPortal/Reports/Participation/Personalization.jsx b/src/components/CommunityPortal/Reports/Participation/Personalization.jsx new file mode 100644 index 0000000000..18aa5e196b --- /dev/null +++ b/src/components/CommunityPortal/Reports/Participation/Personalization.jsx @@ -0,0 +1,44 @@ +import { useSelector } from 'react-redux'; +import { useHistory } from 'react-router-dom'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'; +import styles from './Participation.module.css'; + +function Personalization() { + const darkMode = useSelector(state => state.theme.darkMode); + const history = useHistory(); + + return ( +
+ + +

+ Personalization Insights +

+ +
+
+

+ This section will show user engagement patterns, recommended event categories, and + personalized insights based on participation history. +

+
+
+
+ ); +} + +export default Personalization; diff --git a/src/routes.jsx b/src/routes.jsx index 5ebcced785..a058e6869a 100644 --- a/src/routes.jsx +++ b/src/routes.jsx @@ -91,6 +91,9 @@ import ResourcesUsage from './components/CommunityPortal/Activities/activityId/R import EventNoShowChart from './components/CommunityPortal/Attendence/NoshowViz'; import EventList from './components/CommunityPortal/Event/EventList/EventList'; import EventParticipation from './components/CommunityPortal/Reports/Participation/EventParticipation'; +import Demographics from './components/CommunityPortal/Reports/Participation/Demographics'; +import Personalization from './components/CommunityPortal/Reports/Participation/Personalization'; + import MaterialSummary from './components/MaterialSummary/MaterialSummary'; import FeedbackRatingEntry from './components/FeedbackActivityModal/FeedbackActivityEntry'; import AddLessons from './components/BMDashboard/Lessons/AddLessons'; @@ -1116,7 +1119,32 @@ export default ( - + } + /> + } + /> + + + +