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
26 changes: 24 additions & 2 deletions src/actions/summit-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import {
stopLoading,
startLoading,
showSuccessMessage,
authErrorHandler
authErrorHandler,
escapeFilterValue
} from "openstack-uicore-foundation/lib/utils/actions";
import moment from "moment-timezone";
import {
Expand Down Expand Up @@ -130,7 +131,12 @@ export const clearCurrentSummit = () => (dispatch) => {
};

export const loadSummits =
(page = DEFAULT_CURRENT_PAGE, perPage = DEFAULT_PER_PAGE) =>
(
page = DEFAULT_CURRENT_PAGE,
perPage = DEFAULT_PER_PAGE,
term = "",
hidePastEvents = false
) =>
async (dispatch, getState) => {
const accessToken = await getAccessTokenSafely();

Expand All @@ -146,6 +152,22 @@ export const loadSummits =
order: "-start_date"
};

const filters = [];

if (term) {
const escapedTerm = escapeFilterValue(term);
filters.push(`name=@${escapedTerm}`);
}

if (hidePastEvents) {
const now = moment().tz("UTC").unix();
filters.push(`end_date>=${now}`);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if (filters.length > 0) {
params["filter[]"] = filters;
}

getRequest(
createAction(REQUEST_SUMMITS),
createAction(RECEIVE_SUMMITS),
Expand Down
19 changes: 16 additions & 3 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -297,10 +297,23 @@
},
"directory": {
"select": "Select",
"summits": "Events",
"add_summit": "Add Event",
"summits": "Shows",
"shows": "shows",
"add_summit": "Add Show",
"remove_warning": "Are you sure you want to delete event",
"invitation_only": "Invitation Only"
"error_loading": "Error loading directory.",
"invitation_only": "Invitation Only",
"id": "ID",
"summit_name": "Event Name",
"sponsors": "Sponsors",
"forms": "Forms",
"attachments": "Attachments",
"hide_past_events": "Hide past events",
"start_date": "Start Date",
"end_date": "End Date",
"placeholders": {
"search": "Search..."
}
},
"dashboard": {
"dashboard": "Dashboard",
Expand Down
113 changes: 74 additions & 39 deletions src/pages/summits/summit-dashboard-page.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
* */
import React from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import moment from "moment-timezone";
import { getSummitById } from "../../actions/summit-actions";
import T from "i18n-react/dist/i18n-react";
import { Breadcrumb } from "react-breadcrumbs";
import { getSummitById } from "../../actions/summit-actions";
import Member from "../../models/member";
import "../../styles/summit-dashboard-page.less";

Expand All @@ -37,18 +38,21 @@ class SummitDashboardPage extends React.Component {
}

onCollapseChange(section) {
let newCollapseState = { ...this.state.collapseState };
const newCollapseState = { ...this.state.collapseState };
newCollapseState[section] = !newCollapseState[section];
this.setState({ ...this.state, collapseState: newCollapseState });
}

componentDidMount() {
const { currentSummit } = this.props;
this.interval = setInterval(this.localTimer.bind(this), 1000);
this.interval = setInterval(
this.localTimer.bind(this),
moment.duration(1, "second").asMilliseconds()
);

if (currentSummit) {
let localtime = moment().tz(currentSummit.time_zone.name);
this.setState({ ...this.state, localtime: localtime });
if (currentSummit?.time_zone?.name) {
const localtime = moment().tz(currentSummit.time_zone.name);
this.setState({ ...this.state, localtime });
}
}

Expand All @@ -63,8 +67,8 @@ class SummitDashboardPage extends React.Component {
}

getFormattedTime(atime) {
atime = atime * 1000;
return moment(atime)
return moment
.unix(atime)
.tz(this.props.currentSummit.time_zone.name)
.format("MMMM Do YYYY, h:mm:ss a");
}
Expand All @@ -77,10 +81,10 @@ class SummitDashboardPage extends React.Component {

render() {
const { currentSummit, match, member } = this.props;
let memberObj = new Member(member);
let canEditSummit = memberObj.canEditSummit();
const memberObj = new Member(member);
const canEditSummit = memberObj.canEditSummit();
Comment on lines +84 to +85
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how the Member constructor handles undefined/null
ast-grep --pattern 'class Member {
  constructor($$$) {
    $$$
  }
  $$$
}'

Repository: fntechgit/summit-admin

Length of output: 6183


🏁 Script executed:

#!/bin/bash
# Check the PropTypes declaration and the context around lines 84-85
cat -n src/pages/summits/summit-dashboard-page.js | sed -n '1,100p'

Repository: fntechgit/summit-admin

Length of output: 3783


🏁 Script executed:

#!/bin/bash
# Search for PropTypes definition in the component
cat -n src/pages/summits/summit-dashboard-page.js | tail -n +100

Repository: fntechgit/summit-admin

Length of output: 17420


Guard against undefined member before calling canEditSummit().

The member prop is optional (no .isRequired in PropTypes at line 464), so it can arrive as undefined. The Member constructor stores it without validation, and canEditSummit() immediately accesses this._member.groups without a null check, causing a runtime crash. The early-return guard on line 87 only checks currentSummit, not member.

Add a null check before instantiating the Member or before calling canEditSummit():

const memberObj = member ? new Member(member) : null;
const canEditSummit = memberObj?.canEditSummit() ?? false;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/summits/summit-dashboard-page.js` around lines 84 - 85, Guard
against member being undefined before calling Member or canEditSummit: check the
optional member prop and only construct Member when present (e.g., set memberObj
to null when member is falsy) and compute canEditSummit using a safe fallback
(call memberObj.canEditSummit() only when memberObj exists and default to false
otherwise); update the code around the Member instantiation and the
canEditSummit usage so Member and canEditSummit are not invoked when member is
undefined.


if (!currentSummit.id) return <div />;
if (!currentSummit.id || !currentSummit.time_zone?.name) return <div />;

return (
<div>
Expand All @@ -100,16 +104,16 @@ class SummitDashboardPage extends React.Component {
<div className="col-md-6"> {currentSummit.time_zone.name} </div>
<div className="col-md-6">
{" "}
{this.getFormattedTime(this.state.localtime / 1000)}{" "}
{this.getFormattedTime(this.state.localtime.unix())}{" "}
</div>
</div>
<div
className={
"row " +
`row ${
this.getTimeClass(
currentSummit.start_date,
currentSummit.end_date
)
)}`
}
>
<div className="col-md-2">
Expand All @@ -131,11 +135,11 @@ class SummitDashboardPage extends React.Component {
</div>
<div
className={
"row " +
`row ${
this.getTimeClass(
currentSummit.start_date,
currentSummit.end_date
)
)}`
}
>
<div className="col-md-2">
Expand All @@ -160,16 +164,16 @@ class SummitDashboardPage extends React.Component {
</div>
{canEditSummit &&
currentSummit.selection_plans.map((sp) => (
<div key={"seleplan_" + sp.id} className="selection-plan row">
<div key={`seleplan_${ sp.id}`} className="selection-plan row">
<div className="col-md-12">{sp.name}</div>
<div className="col-md-12">
<div
className={
"row " +
`row ${
this.getTimeClass(
currentSummit.start_date,
currentSummit.end_date
)
)}`
}
>
<div className="col-md-2">
Expand All @@ -192,11 +196,11 @@ class SummitDashboardPage extends React.Component {
</div>
<div
className={
"row " +
`row ${
this.getTimeClass(
currentSummit.start_date,
currentSummit.end_date
)
)}`
}
>
<div className="col-md-2">
Expand All @@ -219,11 +223,11 @@ class SummitDashboardPage extends React.Component {
</div>
<div
className={
"row " +
`row ${
this.getTimeClass(
currentSummit.start_date,
currentSummit.end_date
)
)}`
}
>
<div className="col-md-2">
Expand Down Expand Up @@ -253,24 +257,24 @@ class SummitDashboardPage extends React.Component {
<hr />
<h4>
{T.translate("dashboard.events")}&nbsp;
{this.state.collapseState["events"] && (
{this.state.collapseState.events && (
<i
title={T.translate("dashboard.expand")}
onClick={() => this.onCollapseChange("events")}
className="fa fa-plus-square clickable"
aria-hidden="true"
></i>
/>
)}
{!this.state.collapseState["events"] && (
{!this.state.collapseState.events && (
<i
title={T.translate("dashboard.collapse")}
onClick={() => this.onCollapseChange("events")}
className="fa fa-minus-square clickable"
aria-hidden="true"
></i>
/>
)}
</h4>
{!this.state.collapseState["events"] && (
{!this.state.collapseState.events && (
<div>
<div className="row">
<div className="col-md-4">
Expand Down Expand Up @@ -309,24 +313,24 @@ class SummitDashboardPage extends React.Component {
<hr />
<h4>
{T.translate("dashboard.voting")}&nbsp;
{this.state.collapseState["voting"] && (
{this.state.collapseState.voting && (
<i
title={T.translate("dashboard.expand")}
onClick={() => this.onCollapseChange("voting")}
className="fa fa-plus-square clickable"
aria-hidden="true"
></i>
/>
)}
{!this.state.collapseState["voting"] && (
{!this.state.collapseState.voting && (
<i
title={T.translate("dashboard.collapse")}
onClick={() => this.onCollapseChange("voting")}
className="fa fa-minus-square clickable"
aria-hidden="true"
></i>
/>
)}
</h4>
{!this.state.collapseState["voting"] && (
{!this.state.collapseState.voting && (
<div>
<div className="row">
<div className="col-md-6">
Expand All @@ -345,24 +349,24 @@ class SummitDashboardPage extends React.Component {
<hr />
<h4>
{T.translate("dashboard.emails")}&nbsp;
{this.state.collapseState["emails"] && (
{this.state.collapseState.emails && (
<i
title={T.translate("dashboard.expand")}
onClick={() => this.onCollapseChange("emails")}
className="fa fa-plus-square clickable"
aria-hidden="true"
></i>
/>
)}
{!this.state.collapseState["emails"] && (
{!this.state.collapseState.emails && (
<i
title={T.translate("dashboard.collapse")}
onClick={() => this.onCollapseChange("emails")}
className="fa fa-minus-square clickable"
aria-hidden="true"
></i>
/>
)}
</h4>
{!this.state.collapseState["emails"] && (
{!this.state.collapseState.emails && (
<div>
<div className="row">
<div className="col-md-4">
Expand Down Expand Up @@ -432,6 +436,37 @@ class SummitDashboardPage extends React.Component {
}
}

SummitDashboardPage.propTypes = {
currentSummit: PropTypes.shape({
id: PropTypes.number,
name: PropTypes.string,
time_zone: PropTypes.shape({
name: PropTypes.string
}),
start_date: PropTypes.number,
end_date: PropTypes.number,
registration_begin_date: PropTypes.number,
registration_end_date: PropTypes.number,
selection_plans: PropTypes.arrayOf(PropTypes.object),
locations: PropTypes.arrayOf(PropTypes.object),
speakers_count: PropTypes.number,
presentations_submitted_count: PropTypes.number,
published_events_count: PropTypes.number,
presentation_voters_count: PropTypes.number,
presentation_votes_count: PropTypes.number,
speaker_announcement_email_accepted_count: PropTypes.number,
speaker_announcement_email_rejected_count: PropTypes.number,
speaker_announcement_email_alternate_count: PropTypes.number,
speaker_announcement_email_accepted_alternate_count: PropTypes.number,
speaker_announcement_email_accepted_rejected_count: PropTypes.number,
speaker_announcement_email_alternate_rejected_count: PropTypes.number
}).isRequired,
member: PropTypes.object,
match: PropTypes.shape({
url: PropTypes.string
}).isRequired
};

const mapStateToProps = ({ currentSummitState, loggedUserState }) => ({
currentSummit: currentSummitState.currentSummit,
member: loggedUserState.member
Expand Down
Loading
Loading