diff --git a/package.json b/package.json index e9a61ed21..cad2946a5 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "moment": "^2.29.1", "moment-duration-format": "^2.3.2", "moment-timezone": "^0.5.33", - "openstack-uicore-foundation": "5.0.20", + "openstack-uicore-foundation": "5.0.21-beta.1", "p-limit": "^6.1.0", "path-browserify": "^1.0.1", "postcss-loader": "^6.2.1", @@ -114,7 +114,7 @@ "react-dropzone": "^4.2.13", "react-final-form": "^6.5.9", "react-google-maps": "^9.4.5", - "react-redux": "^5.0.7", + "react-redux": "^7.1.0", "react-router": "^4.3.1", "react-router-dom": "^4.3.1", "react-rte": "^0.16.3", diff --git a/src/actions/speaker-actions.js b/src/actions/speaker-actions.js index fa8a8b9f9..f1c785b9c 100644 --- a/src/actions/speaker-actions.js +++ b/src/actions/speaker-actions.js @@ -772,116 +772,6 @@ export const removeFeaturedSpeaker = /* SPEAKERS BY SUMMIT */ /** ************************************************************************************************* */ -const parseFilters = (filters) => { - const filter = []; - - if ( - filters?.selectionPlanFilter && - Array.isArray(filters.selectionPlanFilter) && - filters.selectionPlanFilter.length > 0 - ) { - filter.push( - `presentations_selection_plan_id==${filters.selectionPlanFilter.join( - "||" - )}` - ); - } - - if ( - filters?.trackFilter && - Array.isArray(filters.trackFilter) && - filters.trackFilter.length > 0 - ) { - filter.push(`presentations_track_id==${filters.trackFilter.join("||")}`); - } - - if ( - filters?.trackGroupFilter && - Array.isArray(filters.trackGroupFilter) && - filters.trackGroupFilter.length > 0 - ) { - filter.push( - `presentations_track_group_id==${filters.trackGroupFilter.join("||")}` - ); - } - - if ( - filters?.activityTypeFilter && - Array.isArray(filters.activityTypeFilter) && - filters.activityTypeFilter.length > 0 - ) { - filter.push( - `presentations_type_id==${filters.activityTypeFilter.join("||")}` - ); - } - - if ( - filters?.selectionStatusFilter && - Array.isArray(filters.selectionStatusFilter) && - filters.selectionStatusFilter.length > 0 - ) { - // exclusive filters - if (filters.selectionStatusFilter.includes("only_rejected")) { - filter.push("has_rejected_presentations==true"); - filter.push("has_accepted_presentations==false"); - filter.push("has_alternate_presentations==false"); - } else if (filters.selectionStatusFilter.includes("only_accepted")) { - filter.push("has_rejected_presentations==false"); - filter.push("has_accepted_presentations==true"); - filter.push("has_alternate_presentations==false"); - } else if (filters.selectionStatusFilter.includes("only_alternate")) { - filter.push("has_rejected_presentations==false"); - filter.push("has_accepted_presentations==false"); - filter.push("has_alternate_presentations==true"); - } else if (filters.selectionStatusFilter.includes("accepted_alternate")) { - filter.push("has_rejected_presentations==false"); - filter.push("has_accepted_presentations==true"); - filter.push("has_alternate_presentations==true"); - } else if (filters.selectionStatusFilter.includes("accepted_rejected")) { - filter.push("has_rejected_presentations==true"); - filter.push("has_accepted_presentations==true"); - filter.push("has_alternate_presentations==false"); - } else if (filters.selectionStatusFilter.includes("alternate_rejected")) { - filter.push("has_rejected_presentations==true"); - filter.push("has_accepted_presentations==false"); - filter.push("has_alternate_presentations==true"); - } else { - filter.push( - filters.selectionStatusFilter.reduce( - (accumulator, at) => - `${ - accumulator + (accumulator !== "" ? "," : "") - }has_${at}_presentations==true`, - "" - ) - ); - } - } - - if ( - filters?.mediaUploadTypeFilter && - filters.mediaUploadTypeFilter.operator !== null && - Array.isArray(filters.mediaUploadTypeFilter.value) && - filters.mediaUploadTypeFilter.value.length > 0 - ) { - filter.push( - `${ - filters.mediaUploadTypeFilter.operator - }${filters.mediaUploadTypeFilter.value - .map((v) => v.id) - .join( - filters.mediaUploadTypeFilter.operator === - "has_media_upload_with_type==" - ? "||" - : "&&" - )}` - ); - } - - // return checkOrFilter(filters, filter); - return filter; -}; - export const getSpeakersBySummit = ( term = null, @@ -889,19 +779,18 @@ export const getSpeakersBySummit = perPage = DEFAULT_PER_PAGE, order = "full_name", orderDir = DEFAULT_ORDER_DIR, - filters = {} + filters = [] ) => async (dispatch, getState) => { const { currentSummitState } = getState(); const accessToken = await getAccessTokenSafely(); const { currentSummit } = currentSummitState; - const filter = parseFilters(filters); + const filter = [...filters]; dispatch(startLoading()); if (term) { const filterTerm = buildTermFilter(term); - filter.push(filterTerm.join(",")); } @@ -948,7 +837,7 @@ export const getSpeakersBySummit = }; export const exportSummitSpeakers = - (term = null, order = "id", orderDir = DEFAULT_ORDER_DIR, filters = {}) => + (term = null, order = "id", orderDir = DEFAULT_ORDER_DIR, filters = []) => async (dispatch, getState) => { const csvMIME = "text/csv;charset=utf-8"; const pageSize = 500; @@ -967,7 +856,7 @@ export const exportSummitSpeakers = dispatch(startLoading()); - const filter = parseFilters(filters); + const filter = [...filters]; if (term) { const filterTerm = buildTermFilter(term); @@ -1018,7 +907,7 @@ export const exportSummitSpeakers = export const sendSpeakerEmails = ( term = null, - filters = {}, + filters = [], testRecipient = "", excerptRecipient = "", shouldSendCopy2Submitter = false, @@ -1047,7 +936,7 @@ export const sendSpeakerEmails = if (!selectedAll && selectedItems.length > 0) { // we don't need the filter criteria, we have the ids filter.push(`id==${selectedItems.join("||")}`); - const originalFilters = parseFilters(filters); + const originalFilters = [...filters]; if (term) { const filterTerm = buildTermFilter(term); originalFilters.push(filterTerm.join(",")); @@ -1055,7 +944,7 @@ export const sendSpeakerEmails = payload.original_filter = originalFilters; } else { - filter = parseFilters(filters); + filter = [...filters]; if (term) { const filterTerm = buildTermFilter(term); diff --git a/src/actions/submitter-actions.js b/src/actions/submitter-actions.js index 57da93e08..689232a02 100644 --- a/src/actions/submitter-actions.js +++ b/src/actions/submitter-actions.js @@ -54,7 +54,7 @@ export const getSubmittersBySummit = perPage = DEFAULT_PER_PAGE, order = "full_name", orderDir = DEFAULT_ORDER_DIR, - filters = {}, + filters = [], source = null ) => async (dispatch, getState) => { @@ -62,7 +62,7 @@ export const getSubmittersBySummit = const accessToken = await getAccessTokenSafely(); const { currentSummit } = currentSummitState; - const filter = parseFilters(filters); + const filter = [...filters]; if (source === sources.submitters_no_speakers) { filter.push("is_speaker==false"); @@ -133,7 +133,7 @@ export const exportSummitSubmitters = access_token: accessToken }; - const filter = parseFilters(filters); + const filter = [...filters]; if (source === sources.submitters_no_speakers) { filter.push("is_speaker==false"); @@ -209,7 +209,7 @@ export const sendSubmitterEmails = if (!selectedAll && selectedItems.length > 0) { // we don't need the filter criteria, we have the ids filter.push(`id==${selectedItems.join("||")}`); - const originalFilters = parseFilters(filters); + const originalFilters = [...filters]; if (source && source === sources.submitters_no_speakers) { originalFilters.push("is_speaker==false"); @@ -222,7 +222,7 @@ export const sendSubmitterEmails = payload.original_filter = originalFilters; } else { - filter = parseFilters(filters); + filter = [...filters]; if (source && source === sources.submitters_no_speakers) { filter.push("is_speaker==false"); @@ -292,132 +292,6 @@ export const setCurrentSubmitterFlowEvent = (value) => (dispatch) => { dispatch(createAction(SET_SUBMITTERS_CURRENT_FLOW_EVENT)(value)); }; -const parseFilters = (filters) => { - const filter = []; - - if ( - filters.hasOwnProperty("selectionPlanFilter") && - Array.isArray(filters.selectionPlanFilter) && - filters.selectionPlanFilter.length > 0 - ) { - filter.push( - `presentations_selection_plan_id==${filters.selectionPlanFilter.reduce( - (accumulator, sp) => - `${accumulator + (accumulator !== "" ? "||" : "")}${sp}`, - "" - )}` - ); - } - - if ( - filters.hasOwnProperty("trackFilter") && - Array.isArray(filters.trackFilter) && - filters.trackFilter.length > 0 - ) { - filter.push( - `presentations_track_id==${filters.trackFilter.reduce( - (accumulator, t) => - `${accumulator + (accumulator !== "" ? "||" : "")}${t}`, - "" - )}` - ); - } - - if ( - filters.hasOwnProperty("trackGroupFilter") && - Array.isArray(filters.trackGroupFilter) && - filters.trackGroupFilter.length > 0 - ) { - filter.push( - `presentations_track_group_id==${filters.trackGroupFilter.reduce( - (accumulator, t) => - `${accumulator + (accumulator !== "" ? "||" : "")}${t}`, - "" - )}` - ); - } - - if ( - filters.hasOwnProperty("activityTypeFilter") && - Array.isArray(filters.activityTypeFilter) && - filters.activityTypeFilter.length > 0 - ) { - filter.push( - `presentations_type_id==${filters.activityTypeFilter.reduce( - (accumulator, at) => - `${accumulator + (accumulator !== "" ? "||" : "")}${at}`, - "" - )}` - ); - } - - if ( - filters.hasOwnProperty("selectionStatusFilter") && - Array.isArray(filters.selectionStatusFilter) && - filters.selectionStatusFilter.length > 0 - ) { - // exclusive filters - if (filters.selectionStatusFilter.includes("only_rejected")) { - filter.push("has_rejected_presentations==true"); - filter.push("has_accepted_presentations==false"); - filter.push("has_alternate_presentations==false"); - } else if (filters.selectionStatusFilter.includes("only_accepted")) { - filter.push("has_rejected_presentations==false"); - filter.push("has_accepted_presentations==true"); - filter.push("has_alternate_presentations==false"); - } else if (filters.selectionStatusFilter.includes("only_alternate")) { - filter.push("has_rejected_presentations==false"); - filter.push("has_accepted_presentations==false"); - filter.push("has_alternate_presentations==true"); - } else if (filters.selectionStatusFilter.includes("accepted_alternate")) { - filter.push("has_rejected_presentations==false"); - filter.push("has_accepted_presentations==true"); - filter.push("has_alternate_presentations==true"); - } else if (filters.selectionStatusFilter.includes("accepted_rejected")) { - filter.push("has_rejected_presentations==true"); - filter.push("has_accepted_presentations==true"); - filter.push("has_alternate_presentations==false"); - } else if (filters.selectionStatusFilter.includes("alternate_rejected")) { - filter.push("has_rejected_presentations==true"); - filter.push("has_accepted_presentations==false"); - filter.push("has_alternate_presentations==true"); - } else { - filter.push( - filters.selectionStatusFilter.reduce( - (accumulator, at) => - `${ - accumulator + (accumulator !== "" ? "," : "") - }has_${at}_presentations==true`, - "" - ) - ); - } - } - - if ( - filters.hasOwnProperty("mediaUploadTypeFilter") && - filters.mediaUploadTypeFilter.operator !== null && - Array.isArray(filters.mediaUploadTypeFilter.value) && - filters.mediaUploadTypeFilter.value.length > 0 - ) { - filter.push( - `${ - filters.mediaUploadTypeFilter.operator - }${filters.mediaUploadTypeFilter.value - .map((v) => v.id) - .join( - filters.mediaUploadTypeFilter.operator === - "has_media_upload_with_type==" - ? "||" - : "&&" - )}` - ); - } - - // return checkOrFilter(filters, filter); - return filter; -}; - const buildTermFilter = (term) => { const escapedTerm = escapeFilterValue(term); diff --git a/src/pages/summit_speakers/summit-speakers-list-page.js b/src/pages/summit_speakers/summit-speakers-list-page.js deleted file mode 100644 index 24e47df74..000000000 --- a/src/pages/summit_speakers/summit-speakers-list-page.js +++ /dev/null @@ -1,1225 +0,0 @@ -/** - * Copyright 2017 OpenStack Foundation - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * 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 { connect } from "react-redux"; -import T from "i18n-react/dist/i18n-react"; -import Swal from "sweetalert2"; -import { Modal, Pagination } from "react-bootstrap"; -import FreeTextSearch from "openstack-uicore-foundation/lib/components/free-text-search" -import SelectableTable from "openstack-uicore-foundation/lib/components/table-selectable" -import Dropdown from "openstack-uicore-foundation/lib/components/inputs/dropdown" -import Input from "openstack-uicore-foundation/lib/components/inputs/text-input"; -import SpeakerPromoCodeSpecForm from "../../components/forms/speakers-promo-code-spec-form"; -import { - initSpeakersList, - getSpeakersBySummit, - exportSummitSpeakers, - selectSummitSpeaker, - unselectSummitSpeaker, - selectAllSummitSpeakers, - unselectAllSummitSpeakers, - setCurrentFlowEvent, - sendSpeakerEmails -} from "../../actions/speaker-actions"; -import { - initSubmittersList, - getSubmittersBySummit, - exportSummitSubmitters, - selectSummitSubmitter, - unselectSummitSubmitter, - selectAllSummitSubmitters, - unselectAllSummitSubmitters, - setCurrentSubmitterFlowEvent, - sendSubmitterEmails -} from "../../actions/submitter-actions"; -import { - validateSpecs, - resetPromoCodeSpecForm -} from "../../actions/promocode-specification-actions"; -import { - EXISTING_SPEAKERS_PROMO_CODE, - EXISTING_SPEAKERS_DISCOUNT_CODE, - AUTO_GENERATED_SPEAKERS_PROMO_CODE, - AUTO_GENERATED_SPEAKERS_DISCOUNT_CODE -} from "../../actions/promocode-actions"; - -import { ALL_FILTER, SpeakersSources as sources } from "../../utils/constants"; -import { validateEmail } from "../../utils/methods"; -import MediaTypeFilter from "../../components/filters/media-type-filter"; - -import "../../styles/speakers-list-page.less"; - -class SummitSpeakersListPage extends React.Component { - constructor(props) { - super(props); - - this.getSubjectProps = this.getSubjectProps.bind(this); - this.export = this.export.bind(this); - this.getBySummit = this.getBySummit.bind(this); - this.handleSpeakerSubmitterSourceChange = - this.handleSpeakerSubmitterSourceChange.bind(this); - this.handleEdit = this.handleEdit.bind(this); - this.handlePageChange = this.handlePageChange.bind(this); - this.handleSort = this.handleSort.bind(this); - this.handleSearch = this.handleSearch.bind(this); - this.handleExport = this.handleExport.bind(this); - this.handleSelected = this.handleSelected.bind(this); - this.handleSelectedAll = this.handleSelectedAll.bind(this); - this.handleChangeSelectionPlanFilter = - this.handleChangeSelectionPlanFilter.bind(this); - this.handleChangeTrackFilter = this.handleChangeTrackFilter.bind(this); - this.handleChangeTrackGroupFilter = - this.handleChangeTrackGroupFilter.bind(this); - this.handleChangeActivityTypeFilter = - this.handleChangeActivityTypeFilter.bind(this); - this.handleChangeSelectionStatusFilter = - this.handleChangeSelectionStatusFilter.bind(this); - this.handleChangeFlowEvent = this.handleChangeFlowEvent.bind(this); - this.showEmailSendModal = this.showEmailSendModal.bind(this); - this.handleSendEmails = this.handleSendEmails.bind(this); - this.handleChangePromoCodeStrategy = - this.handleChangePromoCodeStrategy.bind(this); - this.handleOrAndFilter = this.handleOrAndFilter.bind(this); - this.handleChangeMediaUploadTypeFilter = - this.handleChangeMediaUploadTypeFilter.bind(this); - - this.state = { - testRecipient: "", - showSendEmailModal: false, - excerptRecipient: "", - source: sources.speakers, - promoCodeStrategy: 0, - speakerFilters: { - orAndFilter: ALL_FILTER - } - }; - } - - componentDidMount() { - const { currentSummit, initSubmittersList, initSpeakersList } = this.props; - initSubmittersList(); - initSpeakersList(); - if (currentSummit) { - const { - term, - page, - order, - orderDir, - perPage, - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - mediaUploadTypeFilter - } = this.getSubjectProps(); - const { - speakerFilters: { orAndFilter } - } = this.state; - this.getBySummit(term, page, perPage, order, orderDir, { - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - orAndFilter, - mediaUploadTypeFilter - }); - } - } - - getSubjectProps() { - const { source } = this.state; - return source === sources.speakers - ? this.props.speakersProps - : this.props.submittersProps; - } - - getBySummit(term, page, perPage, order, orderDir, filters) { - const { source } = this.state; - const callable = - source === sources.speakers - ? this.props.getSpeakersBySummit - : this.props.getSubmittersBySummit; - callable(term, page, perPage, order, orderDir, filters, source); - } - - export(term, order, orderDir, filters) { - const { source } = this.state; - const callable = - source === sources.speakers - ? this.props.exportSummitSpeakers - : this.props.exportSummitSubmitters; - callable(term, order, orderDir, filters, source); - } - - handleSpeakerSubmitterSourceChange(ev) { - const { value } = ev.target; - const { - term, - order, - orderDir, - perPage, - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - mediaUploadTypeFilter - } = this.getSubjectProps(); - const { - speakerFilters: { orAndFilter } - } = this.state; - const { initSubmittersList, initSpeakersList } = this.props; - this.setState({ ...this.state, source: value }, function () { - initSubmittersList(); - initSpeakersList(); - this.getBySummit(term, 1, perPage, order, orderDir, { - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - orAndFilter, - mediaUploadTypeFilter - }); - }); - } - - handleEdit(itemId) { - if (this.state.source === sources.speakers) { - const { history } = this.props; - history.push(`/app/speakers/${itemId}`); - } - } - - handlePageChange(page) { - const { - term, - order, - orderDir, - perPage, - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - mediaUploadTypeFilter - } = this.getSubjectProps(); - const { - speakerFilters: { orAndFilter } - } = this.state; - this.getBySummit(term, page, perPage, order, orderDir, { - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - orAndFilter, - mediaUploadTypeFilter - }); - } - - handleSort(index, key, dir) { - const { - term, - page, - perPage, - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - mediaUploadTypeFilter - } = this.getSubjectProps(); - const { - speakerFilters: { orAndFilter } - } = this.state; - this.getBySummit(term, page, perPage, key, dir, { - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - orAndFilter, - mediaUploadTypeFilter - }); - } - - handleSearch(term) { - const { - order, - orderDir, - page, - perPage, - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - mediaUploadTypeFilter - } = this.getSubjectProps(); - const { - speakerFilters: { orAndFilter } - } = this.state; - this.getBySummit(term, page, perPage, order, orderDir, { - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - orAndFilter, - mediaUploadTypeFilter - }); - } - - handleChangeSelectionPlanFilter(ev) { - const { value: newSelectionPlanFilter } = ev.target; - const { - term, - order, - page, - orderDir, - perPage, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - mediaUploadTypeFilter - } = this.getSubjectProps(); - const { - speakerFilters: { orAndFilter } - } = this.state; - this.getBySummit(term, page, perPage, order, orderDir, { - selectionPlanFilter: newSelectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - orAndFilter, - mediaUploadTypeFilter - }); - } - - handleChangeTrackFilter(ev) { - const { value: newTrackFilter } = ev.target; - const { - term, - order, - page, - orderDir, - perPage, - selectionPlanFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - mediaUploadTypeFilter - } = this.getSubjectProps(); - const { - speakerFilters: { orAndFilter } - } = this.state; - this.getBySummit(term, page, perPage, order, orderDir, { - selectionPlanFilter, - trackFilter: newTrackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - orAndFilter, - mediaUploadTypeFilter - }); - } - - handleChangeTrackGroupFilter(ev) { - const { value: newTrackGroupFilter } = ev.target; - const { - term, - order, - page, - orderDir, - perPage, - selectionPlanFilter, - trackFilter, - activityTypeFilter, - selectionStatusFilter, - mediaUploadTypeFilter - } = this.getSubjectProps(); - const { - speakerFilters: { orAndFilter } - } = this.state; - this.getBySummit(term, page, perPage, order, orderDir, { - selectionPlanFilter, - trackFilter, - trackGroupFilter: newTrackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - orAndFilter, - mediaUploadTypeFilter - }); - } - - handleChangeActivityTypeFilter(ev) { - const { value: newActivityTypeFilter } = ev.target; - const { - term, - order, - page, - orderDir, - perPage, - selectionPlanFilter, - trackFilter, - trackGroupFilter, - selectionStatusFilter, - mediaUploadTypeFilter - } = this.getSubjectProps(); - const { - speakerFilters: { orAndFilter } - } = this.state; - this.getBySummit(term, page, perPage, order, orderDir, { - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter: newActivityTypeFilter, - selectionStatusFilter, - orAndFilter, - mediaUploadTypeFilter - }); - } - - handleChangeMediaUploadTypeFilter(ev) { - const { value, operator } = ev.target; - const { - term, - order, - page, - orderDir, - perPage, - activityTypeFilter, - selectionPlanFilter, - trackFilter, - trackGroupFilter, - selectionStatusFilter, - mediaUploadTypeFilter - } = this.getSubjectProps(); - const { - speakerFilters: { orAndFilter } - } = this.state; - if (operator && value.length > 0) { - this.getBySummit(term, page, perPage, order, orderDir, { - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - orAndFilter, - mediaUploadTypeFilter: { operator, value } - }); - // get speakers if the media upload types filter is clear - } else if (mediaUploadTypeFilter.value.length > 0 && value.length === 0) { - this.getBySummit(term, page, perPage, order, orderDir, { - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - orAndFilter, - mediaUploadTypeFilter: { operator: null, value: [] } - }); - } - } - - handleChangeSelectionStatusFilter(ev) { - let { value: newSelectionStatusFilter } = ev.target; - // exclusive filters tests .... - if (newSelectionStatusFilter.includes("only_rejected")) { - newSelectionStatusFilter = ["only_rejected"]; - } else if (newSelectionStatusFilter.includes("only_alternate")) { - newSelectionStatusFilter = ["only_alternate"]; - } else if (newSelectionStatusFilter.includes("only_accepted")) { - newSelectionStatusFilter = ["only_accepted"]; - } else if (newSelectionStatusFilter.includes("accepted_alternate")) { - newSelectionStatusFilter = ["accepted_alternate"]; - } else if (newSelectionStatusFilter.includes("accepted_rejected")) { - newSelectionStatusFilter = ["accepted_rejected"]; - } else if (newSelectionStatusFilter.includes("alternate_rejected")) { - newSelectionStatusFilter = ["alternate_rejected"]; - } - - const { - term, - order, - page, - orderDir, - perPage, - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - mediaUploadTypeFilter - } = this.getSubjectProps(); - const { - speakerFilters: { orAndFilter } - } = this.state; - this.getBySummit(term, page, perPage, order, orderDir, { - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - orAndFilter, - selectionStatusFilter: newSelectionStatusFilter, - mediaUploadTypeFilter - }); - } - - handleChangeFlowEvent(ev) { - const { value } = ev.target; - const { source } = this.state; - if (source === sources.speakers) { - this.props.setCurrentFlowEvent(value); - } else { - this.props.setCurrentSubmitterFlowEvent(value); - } - } - - handleSendEmails(ev) { - ev.stopPropagation(); - ev.preventDefault(); - const { currentPromocodeSpecification } = this.props; - const { promoCodeStrategy, testRecipient, source } = this.state; - const isSpeakerMode = source === sources.speakers; - const excerptRecipient = this.ingestEmailRef.value; - const shouldSendCopy2Submitter = - isSpeakerMode && this.shouldSendCopy2SubmitterRef.checked; - const { - term, - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - mediaUploadTypeFilter - } = this.getSubjectProps(); - const { - speakerFilters: { orAndFilter } - } = this.state; - - this.props.validateSpecs( - promoCodeStrategy, - currentPromocodeSpecification.entity, - () => { - this.setState({ - showSendEmailModal: false, - excerptRecipient: "", - testRecipient: "", - promoCodeStrategy: 0 - }); - // send emails - - const callable = isSpeakerMode - ? this.props.sendSpeakerEmails - : this.props.sendSubmitterEmails; - - callable( - term, - { - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - orAndFilter, - mediaUploadTypeFilter - }, - testRecipient, - excerptRecipient, - shouldSendCopy2Submitter, - source, - promoCodeStrategy, - currentPromocodeSpecification.entity - ); - } - ); - } - - handleChangePromoCodeStrategy(ev) { - const { value } = ev.target; - this.setState({ ...this.state, promoCodeStrategy: value }); - this.props.resetPromoCodeSpecForm(); - } - - showEmailSendModal(ev) { - ev.stopPropagation(); - ev.preventDefault(); - - const { source, testRecipient } = this.state; - const { currentFlowEvent, selectedCount } = this.getSubjectProps(); - - if (!currentFlowEvent) { - Swal.fire( - "Validation error", - T.translate("summit_speakers_list.select_template"), - "warning" - ); - return false; - } - - if (selectedCount === 0) { - const content = - source === sources.speakers - ? T.translate("summit_speakers_list.select_items") - : T.translate("summit_submitters_list.select_items"); - Swal.fire("Validation error", content, "warning"); - return false; - } - - if (testRecipient !== "" && !validateEmail(testRecipient)) { - Swal.fire( - "Validation error", - T.translate("summit_speakers_list.invalid_recipient_email"), - "warning" - ); - return false; - } - - this.setState({ - ...this.state, - showSendEmailModal: true, - excerptRecipient: "" - }); - } - - handleExport(ev) { - const { - term, - order, - orderDir, - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - mediaUploadTypeFilter - } = this.getSubjectProps(); - const { - speakerFilters: { orAndFilter } - } = this.state; - ev.preventDefault(); - this.export(term, order, orderDir, { - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - orAndFilter, - mediaUploadTypeFilter - }); - } - - handleSelected(item_id, isSelected) { - const { source } = this.state; - if (isSelected) { - if (source === sources.speakers) { - this.props.selectSummitSpeaker(item_id); - } else { - this.props.selectSummitSubmitter(item_id); - } - return; - } - if (source === sources.speakers) { - this.props.unselectSummitSpeaker(item_id); - } else { - this.props.unselectSummitSubmitter(item_id); - } - } - - handleSelectedAll(ev) { - const selectedAll = ev.target.checked; - const { source } = this.state; - if (source === sources.speakers) { - this.props.selectAllSummitSpeakers(); - } else { - this.props.selectAllSummitSubmitters(); - } - if (!selectedAll) { - // clear all selected - if (source === sources.speakers) { - this.props.unselectAllSummitSpeakers(); - } else { - this.props.unselectAllSummitSubmitters(); - } - } - } - - handleOrAndFilter(ev) { - const { - term, - order, - page, - orderDir, - perPage, - trackFilter, - trackGroupFilter, - selectionPlanFilter, - activityTypeFilter, - selectionStatusFilter, - mediaUploadTypeFilter - } = this.getSubjectProps(); - this.setState({ - ...this.state, - speakerFilters: { ...this.state.speakerFilters, orAndFilter: ev } - }); - this.getBySummit(term, page, perPage, order, orderDir, { - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - mediaUploadTypeFilter, - selectionStatusFilter, - orAndFilter: ev - }); - } - - render() { - const { currentSummit, currentPromocodeSpecification } = this.props; - - const { testRecipient, source, promoCodeStrategy } = this.state; - - const { - items, - lastPage, - currentPage, - term, - order, - orderDir, - totalItems, - selectedCount, - selectedAll, - selectionPlanFilter, - trackFilter, - trackGroupFilter, - activityTypeFilter, - selectionStatusFilter, - mediaUploadTypeFilter, - currentFlowEvent - } = this.getSubjectProps(); - - const columns = [ - { - columnKey: "full_name", - value: T.translate("general.name"), - sortable: true - }, - { - columnKey: "email", - value: T.translate("general.email"), - sortable: true - }, - { - columnKey: "accepted_presentations_count", - value: T.translate("summit_speakers_list.accepted") - }, - { - columnKey: "alternate_presentations_count", - value: T.translate("summit_speakers_list.alternate") - }, - { - columnKey: "rejected_presentations_count", - value: T.translate("summit_speakers_list.rejected") - } - ]; - - const selectionPlansDDL = currentSummit.selection_plans.map( - (selectionPlan) => ({ - label: selectionPlan.name, - value: selectionPlan.id - }) - ); - const tracksDDL = currentSummit.tracks.map((track) => ({ - label: track.name, - value: track.id - })); - const trackGroupsDDL = currentSummit.track_groups.map((trackGroup) => ({ - label: trackGroup.name, - value: trackGroup.id - })); - const activityTypesDDL = currentSummit.event_types.map((type) => ({ - label: type.name, - value: type.id - })); - - const selectionStatusDDL = [ - { label: "Accepted", value: "accepted" }, - { label: "Alternate", value: "alternate" }, - { label: "Rejected", value: "rejected" }, - { label: "Only Rejected", value: "only_rejected" }, - { label: "Only Accepted", value: "only_accepted" }, - { label: "Only Alternate", value: "only_alternate" }, - { label: "Accepted/Alternate", value: "accepted_alternate" }, - { label: "Accepted/Rejected", value: "accepted_rejected" }, - { label: "Alternate/Rejected", value: "alternate_rejected" } - ]; - - const speakerSubmitterSourceSelectorDDL = [ - { - label: T.translate("summit_speakers_list.speakers"), - value: sources.speakers - }, - { - label: T.translate("summit_submitters_list.submitters"), - value: sources.submitters - }, - { - label: T.translate("summit_submitters_list.submitters_no_speakers"), - value: sources.submitters_no_speakers - } - ]; - - const emailFlowDDL = - this.state.source === sources.speakers - ? [ - { label: "-- SELECT EMAIL EVENT --", value: "" }, - { - label: - "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ACCEPTED_ALTERNATE", - value: - "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ACCEPTED_ALTERNATE" - }, - { - label: - "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ACCEPTED_REJECTED", - value: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ACCEPTED_REJECTED" - }, - { - label: - "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ALTERNATE_REJECTED", - value: - "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ALTERNATE_REJECTED" - }, - { - label: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ACCEPTED_ONLY", - value: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ACCEPTED_ONLY" - }, - { - label: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ALTERNATE_ONLY", - value: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ALTERNATE_ONLY" - }, - { - label: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_REJECTED_ONLY", - value: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_REJECTED_ONLY" - } - ] - : [ - { label: "-- SELECT EMAIL EVENT --", value: "" }, - { - label: - "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ACCEPTED_ALTERNATE", - value: - "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ACCEPTED_ALTERNATE" - }, - { - label: - "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ACCEPTED_REJECTED", - value: - "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ACCEPTED_REJECTED" - }, - { - label: - "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ALTERNATE_REJECTED", - value: - "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ALTERNATE_REJECTED" - }, - { - label: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ACCEPTED_ONLY", - value: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ACCEPTED_ONLY" - }, - { - label: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ALTERNATE_ONLY", - value: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ALTERNATE_ONLY" - }, - { - label: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_REJECTED_ONLY", - value: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_REJECTED_ONLY" - } - ]; - - const promoCodeStrategiesDDL = [ - { - label: T.translate("summit_speakers_list.select_promo_code_strategy"), - value: 0 - }, - { - label: T.translate("summit_speakers_list.select_speaker_promo_code"), - value: EXISTING_SPEAKERS_PROMO_CODE - }, - { - label: T.translate("summit_speakers_list.select_speaker_discount_code"), - value: EXISTING_SPEAKERS_DISCOUNT_CODE - }, - { - label: T.translate( - "summit_speakers_list.select_auto_generate_speaker_promo_code" - ), - value: AUTO_GENERATED_SPEAKERS_PROMO_CODE - }, - { - label: T.translate( - "summit_speakers_list.select_auto_generate_speaker_discount_code" - ), - value: AUTO_GENERATED_SPEAKERS_DISCOUNT_CODE - } - ]; - - const table_options = { - sortCol: order, - sortDir: orderDir, - actions: { - edit: { - onClick: this.handleEdit, - onSelected: this.handleSelected, - onSelectedAll: this.handleSelectedAll - } - }, - selectedAll - }; - - if (!currentSummit.id) return
; - - return ( -
-

- {" "} - {this.state.source === sources.speakers - ? T.translate("summit_speakers_list.summit_speakers_list") - : T.translate("summit_submitters_list.summit_submitters_list")}{" "} - ({totalItems}) -

-
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
- -
-
- - this.setState({ testRecipient: ev.target.value }) - } - placeholder={T.translate( - "summit_speakers_list.placeholders.test_recipient" - )} - /> -
-
- -
-
- - {items.length === 0 && ( -
- {this.state.source === sources.speakers - ? T.translate("summit_speakers_list.no_speakers") - : T.translate("summit_submitters_list.no_submitters")} -
- )} - - {items.length > 0 && ( -
- - - {T.translate("summit_speakers_list.items_qty", { - qty: selectedCount - })} - - - - - - - this.setState({ ...this.state, showSendEmailModal: false }) - } - backdrop={false} - > - - - {this.state.source === sources.speakers - ? T.translate("summit_speakers_list.send_emails_title") - : T.translate("summit_submitters_list.send_emails_title")} - - - -
-
- {T.translate("summit_speakers_list.send_email_warning", { - template: currentFlowEvent, - qty: selectedCount - })} -
- {this.state.testRecipient !== "" && ( -
- {T.translate( - "summit_speakers_list.email_test_recipient", - { - email: this.state.testRecipient - } - )} -
- )} -
- -
- -
-
- -
-
- -
- { - this.ingestEmailRef = node; - }} - /> -
- {this.state.source === sources.speakers && ( -
-
- { - this.shouldSendCopy2SubmitterRef = node; - }} - /> - -
-
- )} -
-
- - - - -
-
- )} -
- ); - } -} - -const mapStateToProps = ({ - currentSummitState, - currentSummitSpeakersListState, - currentSummitSubmittersListState, - currentPromocodeSpecificationState -}) => ({ - currentSummit: currentSummitState.currentSummit, - speakersProps: currentSummitSpeakersListState, - submittersProps: currentSummitSubmittersListState, - currentPromocodeSpecification: currentPromocodeSpecificationState -}); - -export default connect(mapStateToProps, { - initSpeakersList, - getSpeakersBySummit, - exportSummitSpeakers, - selectSummitSpeaker, - unselectSummitSpeaker, - selectAllSummitSpeakers, - unselectAllSummitSpeakers, - setCurrentFlowEvent, - sendSpeakerEmails, - initSubmittersList, - getSubmittersBySummit, - exportSummitSubmitters, - selectSummitSubmitter, - unselectSummitSubmitter, - selectAllSummitSubmitters, - unselectAllSummitSubmitters, - setCurrentSubmitterFlowEvent, - sendSubmitterEmails, - validateSpecs, - resetPromoCodeSpecForm -})(SummitSpeakersListPage); diff --git a/src/pages/summit_speakers/summit-speakers-list-page/components/send-email-modal.js b/src/pages/summit_speakers/summit-speakers-list-page/components/send-email-modal.js new file mode 100644 index 000000000..d07637d82 --- /dev/null +++ b/src/pages/summit_speakers/summit-speakers-list-page/components/send-email-modal.js @@ -0,0 +1,395 @@ +import React, { useState } from "react"; +import T from "i18n-react"; +import { connect } from "react-redux"; +import Swal from "sweetalert2"; +import Dropdown from "openstack-uicore-foundation/lib/components/inputs/dropdown"; +import Input from "openstack-uicore-foundation/lib/components/inputs/text-input"; +import { Modal } from "react-bootstrap"; +import { validateEmail } from "../../../../utils/methods"; +import { SpeakersSources as sources } from "../../../../utils/constants"; +import SpeakerPromoCodeSpecForm from "../../../../components/forms/speakers-promo-code-spec-form"; +import { + AUTO_GENERATED_SPEAKERS_DISCOUNT_CODE, + AUTO_GENERATED_SPEAKERS_PROMO_CODE, + EXISTING_SPEAKERS_DISCOUNT_CODE, + EXISTING_SPEAKERS_PROMO_CODE +} from "../../../../actions/promocode-actions"; +import { + sendSpeakerEmails, + setCurrentFlowEvent +} from "../../../../actions/speaker-actions"; +import { + sendSubmitterEmails, + setCurrentSubmitterFlowEvent +} from "../../../../actions/submitter-actions"; +import { + resetPromoCodeSpecForm, + validateSpecs +} from "../../../../actions/promocode-specification-actions"; + +const emailFlowSpeakersDDL = [ + { label: "-- SELECT EMAIL EVENT --", value: "" }, + { + label: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ACCEPTED_ALTERNATE", + value: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ACCEPTED_ALTERNATE" + }, + { + label: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ACCEPTED_REJECTED", + value: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ACCEPTED_REJECTED" + }, + { + label: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ALTERNATE_REJECTED", + value: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ALTERNATE_REJECTED" + }, + { + label: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ACCEPTED_ONLY", + value: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ACCEPTED_ONLY" + }, + { + label: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ALTERNATE_ONLY", + value: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_ALTERNATE_ONLY" + }, + { + label: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_REJECTED_ONLY", + value: "SUMMIT_SUBMISSIONS_PRESENTATION_SPEAKER_REJECTED_ONLY" + } +]; +const emailFlowSubmittersDDL = [ + { label: "-- SELECT EMAIL EVENT --", value: "" }, + { + label: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ACCEPTED_ALTERNATE", + value: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ACCEPTED_ALTERNATE" + }, + { + label: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ACCEPTED_REJECTED", + value: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ACCEPTED_REJECTED" + }, + { + label: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ALTERNATE_REJECTED", + value: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ALTERNATE_REJECTED" + }, + { + label: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ACCEPTED_ONLY", + value: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ACCEPTED_ONLY" + }, + { + label: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ALTERNATE_ONLY", + value: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_ALTERNATE_ONLY" + }, + { + label: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_REJECTED_ONLY", + value: "SUMMIT_SUBMISSIONS_PRESENTATION_SUBMITTER_REJECTED_ONLY" + } +]; + +const promoCodeStrategiesDDL = [ + { + label: T.translate("summit_speakers_list.select_promo_code_strategy"), + value: 0 + }, + { + label: T.translate("summit_speakers_list.select_speaker_promo_code"), + value: EXISTING_SPEAKERS_PROMO_CODE + }, + { + label: T.translate("summit_speakers_list.select_speaker_discount_code"), + value: EXISTING_SPEAKERS_DISCOUNT_CODE + }, + { + label: T.translate( + "summit_speakers_list.select_auto_generate_speaker_promo_code" + ), + value: AUTO_GENERATED_SPEAKERS_PROMO_CODE + }, + { + label: T.translate( + "summit_speakers_list.select_auto_generate_speaker_discount_code" + ), + value: AUTO_GENERATED_SPEAKERS_DISCOUNT_CODE + } +]; + +const SendEmailModal = ({ + source, + filters, + speakersProps, + submittersProps, + currentSummit, + currentPromocodeSpecification, + setCurrentFlowEvent, + setCurrentSubmitterFlowEvent, + validateSpecs, + resetPromoCodeSpecForm, + sendSpeakerEmails, + sendSubmitterEmails +}) => { + const [openModal, setOpenModal] = useState(false); + const [promoCodeStrategy, setPromoCodeStrategy] = useState(null); + const [testRecipient, setTestRecipient] = useState(""); + const [modalValues, setModalValues] = useState({ + ingest_email: "", + should_send_copy_2_submitter: false + }); + const isSpeakerMode = source === sources.speakers; + const subjectProps = isSpeakerMode ? speakersProps : submittersProps; + const { currentFlowEvent, selectedCount } = subjectProps; + const emailFlowDDL = isSpeakerMode + ? emailFlowSpeakersDDL + : emailFlowSubmittersDDL; + + const handleOpenModal = (ev) => { + ev.stopPropagation(); + ev.preventDefault(); + + if (!currentFlowEvent) { + Swal.fire( + "Validation error", + T.translate("summit_speakers_list.select_template"), + "warning" + ); + return false; + } + + if (selectedCount === 0) { + const content = + source === sources.speakers + ? T.translate("summit_speakers_list.select_items") + : T.translate("summit_submitters_list.select_items"); + Swal.fire("Validation error", content, "warning"); + return false; + } + + if (testRecipient !== "" && !validateEmail(testRecipient)) { + Swal.fire( + "Validation error", + T.translate("summit_speakers_list.invalid_recipient_email"), + "warning" + ); + return false; + } + + setOpenModal(true); + setModalValues({ ingest_email: "", should_send_copy_2_submitter: false }); + }; + + const handleChangeFlowEvent = (ev) => { + const { value } = ev.target; + if (isSpeakerMode) { + setCurrentFlowEvent(value); + } else { + setCurrentSubmitterFlowEvent(value); + } + }; + + const handleChangePromoCodeStrategy = (ev) => { + const { value } = ev.target; + setPromoCodeStrategy(value); + resetPromoCodeSpecForm(); + }; + + const handleChangeTestRecipient = (ev) => { + const { value } = ev.target; + setTestRecipient(value); + }; + + const handleModalChange = (ev) => { + const { id, value, checked, type } = ev.target; + setModalValues({ + ...modalValues, + [id]: type === "checkbox" ? checked : value + }); + }; + + const onValidationSuccess = () => { + const { + ingest_email: excerptRecipient, + should_send_copy_2_submitter: sendCopy + } = modalValues; + const shouldSendCopy2Submitter = isSpeakerMode && sendCopy; + const { term } = subjectProps; + + setOpenModal(false); + setTestRecipient(""); + setPromoCodeStrategy(null); + setModalValues({ ingest_email: "", should_send_copy_2_submitter: false }); + + // send emails + const sendEmails = isSpeakerMode ? sendSpeakerEmails : sendSubmitterEmails; + + sendEmails( + term, + filters, + testRecipient, + excerptRecipient, + shouldSendCopy2Submitter, + source, + promoCodeStrategy, + currentPromocodeSpecification.entity + ); + }; + + const handleSendEmails = (ev) => { + ev.stopPropagation(); + ev.preventDefault(); + + validateSpecs( + promoCodeStrategy, + currentPromocodeSpecification.entity, + onValidationSuccess + ); + }; + + return ( + <> +
+
+ +
+
+ +
+
+ +
+
+ setOpenModal(false)} + backdrop={false} + > + + + {isSpeakerMode + ? T.translate("summit_speakers_list.send_emails_title") + : T.translate("summit_submitters_list.send_emails_title")} + + + +
+
+ {T.translate("summit_speakers_list.send_email_warning", { + template: currentFlowEvent, + qty: selectedCount + })} +
+ {testRecipient !== "" && ( +
+ {T.translate("summit_speakers_list.email_test_recipient", { + email: testRecipient + })} +
+ )} +
+ +
+ +
+
+ +
+
+ +
+ +
+ {isSpeakerMode && ( +
+
+ + +
+
+ )} +
+
+ + + + +
+ + ); +}; + +const mapStateToProps = ({ + currentSummitState, + currentSummitSpeakersListState, + currentSummitSubmittersListState, + currentPromocodeSpecificationState +}) => ({ + currentSummit: currentSummitState.currentSummit, + speakersProps: currentSummitSpeakersListState, + submittersProps: currentSummitSubmittersListState, + currentPromocodeSpecification: currentPromocodeSpecificationState +}); + +export default connect(mapStateToProps, { + setCurrentFlowEvent, + sendSpeakerEmails, + setCurrentSubmitterFlowEvent, + sendSubmitterEmails, + validateSpecs, + resetPromoCodeSpecForm +})(SendEmailModal); diff --git a/src/pages/summit_speakers/summit-speakers-list-page/index.js b/src/pages/summit_speakers/summit-speakers-list-page/index.js new file mode 100644 index 000000000..d918c235c --- /dev/null +++ b/src/pages/summit_speakers/summit-speakers-list-page/index.js @@ -0,0 +1,528 @@ +/** + * Copyright 2017 OpenStack Foundation + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * 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, { useEffect, useState } from "react"; +import { connect } from "react-redux"; +import T from "i18n-react/dist/i18n-react"; +import { Pagination } from "react-bootstrap"; +import SelectableTable from "openstack-uicore-foundation/lib/components/table-selectable"; +import Dropdown from "openstack-uicore-foundation/lib/components/inputs/dropdown"; +import FreeTextSearch from "openstack-uicore-foundation/lib/components/free-text-search"; +import { + GridFilter, + useGridFilter, + OPERATORS +} from "openstack-uicore-foundation/lib/components/mui/grid-filter"; +import { + exportSummitSpeakers, + getSpeakersBySummit, + initSpeakersList, + selectAllSummitSpeakers, + selectSummitSpeaker, + unselectAllSummitSpeakers, + unselectSummitSpeaker +} from "../../../actions/speaker-actions"; +import { + exportSummitSubmitters, + getSubmittersBySummit, + initSubmittersList, + selectAllSummitSubmitters, + selectSummitSubmitter, + unselectAllSummitSubmitters, + unselectSummitSubmitter +} from "../../../actions/submitter-actions"; +import { getMediaUploads } from "../../../actions/media-upload-actions"; +import { + MAX_PER_PAGE, + SpeakersSources as sources +} from "../../../utils/constants"; +import "../../../styles/speakers-list-page.less"; +import SendEmailModal from "./components/send-email-modal"; + +const FILTER_ID = "summit_speakers_list"; + +const selectionStatusOptions = [ + { label: "Accepted", value: "accepted" }, + { label: "Alternate", value: "alternate" }, + { label: "Rejected", value: "rejected" }, + { label: "Only Rejected", value: "only_rejected" }, + { label: "Only Accepted", value: "only_accepted" }, + { label: "Only Alternate", value: "only_alternate" }, + { label: "Accepted/Alternate", value: "accepted_alternate" }, + { label: "Accepted/Rejected", value: "accepted_rejected" }, + { label: "Alternate/Rejected", value: "alternate_rejected" } +]; + +const getCriterias = (summit, mediaUploadTypes) => [ + { + key: "presentations_selection_plan_id", + label: "Selection Plan", + operators: [OPERATORS.IS], + values: { + type: "select", + props: { + options: summit.selection_plans.map((sp) => ({ + label: sp.name, + value: sp.id + })), + placeholder: "Filter by Selection Plan", + multiple: true + } + } + }, + { + key: "presentations_track_id", + label: "Track", + operators: [OPERATORS.IS], + values: { + type: "select", + props: { + options: summit.tracks.map((t) => ({ label: t.name, value: t.id })), + placeholder: "Filter by Track", + multiple: true + } + } + }, + { + key: "presentations_type_id", + label: "Activity Type", + operators: [OPERATORS.IS], + values: { + type: "select", + props: { + options: summit.event_types.map((type) => ({ + label: type.name, + value: type.id + })), + placeholder: "Filter by Activity Type", + multiple: true + } + } + }, + { + key: "selection_status", + label: "Selection Status", + operators: [OPERATORS.IS], + values: { + type: "select", + props: { + options: [...selectionStatusOptions], + placeholder: "Filter by Selection Status", + multiple: true + } + }, + customParser: (f) => { + const filter = []; + if (f.value) { + switch (f.value) { + case "only_rejected": + filter.push("has_rejected_presentations==true"); + filter.push("has_accepted_presentations==false"); + filter.push("has_alternate_presentations==false"); + break; + case "only_accepted": + filter.push("has_rejected_presentations==false"); + filter.push("has_accepted_presentations==true"); + filter.push("has_alternate_presentations==false"); + break; + case "only_alternate": + filter.push("has_rejected_presentations==false"); + filter.push("has_accepted_presentations==false"); + filter.push("has_alternate_presentations==true"); + break; + case "accepted_alternate": + filter.push("has_rejected_presentations==false"); + filter.push("has_accepted_presentations==true"); + filter.push("has_alternate_presentations==true"); + break; + case "accepted_rejected": + filter.push("has_rejected_presentations==true"); + filter.push("has_accepted_presentations==true"); + filter.push("has_alternate_presentations==false"); + break; + case "alternate_rejected": + filter.push("has_rejected_presentations==true"); + filter.push("has_accepted_presentations==false"); + filter.push("has_alternate_presentations==true"); + break; + case "accepted": + filter.push("has_accepted_presentations==true"); + break; + case "rejected": + filter.push("has_rejected_presentations==true"); + break; + case "alternate": + filter.push("has_alternate_presentations==true"); + break; + default: + break; + } + } + return filter; + } + }, + { + key: "presentations_track_group_id", + label: "Track Group", + operators: [OPERATORS.IS], + values: { + type: "select", + props: { + options: summit.track_groups.map((trackGroup) => ({ + label: trackGroup.name, + value: trackGroup.id + })), + placeholder: "Filter by Track Groups", + multiple: true + } + } + }, + { + key: "media_upload_with_type", + label: "Media Upload Type", + operators: [OPERATORS.HAS, OPERATORS.HAS_NOT], + values: { + type: "select", + props: { + options: mediaUploadTypes.map((type) => ({ + value: type.id, + label: type.name + })), + placeholder: "Filter by Media Upload Type", + multiple: true + } + }, + customParser: (f) => { + const filter = []; + + if (f.operator === OPERATORS.HAS.value) { + const value = Array.isArray(f.value) ? f.value.join("||") : f.value; + filter.push(`has_media_upload_with_type==${value}`); + } else { + const value = Array.isArray(f.value) ? f.value.join("&&") : f.value; + filter.push(`has_not_media_upload_with_type==${value}`); + } + + return filter; + } + } +]; + +const sourceOptions = [ + { + label: T.translate("summit_speakers_list.speakers"), + value: sources.speakers + }, + { + label: T.translate("summit_submitters_list.submitters"), + value: sources.submitters + }, + { + label: T.translate("summit_submitters_list.submitters_no_speakers"), + value: sources.submitters_no_speakers + } +]; + +const SummitSpeakersListPage = ({ + currentSummit, + history, + speakersProps, + submittersProps, + mediaUploadTypes, + getMediaUploads, + initSpeakersList, + initSubmittersList, + getSpeakersBySummit, + getSubmittersBySummit, + exportSummitSpeakers, + exportSummitSubmitters, + selectSummitSpeaker, + unselectSummitSpeaker, + selectSummitSubmitter, + unselectSummitSubmitter, + selectAllSummitSpeakers, + selectAllSummitSubmitters, + unselectAllSummitSpeakers, + unselectAllSummitSubmitters +}) => { + const [source, setSource] = useState(sources.speakers); + const isSpeakerMode = source === sources.speakers; + const subjectProps = isSpeakerMode ? speakersProps : submittersProps; + const { parsedFilter, resetFilters } = useGridFilter(FILTER_ID); + + useEffect(() => { + if (currentSummit) { + getMediaUploads("", 1, MAX_PER_PAGE, "name", 1); + } + }, [currentSummit]); + + // reset filters if source changes + useEffect(() => { + initSubmittersList(); + initSpeakersList(); + resetFilters(); + }, [currentSummit, source]); + + // fetch speakers/submitters list if filters change + useEffect(() => { + if (currentSummit) { + getBySummit(); + } + }, [currentSummit, source, parsedFilter.join(",")]); + + const getBySummit = (params = {}) => { + const { term, page, perPage, order, orderDir } = subjectProps; + + const mergedParams = { term, page, perPage, order, orderDir, ...params }; + + const getSubjects = isSpeakerMode + ? getSpeakersBySummit + : getSubmittersBySummit; + + const { + term: t, + page: p, + perPage: pp, + order: o, + orderDir: od + } = mergedParams; + + getSubjects(t, p, pp, o, od, parsedFilter, source); + }; + + const handleSourceChange = (ev) => { + const { value } = ev.target; + setSource(value); + }; + + const handleEdit = (itemId) => { + if (isSpeakerMode) { + history.push(`/app/speakers/${itemId}`); + } + }; + + const handlePageChange = (page) => { + getBySummit({ page }); + }; + + const handleSort = (index, key, dir) => { + getBySummit({ order: key, orderDir: dir }); + }; + + const handleSearch = (term) => { + getBySummit({ term }); + }; + + const handleExport = (ev) => { + ev.preventDefault(); + const { term, order, orderDir } = subjectProps; + const exportSubjects = isSpeakerMode + ? exportSummitSpeakers + : exportSummitSubmitters; + + exportSubjects(term, order, orderDir, parsedFilter, source); + }; + + const handleSelected = (itemId, isSelected) => { + const select = isSpeakerMode ? selectSummitSpeaker : selectSummitSubmitter; + const unselect = isSpeakerMode + ? unselectSummitSpeaker + : unselectSummitSubmitter; + + if (isSelected) select(itemId); + else unselect(itemId); + }; + + const handleSelectedAll = (ev) => { + const selectedAll = ev.target.checked; + const selectAll = isSpeakerMode + ? selectAllSummitSpeakers + : selectAllSummitSubmitters; + const unselectAll = isSpeakerMode + ? unselectAllSummitSpeakers + : unselectAllSummitSubmitters; + + if (selectedAll) selectAll(); + else unselectAll(); + }; + + const { + items, + lastPage, + currentPage, + term, + order, + orderDir, + totalItems, + selectedCount, + selectedAll + } = subjectProps; + + const columns = [ + { + columnKey: "full_name", + value: T.translate("general.name"), + sortable: true + }, + { + columnKey: "email", + value: T.translate("general.email"), + sortable: true + }, + { + columnKey: "accepted_presentations_count", + value: T.translate("summit_speakers_list.accepted") + }, + { + columnKey: "alternate_presentations_count", + value: T.translate("summit_speakers_list.alternate") + }, + { + columnKey: "rejected_presentations_count", + value: T.translate("summit_speakers_list.rejected") + } + ]; + + const tableOptions = { + sortCol: order, + sortDir: orderDir, + actions: { + edit: { + onClick: handleEdit, + onSelected: handleSelected, + onSelectedAll: handleSelectedAll + } + }, + selectedAll + }; + + if (!currentSummit?.id) return
; + + return ( +
+

+ {" "} + {isSpeakerMode + ? T.translate("summit_speakers_list.summit_speakers_list") + : T.translate("summit_submitters_list.summit_submitters_list")}{" "} + ({totalItems}) +

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + + {items.length === 0 && ( +
+ {isSpeakerMode + ? T.translate("summit_speakers_list.no_speakers") + : T.translate("summit_submitters_list.no_submitters")} +
+ )} + + {items.length > 0 && ( +
+ + + {T.translate("summit_speakers_list.items_qty", { + qty: selectedCount + })} + + + + +
+ )} +
+ ); +}; + +const mapStateToProps = ({ + currentSummitState, + currentSummitSpeakersListState, + currentSummitSubmittersListState, + mediaUploadListState +}) => ({ + currentSummit: currentSummitState.currentSummit, + speakersProps: currentSummitSpeakersListState, + submittersProps: currentSummitSubmittersListState, + mediaUploadTypes: mediaUploadListState.media_uploads +}); + +export default connect(mapStateToProps, { + getMediaUploads, + initSpeakersList, + getSpeakersBySummit, + exportSummitSpeakers, + selectSummitSpeaker, + unselectSummitSpeaker, + selectAllSummitSpeakers, + unselectAllSummitSpeakers, + initSubmittersList, + getSubmittersBySummit, + exportSummitSubmitters, + selectSummitSubmitter, + unselectSummitSubmitter, + selectAllSummitSubmitters, + unselectAllSummitSubmitters +})(SummitSpeakersListPage); diff --git a/src/reducers/summit_speakers/summit-speakers-list-reducer.js b/src/reducers/summit_speakers/summit-speakers-list-reducer.js index 8b9746723..fce12d1cd 100644 --- a/src/reducers/summit_speakers/summit-speakers-list-reducer.js +++ b/src/reducers/summit_speakers/summit-speakers-list-reducer.js @@ -31,7 +31,7 @@ import { buildSpeakersSubmittersList } from "../utils/methods"; const DEFAULT_STATE = { items: [], - term: null, + term: "", order: "full_name", orderDir: 1, currentPage: 1, @@ -42,12 +42,6 @@ const DEFAULT_STATE = { selectedItems: [], excludedItems: [], selectedAll: false, - selectionPlanFilter: [], - trackFilter: [], - trackGroupFilter: [], - activityTypeFilter: [], - selectionStatusFilter: [], - mediaUploadTypeFilter: { operator: null, value: [] }, currentFlowEvent: "", currentSummitId: null }; diff --git a/src/reducers/summit_submitters/summit-submitters-list-reducer.js b/src/reducers/summit_submitters/summit-submitters-list-reducer.js index 330c46f73..603dfe644 100644 --- a/src/reducers/summit_submitters/summit-submitters-list-reducer.js +++ b/src/reducers/summit_submitters/summit-submitters-list-reducer.js @@ -31,7 +31,7 @@ import { buildSpeakersSubmittersList } from "../utils/methods"; const DEFAULT_STATE = { items: [], - term: null, + term: "", order: "full_name", orderDir: 1, currentPage: 1, @@ -42,12 +42,6 @@ const DEFAULT_STATE = { selectedItems: [], excludedItems: [], selectedAll: false, - selectionPlanFilter: [], - trackFilter: [], - trackGroupFilter: [], - activityTypeFilter: [], - selectionStatusFilter: [], - mediaUploadTypeFilter: { operator: null, value: [] }, currentFlowEvent: "", currentSummitId: null }; diff --git a/src/store.js b/src/store.js index 2fbc1a166..a28be6fd9 100644 --- a/src/store.js +++ b/src/store.js @@ -16,6 +16,7 @@ import { loggedUserReducer } from "openstack-uicore-foundation/lib/security/redu import thunk from "redux-thunk"; import { persistStore, persistCombineReducers } from "redux-persist"; import storage from "redux-persist/es/storage"; +import { allFiltersReducer } from "openstack-uicore-foundation/lib/components/mui/grid-filter"; import baseReducer from "./reducers/base-reducer"; import currentSummitReducer from "./reducers/summits/current-summit-reducer"; import directoryReducer from "./reducers/summits/directory-reducer"; @@ -184,6 +185,7 @@ const config = { const reducers = persistCombineReducers(config, { loggedUserState: loggedUserReducer, + allGridFiltersState: allFiltersReducer, baseState: baseReducer, directoryState: directoryReducer, currentSummitState: currentSummitReducer, diff --git a/yarn.lock b/yarn.lock index 81f28eeff..4f610cb1c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9056,10 +9056,10 @@ open@^10.0.3: is-inside-container "^1.0.0" wsl-utils "^0.1.0" -openstack-uicore-foundation@5.0.20: - version "5.0.20" - resolved "https://registry.npmjs.org/openstack-uicore-foundation/-/openstack-uicore-foundation-5.0.20.tgz#b302b85c091b5f30399a010f0336d9217040692d" - integrity sha512-cbHygUhWAi+7Wd+saV/+FBCSRoXYDXG2wt0ySfPAYuMN0ii5aBIfE6G2T2u1iMe4Dy1YxCM3/NPGuMgTLIJsPA== +openstack-uicore-foundation@5.0.21-beta.1: + version "5.0.21-beta.1" + resolved "https://registry.yarnpkg.com/openstack-uicore-foundation/-/openstack-uicore-foundation-5.0.21-beta.1.tgz#f20aaf7025635009328254f024e0fe143e9aed60" + integrity sha512-Y5AUEEe9UXVIIBb8ZIM7qtc0RPn6xemyJm7BOT0kwekwcuMISR8RCjUbBh0FzZtn+SC1AmhABr/nLlJGEBlf3Q== optionator@^0.9.1: version "0.9.4" @@ -10013,7 +10013,7 @@ react-input-autosize@^2.2.1: dependencies: prop-types "^15.5.8" -react-is@^16.13.1, react-is@^16.3.2, react-is@^16.6.0, react-is@^16.7.0: +react-is@^16.13.1, react-is@^16.3.2, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -10033,7 +10033,7 @@ react-is@^19.0.0, react-is@^19.2.3: resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.5.tgz#7e7b54143e9313fed787b23fd4295d5a23872ad9" integrity sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ== -react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: +react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== @@ -10054,20 +10054,7 @@ react-overlays@^0.7.4: prop-types-extra "^1.0.1" warning "^3.0.0" -react-redux@^5.0.7: - version "5.1.2" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.2.tgz#b19cf9e21d694422727bf798e934a916c4080f57" - integrity sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q== - dependencies: - "@babel/runtime" "^7.1.2" - hoist-non-react-statics "^3.3.0" - invariant "^2.2.4" - loose-envify "^1.1.0" - prop-types "^15.6.1" - react-is "^16.6.0" - react-lifecycles-compat "^3.0.0" - -react-redux@^7.2.0: +react-redux@^7.1.0, react-redux@^7.2.0: version "7.2.9" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.9.tgz#09488fbb9416a4efe3735b7235055442b042481d" integrity sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==