diff --git a/src/components/CommunityPortal/CPDashboard.jsx b/src/components/CommunityPortal/CPDashboard.jsx
index 7f9c5b2f9b8..e5b93c93624 100644
--- a/src/components/CommunityPortal/CPDashboard.jsx
+++ b/src/components/CommunityPortal/CPDashboard.jsx
@@ -1,99 +1,14 @@
-import { useState, useEffect, useRef, useCallback } from 'react';
+import { useState, useEffect, useRef } from 'react';
+import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
-import { Container, Row, Alert, Col, Card, CardBody, Button, Input } from 'reactstrap';
-import {
- FaCalendarAlt,
- FaMapMarkerAlt,
- FaUserAlt,
- FaSearch,
- FaTimes,
- FaHistory,
-} from 'react-icons/fa';
+import { Container, Row, Alert, Col, Card, CardBody, Button, Input, Label } from 'reactstrap';
+import { FaCalendarAlt, FaMapMarkerAlt, FaUserAlt, FaSearch, FaTimes } from 'react-icons/fa';
import styles from './CPDashboard.module.css';
import { ENDPOINTS } from '../../utils/URL';
import { Link } from 'react-router-dom';
import axios from 'axios';
-import { isTomorrow, isComingWeekend } from './utils';
-
-const RECENT_SEARCHES_KEY = 'cp_recent_searches';
-const MAX_RECENT_SEARCHES = 10;
-
-function useRecentSearches(key = RECENT_SEARCHES_KEY, maxItems = MAX_RECENT_SEARCHES) {
- const [recentSearches, setRecentSearches] = useState(() => {
- try {
- const stored = localStorage.getItem(key);
- return stored ? JSON.parse(stored) : [];
- } catch {
- return [];
- }
- });
-
- const addSearch = useCallback(
- query => {
- const trimmed = query.trim();
- if (!trimmed) return;
- setRecentSearches(prev => {
- const updated = [trimmed, ...prev.filter(s => s !== trimmed)].slice(0, maxItems);
- localStorage.setItem(key, JSON.stringify(updated));
- return updated;
- });
- },
- [key, maxItems],
- );
-
- const removeSearch = useCallback(
- query => {
- setRecentSearches(prev => {
- const updated = prev.filter(s => s !== query);
- localStorage.setItem(key, JSON.stringify(updated));
- return updated;
- });
- },
- [key],
- );
-
- return { recentSearches, addSearch, removeSearch };
-}
-
-function RecentSearchDropdown({ searches, onSelect, onRemove, darkMode }) {
- if (!searches.length) return null;
- return (
-
-
-
- Recently Searched
-
- {searches.map(term => (
-
-
-
-
- ))}
-
- );
-}
-const FixedRatioImage = ({ src, alt, fallback }) => (
+const FixedRatioImage = ({ src = '', alt = '', fallback }) => (
(
);
+FixedRatioImage.propTypes = {
+ src: PropTypes.string,
+ alt: PropTypes.string,
+ fallback: PropTypes.string.isRequired,
+};
+
// Default filter values
const DEFAULT_FILTERS = {
dateFilter: '',
@@ -128,35 +49,348 @@ const DEFAULT_FILTERS = {
categories: '',
};
-function passesFilters(
- event,
- { showPastEvents, isPastEvent, onlineOnly, dateFilter, searchQuery },
-) {
- if (!showPastEvents && isPastEvent(event)) return false;
- if (onlineOnly && event.location?.toLowerCase() !== 'virtual') return false;
- if (dateFilter === 'tomorrow') return isTomorrow(event.date);
- if (dateFilter === 'weekend') return isComingWeekend(event.date);
- if (!searchQuery) return true;
- const term = searchQuery.toLowerCase();
+// Shared shape for a community event
+const eventShape = PropTypes.shape({
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ image: PropTypes.string,
+ title: PropTypes.string,
+ date: PropTypes.string,
+ location: PropTypes.string,
+ organizer: PropTypes.string,
+});
+
+// Helper: combine base and dark variant class names
+const cx = (base, darkMode) => {
+ if (!darkMode) return styles[base];
+ const darkKey = `${base}-dark`;
+ return `${styles[base]} ${styles[darkKey]}`;
+};
+
+const formatDate = dateStr => {
+ if (!dateStr) return 'Date TBD';
+ const date = new Date(dateStr);
+ return date.toLocaleString('en-US', {
+ weekday: 'long',
+ month: 'long',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: '2-digit',
+ });
+};
+
+const LoadingView = ({ darkMode }) => (
+
+ Loading events...
+
+);
+
+LoadingView.propTypes = {
+ darkMode: PropTypes.bool.isRequired,
+};
+
+const ErrorView = ({ error, darkMode }) => (
+
+
+ {error}
+
+
+);
+
+ErrorView.propTypes = {
+ error: PropTypes.string.isRequired,
+ darkMode: PropTypes.bool.isRequired,
+};
+
+const SearchBar = ({ searchInput, setSearchInput, onSearch, onClear, onKeyDown, darkMode }) => (
+
+
+ setSearchInput(e.target.value)}
+ onKeyDown={onKeyDown}
+ className={cx('dashboard-search-input', darkMode)}
+ />
+
+ {searchInput && (
+
+ )}
+
+
+
+
+);
+
+SearchBar.propTypes = {
+ searchInput: PropTypes.string.isRequired,
+ setSearchInput: PropTypes.func.isRequired,
+ onSearch: PropTypes.func.isRequired,
+ onClear: PropTypes.func.isRequired,
+ onKeyDown: PropTypes.func.isRequired,
+ darkMode: PropTypes.bool.isRequired,
+};
+
+const FiltersSidebar = ({ darkMode }) => (
+
+);
+
+FiltersSidebar.propTypes = {
+ darkMode: PropTypes.bool.isRequired,
+};
+
+// Figma-consistent typography for event cards.
+// Font properties apply in both modes; colors only in light mode so that
+// dark-mode text classes (event-title-dark / event-text-dark) still win.
+const EVENT_TITLE_FONT = {
+ textAlign: 'left',
+ margin: '0 0 8px',
+ fontWeight: 600,
+ fontSize: '1.1rem',
+ lineHeight: 1.3,
+};
+
+const EVENT_META_FONT = {
+ fontSize: '0.86rem',
+ gap: '6px',
+ margin: '4px 0',
+ lineHeight: 1.35,
+ fontWeight: 500,
+};
+
+const EVENT_ICON_FONT = {
+ marginTop: '1px',
+ fontSize: '0.78rem',
+};
+
+const EventCard = ({ event, darkMode, fallback }) => {
+ const titleStyle = darkMode ? EVENT_TITLE_FONT : { ...EVENT_TITLE_FONT, color: '#2f3f55' };
+ const metaStyle = darkMode ? EVENT_META_FONT : { ...EVENT_META_FONT, color: '#5c6b7a' };
+ const iconStyle = darkMode ? EVENT_ICON_FONT : { ...EVENT_ICON_FONT, color: '#6b7785' };
+
return (
- event.title?.toLowerCase().includes(term) ||
- event.location?.toLowerCase().includes(term) ||
- event.organizer?.toLowerCase().includes(term)
+
+
+
+
+
+
+
+ {event.title}
+
+
+ {' '}
+ {formatDate(event.date)}
+
+
+ {event.location}
+
+
+ {event.organizer}
+
+
+
+
);
-}
+};
+
+EventCard.propTypes = {
+ event: eventShape.isRequired,
+ darkMode: PropTypes.bool.isRequired,
+ fallback: PropTypes.string.isRequired,
+};
+
+const EventsGrid = ({ events, darkMode, fallback }) => (
+
+ {events.length > 0 ? (
+ events.map(ev => )
+ ) : (
+
+ No events available
+
+ )}
+
+);
+
+EventsGrid.propTypes = {
+ events: PropTypes.arrayOf(eventShape).isRequired,
+ darkMode: PropTypes.bool.isRequired,
+ fallback: PropTypes.string.isRequired,
+};
+
+const PaginationControls = ({ pagination, totalPages, goToPage }) =>
+ totalPages > 1 ? (
+
+
+
+ Page {pagination.currentPage} of {totalPages}
+
+
+
+ ) : null;
+
+PaginationControls.propTypes = {
+ pagination: PropTypes.shape({
+ currentPage: PropTypes.number,
+ limit: PropTypes.number,
+ total: PropTypes.number,
+ }).isRequired,
+ totalPages: PropTypes.number.isRequired,
+ goToPage: PropTypes.func.isRequired,
+};
-export function CPDashboard() {
+function CPDashboard() {
+ const darkMode = useSelector(state => state.theme.darkMode);
const [events, setEvents] = useState([]);
const [searchInput, setSearchInput] = useState('');
const [searchQuery, setSearchQuery] = useState('');
const [selectedDate, setSelectedDate] = useState('');
- const [onlineOnly] = useState(false);
+ const [onlineOnly, setOnlineOnly] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [showPastEvents, setShowPastEvents] = useState(false);
- const [showRecentSearches, setShowRecentSearches] = useState(false);
- const darkMode = useSelector(state => state.theme.darkMode);
- const { recentSearches, addSearch, removeSearch } = useRecentSearches();
+
+ // Darken the page body in dark mode (app-wide pattern) so the area around the
+ // dashboard isn't left white.
+ useEffect(() => {
+ document.body.classList.toggle('dark-mode-body', darkMode);
+ return () => document.body.classList.remove('dark-mode-body');
+ }, [darkMode]);
// Hide the global back-to-top button — not needed on this page
useEffect(() => {
@@ -202,13 +436,6 @@ export function CPDashboard() {
fetchEvents();
}, []);
- // Darken the page body in dark mode (app-wide pattern) so the area
- // behind the dashboard isn't left white.
- useEffect(() => {
- document.body.classList.toggle('dark-mode-body', darkMode);
- return () => document.body.classList.remove('dark-mode-body');
- }, [darkMode]);
-
useEffect(() => {
const handler = setTimeout(() => {
setSearchQuery(searchInput.trim());
@@ -221,13 +448,11 @@ export function CPDashboard() {
const handleSearchClick = () => {
const trimmed = searchInput.trim();
setSearchQuery(trimmed);
- addSearch(trimmed);
setPagination(prev => ({ ...prev, currentPage: 1 }));
- setShowRecentSearches(false);
};
// keep this near your refs/functions
- const BASE_HEIGHT = 32;
+ const BASE_HEIGHT = 36;
const autoGrow = el => {
if (!el) return;
@@ -236,9 +461,6 @@ export function CPDashboard() {
};
const searchRef = useRef(null);
- const recentDropdownRef = useRef(null);
- const blurTimeoutRef = useRef(null);
-
useEffect(() => {
autoGrow(searchRef.current); // ✅ runs even when you clear via button
}, [searchInput]);
@@ -248,36 +470,10 @@ export function CPDashboard() {
e.preventDefault(); // ✅ stops newline
const trimmed = searchInput.trim();
setSearchQuery(trimmed);
- addSearch(trimmed);
setPagination(prev => ({ ...prev, currentPage: 1 }));
- setShowRecentSearches(false);
- }
- };
-
- const handleSearchFocus = () => {
- setShowRecentSearches(true);
- };
-
- const handleSearchBlur = () => {
- blurTimeoutRef.current = setTimeout(() => {
- setShowRecentSearches(false);
- }, 200);
- };
-
- const handleRecentDropdownMouseDown = () => {
- if (blurTimeoutRef.current) {
- clearTimeout(blurTimeoutRef.current);
}
};
- const handleSelectRecent = term => {
- setSearchInput(term);
- setSearchQuery(term);
- setPagination(prev => ({ ...prev, currentPage: 1 }));
- setShowRecentSearches(false);
- if (searchRef.current) searchRef.current.focus();
- };
-
const formatDate = dateStr => {
if (!dateStr) return 'Date TBD';
const date = new Date(dateStr);
@@ -285,11 +481,35 @@ export function CPDashboard() {
weekday: 'long',
month: 'long',
day: 'numeric',
+ year: 'numeric',
hour: 'numeric',
minute: '2-digit',
});
};
+ const isTomorrow = dateString => {
+ const input = new Date(dateString);
+ const today = new Date();
+ today.setHours(0, 0, 0, 0);
+ const tomorrow = new Date(today);
+ tomorrow.setDate(today.getDate() + 1);
+ return input >= tomorrow && input < new Date(tomorrow.getTime() + 24 * 60 * 60 * 1000);
+ };
+
+ const isComingWeekend = dateString => {
+ const input = new Date(dateString);
+ const today = new Date();
+ today.setHours(0, 0, 0, 0);
+ const day = today.getDay();
+ const daysUntilSaturday = (6 - day + 7) % 7 || 7;
+ const saturday = new Date(today);
+ saturday.setDate(today.getDate() + daysUntilSaturday);
+ const sunday = new Date(saturday);
+ sunday.setDate(saturday.getDate() + 1);
+ sunday.setHours(23, 59, 59, 999);
+ return input >= saturday && input <= sunday;
+ };
+
// Handler to update pending filter values
const handleFilterChange = (filterName, value) => {
setPendingFilters(prev => ({
@@ -298,6 +518,15 @@ export function CPDashboard() {
}));
};
+ // Toggle the date radio: clicking the active option clears it; clicking another switches.
+ // Applies immediately (bypasses the Apply Filters button) so the user sees instant feedback.
+ const handleDateToggle = value => {
+ const next = appliedFilters.dateFilter === value ? '' : value;
+ setPendingFilters(prev => ({ ...prev, dateFilter: next }));
+ setAppliedFilters(prev => ({ ...prev, dateFilter: next }));
+ setPagination(prev => ({ ...prev, currentPage: 1 }));
+ };
+
// Apply all pending filters
const handleApplyFilters = () => {
setAppliedFilters(pendingFilters);
@@ -318,16 +547,32 @@ export function CPDashboard() {
if (!ref) return false;
return new Date(ref) < now;
};
+ // Filter events based on applied filters
+ const filteredEvents = events.filter(event => {
+ if (!showPastEvents && isPastEvent(event)) return false;
+ // Filter by online only
+ if (appliedFilters.onlineOnly) {
+ const isOnlineEvent = event.location?.toLowerCase() === 'virtual';
+ if (!isOnlineEvent) return false;
+ }
- const filteredEvents = events.filter(event =>
- passesFilters(event, {
- showPastEvents,
- isPastEvent,
- onlineOnly: appliedFilters.onlineOnly,
- dateFilter: appliedFilters.dateFilter,
- searchQuery,
- }),
- );
+ // Filter by date
+ if (appliedFilters.dateFilter === 'tomorrow') {
+ return isTomorrow(event.date);
+ } else if (appliedFilters.dateFilter === 'weekend') {
+ return isComingWeekend(event.date);
+ }
+
+ // Filter by search query
+ if (!searchQuery) return true;
+
+ const term = searchQuery.toLowerCase();
+ return (
+ event.title?.toLowerCase().includes(term) ||
+ event.location?.toLowerCase().includes(term) ||
+ event.organizer?.toLowerCase().includes(term)
+ );
+ });
// Reset pagination to page 1 when filters change
useEffect(() => {
@@ -361,6 +606,7 @@ export function CPDashboard() {
);
}
+ // isLoading and error are already handled by the early returns above.
let eventsContent;
if (displayedEvents.length > 0) {
@@ -377,9 +623,7 @@ export function CPDashboard() {
-
- {event.title}
-
+ {event.title}
setSearchInput(e.target.value)}
onKeyDown={handleSearchKeyDown}
- onFocus={handleSearchFocus}
- onBlur={handleSearchBlur}
className={`${styles.dashboardSearchTextarea} ${
darkMode ? styles.darkSearchTextarea : ''
}`}
@@ -468,22 +710,6 @@ export function CPDashboard() {
{searchInput.length >= 100 && (
Max 100 characters
)}
- {showRecentSearches && (
-
-
-
- )}
@@ -491,32 +717,30 @@ export function CPDashboard() {
{/* Branches Filter */}
diff --git a/src/components/CommunityPortal/CPDashboard.module.css b/src/components/CommunityPortal/CPDashboard.module.css
index abbdaa81895..1664eb3f862 100644
--- a/src/components/CommunityPortal/CPDashboard.module.css
+++ b/src/components/CommunityPortal/CPDashboard.module.css
@@ -1,3 +1,4 @@
+/* stylelint-disable no-descending-specificity */
:global(body) {
font-family: Poppins, sans-serif;
background: #fff;
@@ -5,6 +6,12 @@
padding: 0;
}
+/* In dark mode the page body is darkened (class toggled from the component) so
+ the area around the dashboard matches the app's dark theme instead of white. */
+:global(body.dark-mode-body) {
+ background: #1b2a41;
+}
+
.dashboardContainer {
padding: 40px 10px;
margin: 0 auto;
@@ -20,13 +27,15 @@
.darkContainer {
background: #2b3e59 !important;
color: #fff;
+ border-radius: 0;
+ box-shadow: none;
}
.dashboardHeader {
display: flex;
align-items: center;
- justify-content: flex-start;
- gap: 20px;
+ justify-content: flex-start;
+ gap: 20px;
margin-bottom: 30px;
padding: 16px 24px;
background: linear-gradient(120deg, #fff, #f8f9fa);
@@ -35,6 +44,11 @@
flex-wrap: nowrap;
}
+.darkHeader{
+ background: #1b2a41;
+ color: #fff;
+}
+
.dashboardHeader h1 {
margin: 0;
font-size: 2.5rem;
@@ -44,8 +58,9 @@
flex-shrink: 0;
}
-.darkHeader {
- background: #1b2a41;
+/* In dark mode the header background is dark navy, so the navy h1 color above
+ has almost no contrast — force a light color. */
+.darkHeader h1 {
color: #fff;
}
@@ -55,6 +70,7 @@
gap: 15px;
}
+/* pill input */
.dashboardSearchInput {
width: 100%;
border-radius: 999px;
@@ -63,37 +79,41 @@
box-sizing: border-box;
}
+/* clear X button */
.dashboardClearBtn {
+ position: absolute;
+ right: 35px;
+ top: 45%;
+ transform: translateY(-50%);
border: none;
background: transparent;
cursor: pointer;
font-size: 0.9rem;
color: #1b3c55;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 0;
}
+/* search icon button inside the bar */
.dashboardSearchIconBtn {
+ position: absolute;
+ right: 1%;
+ top: 48%;
+ transform: translateY(-50%);
width: 34px;
height: 34px;
border-radius: 50%;
cursor: pointer;
background: transparent;
- border: none;
display: flex;
align-items: center;
justify-content: center;
color: #1b3c55;
font-size: 0.9rem;
- padding: 0;
}
.cp_dashboard_header {
display: flex;
align-items: center;
- justify-content: space-between;
+ justify-content: space-between;
gap: 24px;
padding: 16px 24px;
margin-bottom: 30px;
@@ -113,7 +133,12 @@
overflow: hidden;
border: 2px solid #1b3c55;
background: #fff;
+}
+
+.darkHeader .dashboardSearchContainer { /* white when NOT focused */
outline: none;
+ border-color: #fff;
+ background: #1b2a41;
}
.dashboardSearchContainer {
@@ -129,15 +154,12 @@
margin-top: 8px;
}
-.dashboardSearchContainer input,
-.dashboardSearchContainer textarea {
+.dashboardSearchContainer input {
outline: none;
}
.dashboardSearchContainer input:focus,
-.dashboardSearchContainer input:focus-visible,
-.dashboardSearchContainer textarea:focus,
-.dashboardSearchContainer textarea:focus-visible {
+.dashboardSearchContainer input:focus-visible {
outline: none;
box-shadow: none;
}
@@ -147,11 +169,6 @@
outline: none;
}
-.darkHeader .dashboardSearchContainer {
- border-color: #fff;
- background: #1b2a41;
-}
-
.dashboardSearchContainer:focus-within {
border-color: #4da3ff;
box-shadow: none;
@@ -169,11 +186,11 @@
background: transparent;
font-size: 1rem;
line-height: 1.5;
- padding: 8px 96px 8px 18px;
- min-height: 32px;
+ padding: 10px 96px 10px 18px;
+ min-height: 36px;
max-height: 120px;
box-sizing: border-box;
- overflow: hidden auto;
+ overflow: hidden auto; /* ✅ Brave-like wrapping */ /* ✅ prevents overflow */
white-space: pre-wrap;
overflow-wrap: break-word;
word-break: normal;
@@ -187,6 +204,7 @@
color: #cbd5e1;
}
+
@media (width <= 850px) {
.dashboardSearchContainer {
width: 250px;
@@ -220,6 +238,12 @@
border-color: #4da3ff;
}
+.dashboardSearchContainer,
+.dashboardSearchContainer:focus,
+.dashboardSearchContainer:focus-visible {
+ outline: none;
+}
+
.darkSearchTextarea {
color: #fff;
}
@@ -247,12 +271,42 @@
}
.darkSidebar {
- background: #2b3e59;
- border-right: 1px solid #1b2a41;
+ background: #1b2a41;
}
-.filterSection {
- width: 100%;
+.darkSidebar .filterItem input:not([type='checkbox'], [type='radio']),
+.darkSidebar .filterItem select {
+ background-color: #34495e;
+ color: #fff;
+ border: 1px solid #4a6572;
+}
+
+.darkSidebar .filterItem label {
+ background: transparent;
+ color: #ecf0f1;
+}
+
+.darkSidebar input[type="date"] {
+ color-scheme: dark;
+}
+
+.darkSidebar .inputGroup {
+ background-color: #34495e;
+ border-color: #4da3ff;
+}
+
+.darkSidebar .inputGroup input {
+ color: #fff;
+}
+
+.darkSidebar .inputGroup input::placeholder {
+ color: #cbd5e1;
+}
+
+.darkSidebar .inputGroupText {
+ background: #2b3e59;
+ border-right: 1px solid #1b2a41;
+ color: #cbd5e1;
}
.filterSection h4 {
@@ -261,8 +315,9 @@
font-weight: 600;
}
-.filterSectionHeader {
- margin-bottom: 40px;
+/* Dark sidebar background is dark navy; lighten the "Filters" heading for contrast. */
+.darkSidebar .filterSection h4 {
+ color: #ecf0f1;
}
.filterSectionDivider {
@@ -271,16 +326,20 @@
gap: 24px;
}
-.filterItem {
- margin-bottom: 0;
+.filterSectionHeader{
+ margin-bottom: 40px;
}
-.filterItem label {
- display: block;
- font-weight: 600;
- color: #34495e;
- margin-bottom: 8px;
-}
+.filterItem:has(select) label {
+ margin-bottom: 0;
+ line-height: 1.3;
+ }
+
+ .filterItem:has(select) select {
+ margin-top: 4px;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ }
.filterOptionsVertical {
display: flex;
@@ -290,6 +349,13 @@
margin-top: 10px;
}
+.filterItem label {
+ display: block;
+ font-weight: 600;
+ color: #34495e;
+ margin-bottom: 8px;
+}
+
.filterItem input:not([type='checkbox'], [type='radio'], [type='date']),
.filterItem select {
padding: 12px 15px;
@@ -303,87 +369,103 @@
transition: all 0.3s ease;
}
-.filterItem input:focus,
-.filterItem select:focus {
- border-color: #2c3e50;
- box-shadow: 0 0 5px rgb(44 62 80 / 40%);
- outline: none;
-}
-
.radioRow {
- display: flex;
- flex-direction: column;
- gap: 12px;
- margin-top: 10px;
- padding: 0;
-}
-
-.radioColumn {
display: flex;
flex-direction: column;
gap: 8px;
+ margin-top: 8px;
+ align-items: flex-start;
}
-.radioGroup {
- display: flex;
+.radioOption,
+.checkboxOption {
+ display: inline-flex;
align-items: center;
gap: 10px;
margin: 0;
+ cursor: pointer;
+ font-weight: 500;
+ background: transparent !important;
}
-.radioLabel,
-.checkboxLabel {
- display: flex;
- align-items: center;
- gap: 6px;
+.radioOption input[type='radio'],
+.checkboxOption input[type='checkbox'] {
+ margin: 0;
cursor: pointer;
- font-weight: 400;
- margin-bottom: 0;
- color: #34495e;
+ flex-shrink: 0;
}
+/* Fixed, identical size for radios and checkbox so their left edges line up
+ on a straight left line and the control-to-text gap is uniform. */
.filterItem input[type='radio'],
-.filterItem input[type='checkbox'],
-.radioInput,
-.checkboxInput {
- width: 20px;
- height: 20px;
- cursor: pointer;
- margin: 0;
+.filterItem input[type='checkbox'] {
+ display: inline-block; /* <– keeps input on same line as text */
+ width: 16px;
+ height: 16px;
padding: 0;
- accent-color: #1b3c55;
- flex-shrink: 0;
+ margin: 0;
vertical-align: middle;
}
-.darkSidebar .filterItem label,
-.darkSidebar .radioLabel,
-.darkSidebar .checkboxLabel {
- color: #ecf0f1;
+/* Tighter vertical spacing for dropdown filters (Branches/Themes/Categories) */
+.compact-dropdown-filter label {
+ margin-bottom: 4px;
}
-.darkSidebar .radioInput,
-.darkSidebar .filterItem input[type='checkbox'] {
- accent-color: #4da3ff;
+.compact-dropdown-filter select {
+ margin-top: 4px;
}
-.darkSidebar .inputGroup input {
- color: #fff;
+.filter-item input:not([type="checkbox"], [type="radio"]):focus,
+.filter-item select:focus {
+ border-color: #2c3e50;
+ box-shadow: 0 0 5px rgb(44 62 80 / 40%);
+ outline: none;
}
-.darkSidebar .filterItem input:not([type='checkbox'], [type='radio'], [type='date']),
-.darkSidebar .filterItem select {
- background-color: #34495e;
- color: #fff;
- border: 1px solid #4a6572;
+.radioGroup {
+ margin-right: 8px;
}
-.darkSidebar input[type='date'] {
- color-scheme: dark;
+.radioInput {
+ margin-right: 6px;
}
-.dateFilter {
+.radioLabel {
+ margin-bottom: 0;
+ vertical-align: middle;
+}
+
+.filterItem input:not([type="checkbox"], [type="radio"]),
+.filterItem select {
+ padding: 12px 15px;
margin-top: 10px;
+ border: 1px solid #ddd;
+ border-radius: 8px;
+ width: 100%;
+ font-size: 1rem;
+ background: #fff;
+ transition: all 0.3s ease;
+}
+
+/* Render a single dropdown chevron: hide the browser's native arrow and draw
+ one custom chevron so the selects don't show a doubled-up arrow. */
+.filterItem select {
+ appearance: none;
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%232c3e50' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ background-position: right 12px center;
+ background-size: 16px;
+ padding-right: 36px;
+}
+
+/* In the dark sidebar the select text is white, so use a white chevron. */
+.darkSidebar .filterItem select {
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
+}
+
+.dateFilter {
+ margin-top: 8px;
padding: 12px 15px;
border: 1px solid #ddd;
border-radius: 8px;
@@ -391,27 +473,28 @@
height: auto;
font-size: 1rem;
transition: all 0.3s ease;
- margin-bottom: 20px;
+ margin-bottom: 0;
}
.filterItem input[type='date'] {
display: block;
box-sizing: border-box;
padding: 12px 15px;
- margin-top: 10px;
+ margin-top: 8px;
border: 1px solid #ddd;
border-radius: 8px;
width: 100%;
height: auto;
font-size: 1rem;
transition: all 0.3s ease;
- margin-bottom: 20px;
+ margin-bottom: 0;
}
.searchFilter {
margin-bottom: 1.5rem;
}
+/* Search Input Container */
.inputGroup {
display: flex;
align-items: center;
@@ -431,19 +514,6 @@
font-size: 1.1rem;
}
-.darkSidebar .inputGroup {
- background-color: #34495e;
- border-color: #4da3ff;
-}
-
-.darkSidebar .inputGroup input::placeholder {
- color: #cbd5e1;
-}
-
-.darkSidebar .inputGroupText {
- color: #cbd5e1;
-}
-
.dashboardMain {
width: 65%;
padding: 30px;
@@ -454,17 +524,11 @@
background: #2b3e59;
}
-.darkMain .sectionTitle {
- color: #f5f6f7;
-}
-
.eventsHeader {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 30px;
- gap: 16px;
- flex-wrap: wrap;
}
.sectionTitle {
@@ -495,7 +559,7 @@
margin-bottom: 30px;
}
-.clearDateFilterBtn {
+.clearDateFilterBtn{
margin-top: 10px;
}
@@ -504,17 +568,19 @@
margin-bottom: 1.5rem;
}
-.eventCardLink {
+.eventCard:hover {
+ transform: scale(1.02);
+ box-shadow: 0 8px 20px rgb(0 0 0 / 20%);
+}
+
+.eventCardImgContainer img {
width: 100%;
- display: flex;
- text-decoration: none;
- color: inherit;
+ height: auto;
}
.eventCard {
width: 100%;
height: 100%;
- min-height: 100%;
box-shadow: 0 4px 12px rgb(0 0 0 / 10%);
display: flex;
flex-direction: column;
@@ -522,65 +588,19 @@
overflow: hidden;
border: none;
background: #fff;
- transition:
- transform 0.3s ease,
- box-shadow 0.3s ease;
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
position: relative;
}
-.eventCard:hover {
- transform: scale(1.02);
- box-shadow: 0 8px 20px rgb(0 0 0 / 20%);
-}
-
-.eventTitle::after {
- content: attr(data-event-title);
- position: absolute;
- bottom: calc(100% + 8px);
- left: 50%;
- z-index: 2;
- max-width: min(280px, calc(100vw - 48px));
- padding: 6px 10px;
- color: #fff;
- font-size: 0.9rem;
- line-height: 1.3;
- text-align: center;
- white-space: normal;
- overflow-wrap: break-word;
- background-color: #000;
- border-radius: 4px;
- opacity: 0;
- pointer-events: none;
- transform: translate(-50%, -4px);
- transition:
- opacity 0.2s ease,
- transform 0.2s ease;
-}
-
-.eventTitle:hover::after {
- opacity: 1;
- transform: translate(-50%, 0);
-}
-
.darkEventCard {
background: #1b2a41;
}
-.darkEventCard .eventTitle {
- color: #f5f6f7;
-}
-
.eventCardImgContainer {
width: 100%;
overflow: hidden;
background-color: #f0f0f0;
position: relative;
- flex-shrink: 0;
-}
-
-.eventCardImgContainer img {
- width: 100%;
- height: auto;
}
.eventCardImg {
@@ -597,22 +617,21 @@
}
.eventTitle {
- position: relative;
text-align: center;
color: #2c3e50;
margin: 0 0 15px;
font-weight: bold;
font-size: 1.3rem;
line-height: 1.4;
- overflow: visible;
- cursor: default;
-}
-
-.eventTitleText {
- display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
+ cursor: default;
+}
+
+/* On the dark event card the navy title above is unreadable — use a light color. */
+.darkEventCard .eventTitle {
+ color: #fff;
}
.eventDate,
@@ -658,19 +677,17 @@
.noEvents {
text-align: center;
padding: 40px;
- color: #888;
+ color: #595959; /* darker gray to meet AA contrast on the white background */
font-size: 1.2rem;
- width: 100%;
}
.darkMain .noEvents {
- color: #9ca3af;
+ color: #e4e6eb; /* lighter gray to meet AA contrast on the dark background */
}
.dashboardActions {
text-align: center;
- margin-top: 20px;
- margin-bottom: 20px;
+ margin-top: 30px;
}
.dashboardActions button {
@@ -695,11 +712,6 @@
display: flex;
align-items: center;
gap: 12px;
- flex-wrap: wrap;
-}
-
-.paginationInfo {
- font-weight: 500;
}
.paginationBtn {
@@ -708,31 +720,24 @@
color: #fff !important;
}
-.paginationBtn:disabled {
- opacity: 0.65;
-}
-
-.paginationBtn:hover:not(:disabled) {
- background-color: #5a6268 !important;
- border-color: #545b62 !important;
-}
+/* Hide browser's built-in clear icon for search inputs */
.filterActions {
display: flex;
- flex-direction: row;
+ flex-direction: row;
gap: 10px;
margin-top: 20px;
padding-top: 15px;
- width: 100%;
+ width: 100%;
}
.applyBtn {
flex: 1;
- background-color: #1e8449 !important;
+ background-color: #198754 !important; /* darker success green: white text meets AA contrast */
border: none;
color: white;
- padding: 10px 5px;
- font-size: 0.9rem;
+ padding: 10px 5px;
+ font-size: 0.9rem;
font-weight: 600;
border-radius: 8px;
cursor: pointer;
@@ -740,16 +745,16 @@
}
.applyBtn:hover {
- background-color: #156b3a !important;
+ background-color: #146c43 !important;
box-shadow: 0 4px 8px rgb(39 174 96 / 30%);
}
.clearBtn {
flex: 1;
- background-color: #6c757d !important;
+ background-color: #6c757d !important; /* darker secondary gray: white text meets AA contrast */
border: none;
color: white;
- padding: 10px 5px;
+ padding: 10px 5px;
font-size: 0.9rem;
font-weight: 600;
border-radius: 8px;
@@ -758,7 +763,7 @@
}
.clearBtn:hover {
- background-color: #7f8c8d !important;
+ background-color: #565e64 !important;
box-shadow: 0 4px 8px rgb(149 165 166 / 30%);
}
@@ -766,113 +771,6 @@
border-top-color: #3a4e6a;
}
-/* Recent Search Dropdown */
-.recentSearchDropdown {
- position: absolute;
- top: 100%;
- left: 0;
- right: 0;
- z-index: 10;
- background: #fff;
- border: 1px solid #ddd;
- border-radius: 8px;
- box-shadow: 0 4px 12px rgb(0 0 0 / 15%);
- margin-top: 4px;
- max-height: 240px;
- overflow-y: auto;
-}
-
-.darkRecentSearchDropdown {
- background: #1b2a41;
- border-color: #4a6572;
-}
-
-.recentSearchHeader {
- display: flex;
- align-items: center;
- gap: 6px;
- padding: 8px 12px;
- font-size: 0.75rem;
- font-weight: 600;
- color: #888;
- text-transform: uppercase;
- letter-spacing: 0.5px;
- border-bottom: 1px solid #eee;
-}
-
-.darkRecentSearchDropdown .recentSearchHeader {
- color: #9ca3af;
- border-bottom-color: #3a4e6a;
-}
-
-.recentIcon {
- color: #888;
- font-size: 0.7rem;
-}
-
-.recentIconDark {
- color: #9ca3af;
- font-size: 0.7rem;
-}
-
-.recentSearchItem {
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-
-.recentSearchTerm {
- flex: 1;
- display: flex;
- align-items: center;
- gap: 8px;
- padding: 8px 12px;
- border: none;
- background: transparent;
- cursor: pointer;
- font-size: 0.9rem;
- color: #34495e;
- text-align: left;
- transition: background-color 0.15s ease;
-}
-
-.recentSearchTerm:hover {
- background: #f5f5f5;
-}
-
-.darkRecentSearchTerm {
- color: #ecf0f1;
-}
-
-.darkRecentSearchTerm:hover {
- background: #2b3e59;
-}
-
-.recentSearchRemove {
- border: none;
- background: transparent;
- cursor: pointer;
- padding: 4px 8px;
- color: #999;
- font-size: 0.75rem;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: color 0.15s ease;
-}
-
-.recentSearchRemove:hover {
- color: #e74c3c;
-}
-
-.darkRecentSearchRemove {
- color: #7f8c8d;
-}
-
-.darkRecentSearchRemove:hover {
- color: #e74c3c;
-}
-
@media (width <= 768px) {
.dashboardHeader {
flex-wrap: wrap;
@@ -881,26 +779,13 @@
.cp_dashboard_search_container {
width: 100%;
}
+}
- .centeredRow {
- padding: 15px;
- }
-
- .dashboardMain {
- width: 100%;
- padding: 20px 10px;
- }
-
- .dashboardSidebar {
- margin-bottom: 20px;
- }
-
- .eventsHeader {
- flex-direction: column;
- align-items: flex-start;
- }
+.paginationBtn:disabled {
+ opacity: 0.65;
+}
- .filterActions {
- flex-direction: column;
- }
+.paginationBtn:hover:not(:disabled) {
+ background-color: #5a6268 !important;
+ border-color: #545b62 !important;
}
diff --git a/src/utils/URL.js b/src/utils/URL.js
index 29e9d924015..08f3545ce6e 100644
--- a/src/utils/URL.js
+++ b/src/utils/URL.js
@@ -563,7 +563,7 @@ export const ENDPOINTS = {
SUPPLIER_PROJECTS: `${APIEndpoint}/suppliers/projects`,
// event endpoint
- EVENTS: `${APIEndpoint}/events`,
+ EVENTS: `${APIEndpoint}/communityportal/events`,
EVENTS_BY_ID: (activityId) => `${APIEndpoint}/events/${activityId}`,
REGISTER_FOR_EVENT: (activityId) => `${APIEndpoint}/events/${activityId}/register`,
EVENT_TYPES: `${APIEndpoint}/events/types`,