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
72 changes: 71 additions & 1 deletion src/actions/speaker-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,14 @@ export const UNSELECT_ALL_SUMMIT_SPEAKERS = "UNSELECT_ALL_SUMMIT_SPEAKERS";
export const SEND_SPEAKERS_EMAILS = "SEND_SPEAKERS_EMAILS";
export const SET_SPEAKERS_CURRENT_FLOW_EVENT =
"SET_SPEAKERS_CURRENT_FLOW_EVENT";

export const REQUEST_SPEAKERS_ACTIVITIES_COUNT =
"REQUEST_SPEAKERS_ACTIVITIES_COUNT";
export const RECEIVE_SPEAKERS_ACTIVITIES_COUNT =
"RECEIVE_SPEAKERS_ACTIVITIES_COUNT";
export const REQUEST_SELECTED_SPEAKERS_ACTIVITY_COUNT =
"REQUEST_SELECTED_SPEAKERS_ACTIVITY_COUNT";
export const RECEIVE_SELECTED_SPEAKERS_ACTIVITY_COUNT =
"RECEIVE_SELECTED_SPEAKERS_ACTIVITY_COUNT";

const normalizeEntity = (entity) => {
const normalizedEntity = { ...entity };
Expand Down Expand Up @@ -899,6 +902,73 @@ const getSpeakersActivitiesCount =
)(params)(dispatch);
};

export const getSelectedSpeakersActivityCount =
() => async (dispatch, getState) => {
const { currentSummitState, currentSummitSpeakersListState } = getState();
const accessToken = await getAccessTokenSafely();
const { currentSummit } = currentSummitState;
const {
totalActivities,
term,
selectedCount,
selectedItems,
excludedItems,
selectedAll,
selectionPlanFilter,
trackFilter,
trackGroupFilter,
activityTypeFilter,
selectionStatusFilter,
mediaUploadTypeFilter
} = currentSummitSpeakersListState;
const filters = {
selectionPlanFilter,
trackFilter,
trackGroupFilter,
activityTypeFilter,
selectionStatusFilter,
mediaUploadTypeFilter
};
const filter = parseFilters(filters);
const params = { access_token: accessToken };

// if no speakers selected we escape setting count to 0
// if all speakers selected we escape setting count to totalActivities
if (selectedCount === 0 || (selectedAll && excludedItems.length === 0)) {
const activityCount = selectedCount === 0 ? 0 : totalActivities;
dispatch(
createAction(RECEIVE_SELECTED_SPEAKERS_ACTIVITY_COUNT)({
response: { count: activityCount },
override: true
})
);
return;
}

if (term) {
const filterTerm = buildTermFilter(term);
filter.push(filterTerm.join(","));
}

// build id filter
if (selectedAll) {
if (excludedItems.length > 0) {
filter.push(`not_id==${excludedItems.join("||")}`);
}
} else if (selectedItems.length > 0) {
filter.push(`id==${selectedItems.join("||")}`);
}

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

return getRequest(
createAction(REQUEST_SELECTED_SPEAKERS_ACTIVITY_COUNT),
createAction(RECEIVE_SELECTED_SPEAKERS_ACTIVITY_COUNT),
`${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/speakers/all/events/count`,
authErrorHandler
)(params)(dispatch);
Comment on lines +964 to +969
};

export const getSpeakersBySummit =
(
term = null,
Expand Down
70 changes: 70 additions & 0 deletions src/actions/submitter-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export const REQUEST_SUBMITTERS_ACTIVITIES_COUNT =
"REQUEST_SUBMITTERS_ACTIVITIES_COUNT";
export const RECEIVE_SUBMITTERS_ACTIVITIES_COUNT =
"RECEIVE_SUBMITTERS_ACTIVITIES_COUNT";
export const REQUEST_SELECTED_SUBMITTERS_ACTIVITY_COUNT =
"REQUEST_SELECTED_SUBMITTERS_ACTIVITY_COUNT";
export const RECEIVE_SELECTED_SUBMITTERS_ACTIVITY_COUNT =
"RECEIVE_SELECTED_SUBMITTERS_ACTIVITY_COUNT";

export const initSubmittersList = () => async (dispatch) => {
dispatch(createAction(INIT_SUBMITTERS_LIST_PARAMS)());
Expand All @@ -63,6 +67,72 @@ const getSubmittersActivitiesCount =
)(params)(dispatch);
};

export const getSelectedSubmittersActivityCount =

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

getSelectedSubmittersActivityCount is a near-verbatim copy of getSelectedSpeakersActivityCount — same 70 lines, same logic, only the state key (currentSummitSubmittersListState vs currentSummitSpeakersListState) and the endpoint segment (submitters vs speakers) differ. Any fix applied to one (e.g. the filter-change staleness above) has to be manually mirrored in the other.

Consider extracting a shared action factory parameterized by state key and endpoint, and having both exports delegate to it.

@santipalenque

