Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
7410528
feat: Add TODO comment for timezone standardization in EventCard
pavanputti Dec 27, 2025
f67d61e
feat: Add timezoneUtils.js skeleton structure for Phase 2
pavanputti Dec 27, 2025
c34b8fe
feat: Phase 3 - Partial implementation and exploration
pavanputti Dec 27, 2025
7b43fbe
feat: Phase 4 - Testing and bug fixes
pavanputti Dec 28, 2025
f302cc5
Phase 5: Complete timezone conversion implementation
pavanputti Jan 2, 2026
2508408
Fix timezone abbreviation consistency and improve event card alignment
pavanputti Jan 3, 2026
e343574
Refine search logic structure and improve code documentation
pavanputti Jan 10, 2026
2d9bf50
Fix SonarQube issue: Replace isNaN with Number.isNaN for better relia…
pavanputti Jan 11, 2026
a0dfacc
Fix SonarQube issue: Replace isNaN with Number.isNaN in EventCard.jsx
pavanputti Jan 11, 2026
c1c00c8
Fix SonarQube issues: Handle exceptions properly and replace isNaN wi…
pavanputti Jan 11, 2026
0116f9e
removed unnecessary comments
pavanputti Jan 25, 2026
ab8aa71
Merge origin/development into feature/standardize-event-timezone-disp…
pavanputti Feb 7, 2026
84c03ab
Merge origin/development: resolve CPDashboard conflict, keep timezone…
pavanputti Feb 22, 2026
b2cf11a
Fix reviewer feedback: Location TBD, consistent timezone conversion, …
pavanputti Mar 14, 2026
f7e332e
Merge origin/development: resolve CPDashboard.module.css and Activity…
pavanputti Mar 14, 2026
35f7c07
Fix critical bug in formatDateTime: use moment.utc() to prevent doubl…
pavanputti Apr 9, 2026
743a4e3
Fix SonarQube: handle caught exceptions properly in CPDashboard
pavanputti Apr 10, 2026
0aa4819
Merge origin/development: resolve CPDashboard conflict, keep timezone…
pavanputti Apr 10, 2026
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
63 changes: 46 additions & 17 deletions src/components/CommunityPortal/CPDashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
Label,
} from 'reactstrap';
import { FaCalendarAlt, FaMapMarkerAlt, FaUserAlt, FaSearch, FaTimes } from 'react-icons/fa';
import { format } from 'date-fns';
import { getUserTimezone, formatEventTimeWithTimezone } from '../../utils/timezoneUtils';
import styles from './CPDashboard.module.css';
import { ENDPOINTS } from '../../utils/URL';
import DatePicker from 'react-datepicker';
Expand Down Expand Up @@ -132,7 +134,7 @@ export function CPDashboard() {
total: response.data.events?.length || 0,
}));
} catch (err) {
console.error('Failed to fetch events', err);
console.error('Failed to load events:', err);
setError('Failed to load events');
} finally {
setIsLoading(false);
Expand Down Expand Up @@ -180,34 +182,55 @@ export function CPDashboard() {
};

const formatDate = dateStr => {
if (!dateStr) {
if (!dateStr) return 'Date TBD';
try {
const date = new Date(dateStr);
if (Number.isNaN(date.getTime())) {
return 'Invalid date';
}
// Format: "Saturday, February 15"
return format(date, 'EEEE, MMMM d');
} catch (err) {
console.error('Error formatting date:', err);
return 'Date TBD';
}
};

const date = new Date(dateStr);
return date.toLocaleString('en-US', {
weekday: 'long',
month: 'long',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: '2-digit',
});
const formatTime = (eventDate, timeStr) => {
if (!timeStr) return 'Time TBD';
try {
const userTimezone = getUserTimezone();
return formatEventTimeWithTimezone(eventDate, timeStr, userTimezone);
} catch (err) {
console.error('Error formatting time:', err);
return 'Time TBD';
}
};

const getDisplayLocation = location => {
if (
location == null ||
String(location).trim() === '' ||
String(location).toLowerCase() === 'tbd'
) {
return 'Location TBD';
}
return location;
};

const parseEventDate = dateString => {
if (!dateString) return null;

try {
const parsedDate = new Date(dateString);
if (!isNaN(parsedDate.getTime())) {
if (!Number.isNaN(parsedDate.getTime())) {
const year = parsedDate.getFullYear();
const month = String(parsedDate.getMonth() + 1).padStart(2, '0');
const day = String(parsedDate.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
} catch (err) {
console.error('Error parsing date:', err);
console.error('Error parsing event date:', err);
}
return null;
};
Expand Down Expand Up @@ -298,11 +321,17 @@ export function CPDashboard() {
</div>
<CardBody>
<h5 className={styles.eventTitle}>{event.title}</h5>
<p className={styles.eventDate}>
<FaCalendarAlt className={styles.eventIcon} /> {formatDate(event.date)}
</p>
<div className={styles.eventDate}>
<FaCalendarAlt className={styles.eventIcon} />
<div>
<div>{formatDate(event.date)}</div>
{event.startTime && (
<div className={styles.eventTime}>{formatTime(event.date, event.startTime)}</div>
)}
</div>
</div>
<p className={styles.eventLocation}>
<FaMapMarkerAlt className={styles.eventIcon} /> {event.location || 'Location TBD'}
<FaMapMarkerAlt className={styles.eventIcon} /> {getDisplayLocation(event.location)}
</p>
<p className={styles.eventOrganizer}>
{event.organizerLogo && !failedLogos.has(event._id) ? (
Expand Down
50 changes: 48 additions & 2 deletions src/components/CommunityPortal/CPDashboard.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,39 @@
font-size: 1rem;
color: #555;
display: flex;
align-items: center;
align-items: flex-start;
gap: 8px;
margin: 5px 0;
margin: 8px 0;
line-height: 1.5;
}

.eventDate > svg,
.eventLocation > svg,
.eventOrganizer > svg {
flex-shrink: 0;
width: 16px;
height: 16px;
margin-top: 3px;
align-self: flex-start;
}

.eventDate > div {
display: flex;
flex-direction: column;
gap: 4px;
flex: 1;
min-width: 0;
}

.eventDate > div > div:first-child {
line-height: 1.4;
}

.eventTime {
font-size: 0.9rem;
color: #777;
font-weight: 500;
line-height: 1.4;
}

.organizerLogo {
Expand All @@ -367,6 +397,22 @@
font-size: 1rem;
}

/* Dark mode: ensure date, time, location and organizer are readable */
.darkMain .eventDate,
.darkMain .eventDate .eventTime,
.darkMain .eventLocation,
.darkMain .eventOrganizer {
color: #e4e6eb;
}

.darkMain .eventTime {
color: #b0b3b8;
}

.darkMain .eventIcon {
color: #e4e6eb;
}

.noEvents {
text-align: center;
padding: 40px;
Expand Down
55 changes: 49 additions & 6 deletions src/components/CommunityPortal/Event/EventCard/EventCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
faTag,
} from '@fortawesome/free-solid-svg-icons';
import { format } from 'date-fns';
import { getUserTimezone, formatEventTimeWithTimezone } from '../../../../utils/timezoneUtils';
import styles from './EventCard.module.css';

function EventCard(props) {
Expand Down Expand Up @@ -48,10 +49,48 @@ function EventCard(props) {
return (locationType?.toLowerCase() || '') === 'virtual' ? 'virtual-tag' : 'in-person-tag';
};

const formatDateTime = dateString => {
const getDisplayLocation = () => {
if (
location == null ||
String(location).trim() === '' ||
String(location).toLowerCase() === 'tbd'
) {
return 'Location TBD';
}
return location;
};

const formatDate = dateString => {
if (!dateString) {
return 'Date not set';
}
try {
const date = new Date(dateString);
if (Number.isNaN(date.getTime())) {
return 'Invalid date';
}
return format(date, 'MMM dd, yyyy');
} catch (error) {
console.error('Error formatting date:', error);
return 'Date not set';
}
};

const formatDateTime = (eventDate, timeString) => {
Copy link
Copy Markdown

@naznin07 naznin07 Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical bug in formatDateTime.

PR # 4633

try {
return format(new Date(dateString), 'h:mm a');
if (!timeString) {
return 'Time not set';
}
// eventDate is required to correctly anchor a time-only string (e.g. "5:00 PM")
// to a UTC datetime before conversion. Without it, toFullEventDatetime falls back
// to parsing in the local machine timezone, producing inconsistent results.
if (!eventDate) {
return 'Date not set';
}
const userTimezone = getUserTimezone();
return formatEventTimeWithTimezone(eventDate, timeString, userTimezone);
} catch (error) {
console.error('Error formatting date time:', error);
return 'Time not set';
}
};
Expand Down Expand Up @@ -97,8 +136,12 @@ function EventCard(props) {
<div className="d-flex align-items-center mb-2">
<FontAwesomeIcon icon={faMapMarkerAlt} className="me-2 text-muted" />
<span className="text-muted">Location:</span>
<span className={`ms-2 ${styles['attendee-tag']} ${styles[getLocationTag(location)]}`}>
{location}
<span
className={`ms-2 ${styles['attendee-tag']} ${
styles[getLocationTag(getDisplayLocation())]
}`}
>
{getDisplayLocation()}
</span>
</div>
<div className={`${styles['event-description']} mb-2`}>
Expand All @@ -111,12 +154,12 @@ function EventCard(props) {
<div className="mb-4">
<div className="d-flex align-items-center mb-2">
<FontAwesomeIcon icon={faCalendar} className="me-2" />
<span>{format(new Date(date), 'MMM dd, yyyy')}</span>
<span>{formatDate(date)}</span>
</div>
<div className="d-flex align-items-center mb-2">
<FontAwesomeIcon icon={faClock} className="me-2" />
<span>
{formatDateTime(startTime)} - {formatDateTime(endTime)}
{formatDateTime(date, startTime)} - {formatDateTime(date, endTime)}
</span>
</div>
</div>
Expand Down
Loading
Loading