diff --git a/src/actions/summit-actions.js b/src/actions/summit-actions.js index 15a178edd..d6e2f940c 100644 --- a/src/actions/summit-actions.js +++ b/src/actions/summit-actions.js @@ -21,7 +21,8 @@ import { stopLoading, startLoading, showSuccessMessage, - authErrorHandler + authErrorHandler, + escapeFilterValue } from "openstack-uicore-foundation/lib/utils/actions"; import moment from "moment-timezone"; import { @@ -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(); @@ -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}`); + } + + if (filters.length > 0) { + params["filter[]"] = filters; + } + getRequest( createAction(REQUEST_SUMMITS), createAction(RECEIVE_SUMMITS), diff --git a/src/i18n/en.json b/src/i18n/en.json index 9c4f9fd05..c47dff548 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -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", diff --git a/src/pages/summits/summit-dashboard-page.js b/src/pages/summits/summit-dashboard-page.js index 4185da456..010bc48d0 100644 --- a/src/pages/summits/summit-dashboard-page.js +++ b/src/pages/summits/summit-dashboard-page.js @@ -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"; @@ -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 }); } } @@ -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"); } @@ -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(); - if (!currentSummit.id) return
; + if (!currentSummit.id || !currentSummit.time_zone?.name) return
; return (
@@ -100,16 +104,16 @@ class SummitDashboardPage extends React.Component {
{currentSummit.time_zone.name}
{" "} - {this.getFormattedTime(this.state.localtime / 1000)}{" "} + {this.getFormattedTime(this.state.localtime.unix())}{" "}
@@ -131,11 +135,11 @@ class SummitDashboardPage extends React.Component {
@@ -160,16 +164,16 @@ class SummitDashboardPage extends React.Component {
{canEditSummit && currentSummit.selection_plans.map((sp) => ( -
+
{sp.name}
@@ -192,11 +196,11 @@ class SummitDashboardPage extends React.Component {
@@ -219,11 +223,11 @@ class SummitDashboardPage extends React.Component {
@@ -253,24 +257,24 @@ class SummitDashboardPage extends React.Component {

{T.translate("dashboard.events")}  - {this.state.collapseState["events"] && ( + {this.state.collapseState.events && ( this.onCollapseChange("events")} className="fa fa-plus-square clickable" aria-hidden="true" - > + /> )} - {!this.state.collapseState["events"] && ( + {!this.state.collapseState.events && ( this.onCollapseChange("events")} className="fa fa-minus-square clickable" aria-hidden="true" - > + /> )}

- {!this.state.collapseState["events"] && ( + {!this.state.collapseState.events && (
@@ -309,24 +313,24 @@ class SummitDashboardPage extends React.Component {

{T.translate("dashboard.voting")}  - {this.state.collapseState["voting"] && ( + {this.state.collapseState.voting && ( this.onCollapseChange("voting")} className="fa fa-plus-square clickable" aria-hidden="true" - > + /> )} - {!this.state.collapseState["voting"] && ( + {!this.state.collapseState.voting && ( this.onCollapseChange("voting")} className="fa fa-minus-square clickable" aria-hidden="true" - > + /> )}

- {!this.state.collapseState["voting"] && ( + {!this.state.collapseState.voting && (
@@ -345,24 +349,24 @@ class SummitDashboardPage extends React.Component {

{T.translate("dashboard.emails")}  - {this.state.collapseState["emails"] && ( + {this.state.collapseState.emails && ( this.onCollapseChange("emails")} className="fa fa-plus-square clickable" aria-hidden="true" - > + /> )} - {!this.state.collapseState["emails"] && ( + {!this.state.collapseState.emails && ( this.onCollapseChange("emails")} className="fa fa-minus-square clickable" aria-hidden="true" - > + /> )}

- {!this.state.collapseState["emails"] && ( + {!this.state.collapseState.emails && (
@@ -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 diff --git a/src/pages/summits/summit-directory-page.js b/src/pages/summits/summit-directory-page.js index 2b4118710..f5d84a303 100644 --- a/src/pages/summits/summit-directory-page.js +++ b/src/pages/summits/summit-directory-page.js @@ -11,169 +11,247 @@ * limitations under the License. * */ -import React from "react"; +import React, { useEffect, useState } from "react"; import { connect } from "react-redux"; import T from "i18n-react/dist/i18n-react"; -import Swal from "sweetalert2"; import { formatEpoch } from "openstack-uicore-foundation/lib/utils/methods"; -import { Pagination } from "react-bootstrap"; -import history from "../../history"; +import MuiTable from "openstack-uicore-foundation/lib/components/mui/table"; +import MuiSearchInput from "openstack-uicore-foundation/lib/components/mui/search-input"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Checkbox from "@mui/material/Checkbox"; +import Chip from "@mui/material/Chip"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import FormGroup from "@mui/material/FormGroup"; +import Grid2 from "@mui/material/Grid2"; +import AddIcon from "@mui/icons-material/Add"; import { loadSummits, clearCurrentSummit, deleteSummit } from "../../actions/summit-actions"; import Member from "../../models/member"; +import { DEFAULT_CURRENT_PAGE } from "../../utils/constants"; -import "../../styles/summit-directory-page.less"; - -class SummitDirectoryPage extends React.Component { - constructor(props) { - super(props); - - props.clearCurrentSummit(); - props.loadSummits(); - - this.handlePageChange = this.handlePageChange.bind(this); - } +const SummitDirectoryPage = ({ + summits, + member, + currentPage, + perPage, + totalSummits, + loadSummits, + clearCurrentSummit, + deleteSummit, + history +}) => { + const [searchTerm, setSearchTerm] = useState(""); + const [hidePastEvents, setHidePastEvents] = useState(false); + const safeSummits = Array.isArray(summits) ? summits : []; + const safeMember = + member && typeof member === "object" + ? { ...member, groups: Array.isArray(member.groups) ? member.groups : [] } + : { groups: [] }; + useEffect(() => { + clearCurrentSummit(); + loadSummits(DEFAULT_CURRENT_PAGE, perPage, searchTerm, hidePastEvents); + }, []); - handlePageChange(page) { - const { perPage } = this.props; - this.props.loadSummits(page, perPage); + let memberObj; + try { + memberObj = new Member(safeMember); + } catch (e) { + memberObj = { + canEditSummit: () => false, + canAddSummits: () => false, + canDeleteSummits: () => false + }; } + const canEditSummit = + typeof memberObj.canEditSummit === "function" + ? memberObj.canEditSummit() + : false; + const canAddSummits = + typeof memberObj.canAddSummits === "function" + ? memberObj.canAddSummits() + : false; + const canDeleteSummits = + typeof memberObj.canDeleteSummits === "function" + ? memberObj.canDeleteSummits() + : false; - onSelectedSummit(event, summit) { - event.preventDefault(); - history.push(`/app/summits/${summit.id}/dashboard`); - return false; - } + try { + const handlePageChange = (page) => { + loadSummits(page, perPage, searchTerm, hidePastEvents); + }; - onEditSummit(summit, ev) { - const { history } = this.props; - ev.preventDefault(); + const handlePerPageChange = (newPerPage) => { + loadSummits(DEFAULT_CURRENT_PAGE, newPerPage, searchTerm, hidePastEvents); + }; - history.push(`/app/summits/${summit.id}`); - } + const handleSearch = (value) => { + setSearchTerm(value); + loadSummits(DEFAULT_CURRENT_PAGE, perPage, value, hidePastEvents); + }; - onNewSummit(ev) { - const { history } = this.props; - ev.preventDefault(); + const handleHidePastEventsChange = (ev) => { + const { checked } = ev.target; + setHidePastEvents(checked); + loadSummits(DEFAULT_CURRENT_PAGE, perPage, searchTerm, checked); + }; - history.push("/app/summits/new"); - } + const handleNewSummit = () => { + history.push("/app/summits/new"); + }; - onDeleteSummit(summit, ev) { - const { deleteSummit } = this.props; + const handleEditSummit = (summit) => { + history.push(`/app/summits/${summit.id}`); + }; - ev.preventDefault(); + const handleSelectSummit = (summit) => { + history.push(`/app/summits/${summit.id}/dashboard`); + }; - Swal.fire({ - title: T.translate("general.are_you_sure"), - text: `${T.translate("directory.remove_warning")} ${summit.name}`, - type: "warning", - showCancelButton: true, - confirmButtonColor: "#DD6B55", - confirmButtonText: T.translate("general.yes_delete") - }).then((result) => { - if (result.value) { - deleteSummit(summit.id); + const columns = [ + { + columnKey: "id", + header: T.translate("directory.id"), + width: 80 + }, + { + columnKey: "name", + header: T.translate("directory.summit_name") + }, + { + columnKey: "sponsor_qty", + header: T.translate("directory.sponsors"), + render: (row) => row.sponsor_qty ?? 0 + }, + { + columnKey: "sponsor_forms_qty", + header: T.translate("directory.forms"), + render: (row) => row.sponsor_forms_qty ?? 0 + }, + { + columnKey: "sponsor_attachments_qty", + header: T.translate("directory.attachments"), + render: (row) => row.sponsor_attachments_qty ?? 0 + }, + { + columnKey: "start_date", + header: T.translate("directory.start_date"), + render: (row) => formatEpoch(row.start_date, "MMMM Do YYYY") + }, + { + columnKey: "end_date", + header: T.translate("directory.end_date"), + render: (row) => formatEpoch(row.end_date, "MMMM Do YYYY") + }, + { + columnKey: "invite_only_registration", + header: "", + width: 120, + render: (row) => + row.invite_only_registration ? ( + + ) : null } - }); - } - - render() { - const { summits, member, lastPage, currentPage, totalSummits } = this.props; - const memberObj = new Member(member); - - const canEditSummit = memberObj.canEditSummit(); - const canAddSummits = memberObj.canAddSummits(); - const canDeleteSummits = memberObj.canDeleteSummits(); + ]; return ( -
-

- {" "} - {T.translate("directory.summits")} ({totalSummits}) -

- {canAddSummits && ( -
-
- -
-
- )} -
- - - {summits && - summits.map((summit) => ( - - - - - - - - - ))} - -
{summit.id} {summit.name} {formatEpoch(summit.start_date, "MMMM Do YYYY")} {formatEpoch(summit.end_date, "MMMM Do YYYY")} - {summit.invite_only_registration && ( - - {" "} - {T.translate("directory.invitation_only")} - - )} - - this.onSelectedSummit(e, summit)} - className="btn btn-default btn-sm" - > - {T.translate("directory.select")} - - {canEditSummit && ( - - {T.translate("general.edit")} - - )} - {canDeleteSummits && ( - - {T.translate("general.delete")} - - )} -
- -
+ + )} + + + deleteSummit(id) : undefined} + onSelect={handleSelectSummit} + getName={(row) => row.name} + deleteDialogTitle={T.translate("general.are_you_sure")} + deleteDialogBody={(name) => + `${T.translate("directory.remove_warning")} ${name}` + } + deleteDialogConfirmText={T.translate("general.yes_delete")} + confirmButtonColor="error" + /> + + ); + } catch (err) { + return ( +
+

{T.translate("directory.error_loading")}

+
{err.message}
); } -} +}; const mapStateToProps = ({ directoryState, loggedUserState }) => ({ ...directoryState, diff --git a/src/reducers/summits/directory-reducer.js b/src/reducers/summits/directory-reducer.js index 21eb6f84b..1159aa596 100644 --- a/src/reducers/summits/directory-reducer.js +++ b/src/reducers/summits/directory-reducer.js @@ -1,5 +1,6 @@ import { LOGOUT_USER } from "openstack-uicore-foundation/lib/security/actions"; import { + REQUEST_SUMMITS, RECEIVE_ALL_SUMMITS, RECEIVE_SUMMITS, SUMMIT_ADDED, @@ -18,12 +19,25 @@ const DEFAULT_STATE = { const directoryReducer = (state = DEFAULT_STATE, action) => { const { type, payload } = action; switch (type) { + case REQUEST_SUMMITS: { + const page = payload?.page ?? state.currentPage; + const perPage = payload?.per_page ?? state.perPage; + + return { + ...state, + summits: [], + currentPage: page, + perPage + }; + } case RECEIVE_SUMMITS: { - const { current_page, total, last_page, data } = payload.response; + const { current_page, total, last_page, per_page, data } = + payload.response; return { ...state, summits: data, currentPage: current_page, + perPage: per_page ?? state.perPage, lastPage: last_page, totalSummits: total };