@santipalenque santipalenque Jun 26, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I agree , but this would be an improvement to the whole submitters vs speakers action file, imo out of scope for this task and rather work on it on a separate ticket

() => async (dispatch, getState) => {
const { currentSummitState, currentSummitSubmittersListState } = getState();
const accessToken = await getAccessTokenSafely();
const { currentSummit } = currentSummitState;
const {
totalActivities,
term,
selectedCount,
selectedItems,
excludedItems,
selectedAll,
selectionPlanFilter,
trackFilter,
trackGroupFilter,
activityTypeFilter,
selectionStatusFilter,
mediaUploadTypeFilter
} = currentSummitSubmittersListState;
const filters = {
selectionPlanFilter,
trackFilter,
trackGroupFilter,
activityTypeFilter,
selectionStatusFilter,
mediaUploadTypeFilter
};
const filter = parseFilters(filters);
const params = { access_token: accessToken };

// if no submitters selected we escape setting count to 0
// if all submitters selected we escape setting count to totalActivities
if (selectedCount === 0 || (selectedAll && excludedItems.length === 0)) {
const activityCount = selectedCount === 0 ? 0 : totalActivities;
dispatch(
createAction(RECEIVE_SELECTED_SUBMITTERS_ACTIVITY_COUNT)({
response: { count: activityCount }
})
);
return;
}

if (term) {
const filterTerm = buildTermFilter(term);
filter.push(filterTerm.join(","));
}

// build id filter
if (selectedAll) {
if (excludedItems.length > 0) {
filter.push(`not_id==${excludedItems.join("||")}`);
}
} else if (selectedItems.length > 0) {
filter.push(`id==${selectedItems.join("||")}`);
}

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

return getRequest(
createAction(REQUEST_SELECTED_SUBMITTERS_ACTIVITY_COUNT),
createAction(RECEIVE_SELECTED_SUBMITTERS_ACTIVITY_COUNT),
`${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/submitters/all/events/count`,
authErrorHandler
)(params)(dispatch);
Comment on lines +128 to +133
};

export const getSubmittersBySummit =
(
term = null,
Expand Down
4 changes: 1 addition & 3 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1114,7 +1114,6 @@
"should_send_copy_2_submitter": "Also send to submitter?",
"allows_to_reassign": "Allow to reassign?",
"items_qty": "Selected {qty} Speakers | {activitiesQty} Activities",
"items_qty_no_activities": "Selected {qty} Speakers",
"placeholders": {
"search_speakers": "Search by Full Name, Email, Speaker Id, Member Id, Title Or Abstract",
"test_recipient": "Optional Test Recipient"
Expand All @@ -1129,8 +1128,7 @@
"resend_done": "Emails sent successfully.",
"submitters": "Submitters",
"submitters_no_speakers": "Submitters (no speakers)",
"items_qty": "Selected {qty} Submitters | {activitiesQty} Activities",
"items_qty_no_activities": "Selected {qty} Submitters"
"items_qty": "Selected {qty} Submitters | {activitiesQty} Activities"
},
"speaker_attendance_list": {
"speaker_attendance_list": "Speaker Attendance List",
Expand Down
61 changes: 42 additions & 19 deletions src/pages/summit_speakers/summit-speakers-list-page.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ import {
selectAllSummitSpeakers,
unselectAllSummitSpeakers,
setCurrentFlowEvent,
sendSpeakerEmails
sendSpeakerEmails,
getSelectedSpeakersActivityCount
} from "../../actions/speaker-actions";
import {
initSubmittersList,
Expand All @@ -41,7 +42,8 @@ import {
selectAllSummitSubmitters,
unselectAllSummitSubmitters,
setCurrentSubmitterFlowEvent,
sendSubmitterEmails
sendSubmitterEmails,
getSelectedSubmittersActivityCount
} from "../../actions/submitter-actions";
import {
validateSpecs,
Expand All @@ -65,6 +67,7 @@ class SummitSpeakersListPage extends React.Component {
super(props);

this.getSubjectProps = this.getSubjectProps.bind(this);
this.getSelectedActivityCount = this.getSelectedActivityCount.bind(this);
this.export = this.export.bind(this);
this.getBySummit = this.getBySummit.bind(this);
this.handleSpeakerSubmitterSourceChange =
Expand Down Expand Up @@ -139,13 +142,34 @@ class SummitSpeakersListPage extends React.Component {
}
}

componentDidUpdate(prevProps) {
const { source } = this.state;
const subjectPropKey =
source === sources.speakers ? "speakersProps" : "submittersProps";
const { selectedCount: currentCount } = this.props[subjectPropKey];
const { selectedCount: prevCount } = prevProps[subjectPropKey];

if (currentCount !== prevCount) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

componentDidUpdate only triggers getSelectedActivityCount() when selectedCount changes, but getSelectedSpeakersActivityCount (and the submitters variant) passes the active filters to the API. If the user has speakers selected and then changes a filter (track, selection plan, activity type, etc.), the list reloads and the filter context changes — but selectedCount stays the same, so no refresh fires. The displayed activity count will be stale until the next selection change.

Consider also diffing the active filter fields from prevProps[subjectPropKey] vs the current ones and triggering the count fetch when any of them changes.

@santipalenque

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This is not true @smarcet . REQUEST_SPEAKERS_BY_SUMMIT called when filters change is resetting the selectedCount, so the selectedActivityCount will reset too

this.getSelectedActivityCount();
}
}
Comment on lines +145 to +155

getSubjectProps() {
const { source } = this.state;
return source === sources.speakers
? this.props.speakersProps
: this.props.submittersProps;
}

getSelectedActivityCount() {
const { source } = this.state;
if (source === sources.speakers) {
this.props.getSelectedSpeakersActivityCount();
} else {
this.props.getSelectedSubmittersActivityCount();
}
}

getBySummit(term, page, perPage, order, orderDir, filters) {
const { source } = this.state;
const callable =
Expand Down Expand Up @@ -703,6 +727,8 @@ class SummitSpeakersListPage extends React.Component {
orderDir,
totalItems,
selectedCount,
selectedActivityCount,
gettingSelectedActivityCount,
selectedAll,
selectionPlanFilter,
trackFilter,
Expand All @@ -711,12 +737,9 @@ class SummitSpeakersListPage extends React.Component {
selectionStatusFilter,
mediaUploadTypeFilter,
currentFlowEvent,
totalActivities,
excludedItems
totalActivities
} = this.getSubjectProps();

const activitiesCountAccurate = selectedAll && excludedItems.length === 0;

const columns = [
{
columnKey: "full_name",
Expand Down Expand Up @@ -1052,19 +1075,17 @@ class SummitSpeakersListPage extends React.Component {
<div>
<span>
<b>
{activitiesCountAccurate
? T.translate(
this.state.source === sources.speakers
? "summit_speakers_list.items_qty"
: "summit_submitters_list.items_qty",
{ qty: selectedCount, activitiesQty: totalActivities }
)
: T.translate(
this.state.source === sources.speakers
? "summit_speakers_list.items_qty_no_activities"
: "summit_submitters_list.items_qty_no_activities",
{ qty: selectedCount }
)}
{T.translate(
this.state.source === sources.speakers
? "summit_speakers_list.items_qty"
: "summit_submitters_list.items_qty",
{
qty: selectedCount,
activitiesQty: gettingSelectedActivityCount
? "..."
: selectedActivityCount
}
)}
</b>
</span>
<SelectableTable
Expand Down Expand Up @@ -1222,6 +1243,7 @@ const mapStateToProps = ({
export default connect(mapStateToProps, {
initSpeakersList,
getSpeakersBySummit,
getSelectedSpeakersActivityCount,
exportSummitSpeakers,
selectSummitSpeaker,
unselectSummitSpeaker,
Expand All @@ -1231,6 +1253,7 @@ export default connect(mapStateToProps, {
sendSpeakerEmails,
initSubmittersList,
getSubmittersBySummit,
getSelectedSubmittersActivityCount,
exportSummitSubmitters,
selectSummitSubmitter,
unselectSummitSubmitter,
Expand Down
19 changes: 18 additions & 1 deletion src/reducers/summit_speakers/summit-speakers-list-reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import {
UNSELECT_ALL_SUMMIT_SPEAKERS,
SEND_SPEAKERS_EMAILS,
SET_SPEAKERS_CURRENT_FLOW_EVENT,
RECEIVE_SPEAKERS_ACTIVITIES_COUNT
RECEIVE_SPEAKERS_ACTIVITIES_COUNT,
REQUEST_SELECTED_SPEAKERS_ACTIVITY_COUNT,
RECEIVE_SELECTED_SPEAKERS_ACTIVITY_COUNT
} from "../../actions/speaker-actions";

import {
Expand All @@ -41,6 +43,8 @@ const DEFAULT_STATE = {
totalItems: 0,
totalActivities: 0,
selectedCount: 0,
selectedActivityCount: 0,
gettingSelectedActivityCount: false,
selectedItems: [],
excludedItems: [],
selectedAll: false,
Expand Down Expand Up @@ -210,6 +214,19 @@ const summitSpeakersListReducer = (state = DEFAULT_STATE, action = {}) => {
case RECEIVE_SPEAKERS_ACTIVITIES_COUNT: {
return { ...state, totalActivities: payload.response.count };
}
case REQUEST_SELECTED_SPEAKERS_ACTIVITY_COUNT: {
return { ...state, gettingSelectedActivityCount: true };
}
case RECEIVE_SELECTED_SPEAKERS_ACTIVITY_COUNT: {
if (!state.gettingSelectedActivityCount && !payload.override)
return state;
const { count } = payload.response;
return {
...state,
selectedActivityCount: count,
gettingSelectedActivityCount: false
};
}
default:
return state;
}
Expand Down
Loading
Loading