diff --git a/package.json b/package.json index e04c54b36..4aedaa4ed 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.35", + "openstack-uicore-foundation": "5.0.36-beta.2", "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", @@ -124,7 +124,7 @@ "react-switch": "^6.0.0", "react-tooltip": "^5.28.0", "react-window": "^1.8.10", - "redux": "^3.7.2", + "redux": "^4.2.1", "redux-persist": "^5.10.0", "redux-thunk": "^2.3.0", "segmented-control": "0.1.12", diff --git a/src/actions/__tests__/event-actions.test.js b/src/actions/__tests__/event-actions.test.js index 0dce052d8..ba2e72dec 100644 --- a/src/actions/__tests__/event-actions.test.js +++ b/src/actions/__tests__/event-actions.test.js @@ -52,7 +52,7 @@ describe("Event Actions", () => { capturedParams = null; }); - test("builds speakers_count between filter using [] syntax", async () => { + test("passes pre-built [] range filter strings through to the request", async () => { const store = mockStore({ currentSummitState: { currentSummit: { @@ -63,7 +63,7 @@ describe("Event Actions", () => { }); store.dispatch( - getEvents(null, 1, 10, "id", 1, { speakers_count_filter: [1, 3] }, []) + getEvents(null, 1, 10, "id", 1, ["speakers_count[]1&&3"], []) ); await flushPromises(); @@ -71,7 +71,6 @@ describe("Event Actions", () => { expect(getRequest).toHaveBeenCalledTimes(1); expect(capturedParams).toBeTruthy(); expect(capturedParams["filter[]"]).toContain("speakers_count[]1&&3"); - expect(capturedParams["filter[]"]).not.toContain("speakers_count[]]1&&3"); }); test("requests type.use_speakers in fields for event list", async () => { @@ -93,7 +92,7 @@ describe("Event Actions", () => { expect(capturedParams.fields).toContain("type.use_speakers"); }); - test("builds speakers_count operator filter when value is not an array", async () => { + test("passes pre-built operator filter strings through to the request", async () => { const store = mockStore({ currentSummitState: { currentSummit: { @@ -103,9 +102,7 @@ describe("Event Actions", () => { } }); - store.dispatch( - getEvents(null, 1, 10, "id", 1, { speakers_count_filter: ">=2" }, []) - ); + store.dispatch(getEvents(null, 1, 10, "id", 1, ["speakers_count>=2"], [])); await flushPromises(); diff --git a/src/actions/event-actions.js b/src/actions/event-actions.js index 795915f49..2c933993b 100644 --- a/src/actions/event-actions.js +++ b/src/actions/event-actions.js @@ -36,11 +36,9 @@ import { epochToMomentTimeZone } from "openstack-uicore-foundation/lib/utils/met import URI from "urijs"; import history from "../history"; import { - checkOrFilter, getAccessTokenSafely, isNumericString, - joinCVSChunksAndNormalizeHeaders, - parseDateRangeFilter + joinCVSChunksAndNormalizeHeaders } from "../utils/methods"; import { getQAUsersBySummitEvent } from "./user-chat-roles-actions"; import { getAuditLog } from "./audit-log-actions"; @@ -51,11 +49,8 @@ import { DEFAULT_PER_PAGE, EXPORT_PAGE_SIZE_200, FIVE_PER_PAGE, - HOUR_AND_HALF, - SECONDS_TO_MINUTES, - TWO + HOUR_AND_HALF } from "../utils/constants"; -import { getIdValue } from "../utils/summitUtils"; URI.escapeQuerySpace = false; @@ -99,323 +94,6 @@ const fieldsBoundToQuestions = [ SOCIAL_DESCRIPTION ]; -const parseFilters = (filters, term = null) => { - const filter = []; - - if ( - filters.hasOwnProperty("event_type_capacity_filter") && - Array.isArray(filters.event_type_capacity_filter) && - filters.event_type_capacity_filter.length > 0 - ) { - if ( - filters.event_type_capacity_filter.includes("allows_attendee_vote_filter") - ) { - filter.push("type_allows_attendee_vote==1"); - } - if (filters.event_type_capacity_filter.includes("allows_location_filter")) { - filter.push("type_allows_location==1"); - } - if ( - filters.event_type_capacity_filter.includes( - "allows_publishing_dates_filter" - ) - ) { - filter.push("type_allows_publishing_dates==1"); - } - } - - if ( - filters.hasOwnProperty("selection_plan_id_filter") && - Array.isArray(filters.selection_plan_id_filter) && - filters.selection_plan_id_filter.length > 0 - ) { - filter.push( - `selection_plan_id==${filters.selection_plan_id_filter.join("||")}` - ); - } - - if ( - filters.hasOwnProperty("location_id_filter") && - Array.isArray(filters.location_id_filter) && - filters.location_id_filter.length > 0 - ) { - filter.push(`location_id==${filters.location_id_filter.join("||")}`); - } - - if ( - filters.hasOwnProperty("selection_status_filter") && - Array.isArray(filters.selection_status_filter) && - filters.selection_status_filter.length > 0 - ) { - filter.push( - `selection_status==${filters.selection_status_filter.join("||")}` - ); - } - - if ( - filters.hasOwnProperty("review_status_filter") && - Array.isArray(filters.review_status_filter) && - filters.review_status_filter.length > 0 - ) { - filter.push(`review_status==${filters.review_status_filter.join("||")}`); - } - - if (filters?.progress_flag?.length > 0) { - filter.push( - filters.progress_flag - .map((pf) => `actions==type_id==${pf}&&is_completed==1`) - .join(",") - ); - } - - if ( - filters.hasOwnProperty("track_id_filter") && - Array.isArray(filters.track_id_filter) && - filters.track_id_filter.length > 0 - ) { - filter.push(`track_id==${filters.track_id_filter.join("||")}`); - } - - if ( - filters.hasOwnProperty("event_type_id_filter") && - Array.isArray(filters.event_type_id_filter) && - filters.event_type_id_filter.length > 0 - ) { - filter.push(`event_type_id==${filters.event_type_id_filter.join("||")}`); - } - - if ( - filters.hasOwnProperty("speaker_id_filter") && - Array.isArray(filters.speaker_id_filter) && - filters.speaker_id_filter.length > 0 - ) { - filter.push( - `speaker_id==${filters.speaker_id_filter - .map((speaker) => speaker.id) - .join("||")}` - ); - } - - if ( - filters.hasOwnProperty("level_filter") && - Array.isArray(filters.level_filter) && - filters.level_filter.length > 0 - ) { - filter.push(`level==${filters.level_filter.join("||")}`); - } - - if ( - filters.hasOwnProperty("tags_filter") && - Array.isArray(filters.tags_filter) && - filters.tags_filter.length > 0 - ) { - filter.push(`tags==${filters.tags_filter.map((t) => t.tag).join("||")}`); - } - - if (filters.published_filter) { - filter.push( - `published==${filters.published_filter === "published" ? "1" : "0"}` - ); - } - - if (filters.has_rsvp_filter) { - filter.push( - `rsvp_type${filters.has_rsvp_filter === "yes" ? "<>" : "=="}None` - ); - } - - if (filters.start_date_filter) { - parseDateRangeFilter(filter, filters.start_date_filter, "start_date"); - } - - if (filters.end_date_filter) { - parseDateRangeFilter(filter, filters.end_date_filter, "end_date"); - } - - if (filters.created_filter) { - parseDateRangeFilter(filter, filters.created_filter, "created"); - } - - if (filters.modified_filter) { - parseDateRangeFilter(filter, filters.modified_filter, "last_edited"); - } - - if (filters.duration_filter) { - // multiply values to send the minutes in seconds - if (Array.isArray(filters.duration_filter)) { - // between - filter.push( - `duration[]${filters.duration_filter[0] * SECONDS_TO_MINUTES}&&${ - filters.duration_filter[1] * SECONDS_TO_MINUTES - }` - ); - } else { - filter.push( - `duration${filters.duration_filter.replace(/\d/g, "")}${ - filters.duration_filter.replace(/\D/g, "") * SECONDS_TO_MINUTES - }` - ); - } - } - - if (filters.speakers_count_filter) { - if ( - Array.isArray(filters.speakers_count_filter) && - filters.speakers_count_filter.length === TWO - ) { - // between - filter.push( - `speakers_count[]${filters.speakers_count_filter[0]}&&${filters.speakers_count_filter[1]}` - ); - } else { - filter.push(`speakers_count${filters.speakers_count_filter}`); - } - } - - if ( - filters.hasOwnProperty("submitters") && - Array.isArray(filters.submitters) && - filters.submitters.length > 0 - ) { - // created by fullname | created_by_email - filter.push( - filters.submitters.map((tt) => { - const escapedFullName = escapeFilterValue( - `${tt.first_name} ${tt.last_name}` - ); - const escapedEmail = escapeFilterValue(tt.email); - const fullNameFilter = `created_by_fullname==${escapedFullName}`; - const emailFilter = `created_by_email==${escapedEmail}`; - return [fullNameFilter, emailFilter]; - }, "") - ); - } - - if (filters.hasOwnProperty("streaming_url") && filters.streaming_url) { - const searchString = escapeFilterValue(filters.streaming_url); - filter.push(`streaming_url@@${searchString}`); - } - if (filters.hasOwnProperty("meeting_url") && filters.meeting_url) { - const searchString = escapeFilterValue(filters.meeting_url); - filter.push(`meeting_url@@${searchString}`); - } - if (filters.hasOwnProperty("etherpad_link") && filters.etherpad_link) { - const searchString = escapeFilterValue(filters.etherpad_link); - filter.push(`etherpad_link@@${searchString}`); - } - - if (filters.hasOwnProperty("streaming_type") && filters.streaming_type) { - filter.push(`streaming_type==${filters.streaming_type}`); - } - - if ( - filters.hasOwnProperty("submission_source_filter") && - filters.submission_source_filter - ) { - filter.push(`submission_source==${filters.submission_source_filter}`); - } - - if ( - filters.hasOwnProperty("speaker_company") && - Array.isArray(filters.speaker_company) && - filters.speaker_company.length > 0 - ) { - filter.push( - `speaker_company==${filters.speaker_company - .map((c) => escapeFilterValue(c.name)) - .join("||")}` - ); - } - - if ( - filters.hasOwnProperty("submitter_company") && - Array.isArray(filters.submitter_company) && - filters.submitter_company.length > 0 - ) { - filter.push( - `created_by_company==${filters.submitter_company - .map((c) => escapeFilterValue(c.name)) - .join("||")}` - ); - } - - if ( - filters.hasOwnProperty("sponsor") && - Array.isArray(filters.sponsor) && - filters.sponsor.length > 0 - ) { - filter.push( - `sponsor==${filters.sponsor.map((sponsor) => sponsor.name).join("||")}` - ); - } - - if ( - filters.hasOwnProperty("all_companies") && - Array.isArray(filters.all_companies) && - filters.all_companies.length > 0 - ) { - const companies = filters.all_companies - .map((c) => escapeFilterValue(c.name)) - .join("||"); - filter.push( - `speaker_company==${companies},created_by_company==${companies},sponsor==${companies}` - ); - } - - if (filters.is_public) { - filter.push("is_public==1"); - } - - if (filters.is_activity) { - filter.push("is_activity==1"); - } - - if ( - filters.hasOwnProperty("submission_status_filter") && - Array.isArray(filters.submission_status_filter) && - filters.submission_status_filter.length > 0 - ) { - filter.push( - `submission_status==${filters.submission_status_filter.join("||")}` - ); - } - - if ( - filters.hasOwnProperty("media_upload_with_type") && - filters.media_upload_with_type.operator !== null && - Array.isArray(filters.media_upload_with_type.value) && - filters.media_upload_with_type.value.length > 0 - ) { - const concatOperator = - filters.media_upload_with_type.operator === "has_media_upload_with_type==" - ? "||" - : "&&"; - filter.push( - `${ - filters.media_upload_with_type.operator - }${filters.media_upload_with_type.value - .map((v) => v.id) - .join(concatOperator)}` - ); - } - - if (term) { - const escapedTerm = escapeFilterValue(term); - let searchString = - `title=@${escapedTerm},` + - `abstract=@${escapedTerm},` + - `speaker_title=@${escapedTerm}`; - - if (isNumericString(term)) { - searchString += `,id==${term}`; - } - - filter.push(searchString); - } - - return checkOrFilter(filters, filter); -}; - export const normalizeEvent = (entity, eventTypeConfig, summit) => { const normalizedEntity = { ...entity }; if (!normalizedEntity.start_date) delete normalizedEntity.start_date; @@ -516,12 +194,19 @@ export const normalizeBulkEvents = (entity) => { const normalizedEvent = { id: e.id, title: e.title, - selection_plan_id: getIdValue(e.selection_plan) || e.selection_plan_id, - location_id: e.location?.id || e.location_id, + selection_plan_id: + e.selection_plan?.id || + (typeof e.selection_plan === "number" + ? e.selection_plan + : e.selection_plan_id), + location_id: + e.location?.id || + (typeof e.location === "number" ? e.location : e.location_id), start_date: e.start_date, end_date: e.end_date, - type_id: getIdValue(e.type) || e.type_id, - track_id: getIdValue(e.track) || e.track_id, + type_id: e.type?.id || (typeof e.type === "number" ? e.type : e.type_id), + track_id: + e.track?.id || (typeof e.track === "number" ? e.track : e.track_id), duration: e.duration, streaming_url: e.streaming_url, streaming_type: e.streaming_type, @@ -545,12 +230,12 @@ export const normalizeBulkEvents = (entity) => { export const getEvents = ( - term = null, + term = "", page = DEFAULT_CURRENT_PAGE, perPage = DEFAULT_PER_PAGE, order = "id", orderDir = DEFAULT_ORDER_DIR, - filters = {}, + filters = [], extraColumns = [] ) => async (dispatch, getState) => { @@ -558,10 +243,23 @@ export const getEvents = const accessToken = await getAccessTokenSafely(); const { currentSummit } = currentSummitState; const summitTZ = currentSummit.time_zone.name; + const filter = Array.isArray(filters) ? [...filters] : []; dispatch(startLoading()); - const filter = parseFilters(filters, term); + if (term) { + const escapedTerm = escapeFilterValue(term); + let searchString = + `title=@${escapedTerm},` + + `abstract=@${escapedTerm},` + + `speaker_title=@${escapedTerm}`; + + if (isNumericString(term)) { + searchString += `,id==${term}`; + } + + filter.push(searchString); + } const params = { expand: @@ -607,66 +305,65 @@ export const getEvents = }); }; -export const bulkUpdateEvents = - (summitId, events) => async (dispatch, getState) => { - const { currentSummitState } = getState(); - const accessToken = await getAccessTokenSafely(); - const { currentSummit } = currentSummitState; - dispatch(startLoading()); +export const bulkUpdateEvents = (events) => async (dispatch, getState) => { + const { currentSummitState } = getState(); + const accessToken = await getAccessTokenSafely(); + const { currentSummit } = currentSummitState; + dispatch(startLoading()); - const normalizedEvents = normalizeBulkEvents( - events.map((event) => - normalizeEvent( - event, - currentSummit.event_types.find((et) => et.id === event.type_id) - ) + const normalizedEvents = normalizeBulkEvents( + events.map((event) => + normalizeEvent( + event, + currentSummit.event_types.find((et) => et.id === event.type_id) ) - ); + ) + ); - return putRequest( - null, - createAction(UPDATED_REMOTE_EVENTS)({}), - `${window.API_BASE_URL}/api/v1/summits/${summitId}/events/?access_token=${accessToken}`, - { - events: normalizedEvents - }, - authErrorHandler - )({})(dispatch) - .then(() => { - dispatch(stopLoading()); - dispatch( - showSuccessMessage( - T.translate("bulk_actions_page.messages.update_success"), - () => history.push(`/app/summits/${currentSummit.id}/events/`) - ) - ); - const { - currentEventListState: { - term, - currentPage, - perPage, - order, - orderDir, - filters, - extraColumns - } - } = getState(); - dispatch( - getEvents( - term, - currentPage, - perPage, - order, - orderDir, - filters, - extraColumns - ) - ); - }) - .catch(() => { - console.log("ERROR"); - }); - }; + return putRequest( + null, + createAction(UPDATED_REMOTE_EVENTS)({}), + `${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/events/?access_token=${accessToken}`, + { + events: normalizedEvents + }, + authErrorHandler + )({})(dispatch) + .then(() => { + dispatch(stopLoading()); + dispatch( + showSuccessMessage( + T.translate("bulk_actions_page.messages.update_success"), + () => history.push(`/app/summits/${currentSummit.id}/events/`) + ) + ); + const { + currentEventListState: { + term, + currentPage, + perPage, + order, + orderDir, + filters, + extraColumns + } + } = getState(); + dispatch( + getEvents( + term, + currentPage, + perPage, + order, + orderDir, + filters, + extraColumns + ) + ); + }) + .catch(() => { + dispatch(stopLoading()); + }); +}; export const getActionTypes = (selectionPlanId) => async (dispatch, getState) => { @@ -1236,26 +933,34 @@ export const deleteEvent = (eventId) => async (dispatch, getState) => { }; export const exportEvents = - ( - term = null, - order = "id", - orderDir = DEFAULT_ORDER_DIR, - extraFilters = {} - ) => + (term = null, order = "id", orderDir = DEFAULT_ORDER_DIR, filters = []) => async (dispatch, getState) => { - dispatch(startLoading()); const { currentSummitState, currentEventListState } = getState(); const accessToken = await getAccessTokenSafely(); const { currentSummit } = currentSummitState; const { totalEvents } = currentEventListState; - const csvMIME = "text/csv;charset=utf-8"; const filename = `${currentSummit.name}-Activities.csv`; const totalPages = Math.ceil(totalEvents / EXPORT_PAGE_SIZE_200); - const endpoint = `${window.API_BASE_URL}/api/v1/summits/${currentSummit.id}/events/csv`; - const filter = parseFilters(extraFilters, term); + const filter = Array.isArray(filters) ? [...filters] : []; + + dispatch(startLoading()); + + if (term) { + const escapedTerm = escapeFilterValue(term); + let searchString = + `title=@${escapedTerm},` + + `abstract=@${escapedTerm},` + + `speaker_title=@${escapedTerm}`; + + if (isNumericString(term)) { + searchString += `,id==${term}`; + } + + filter.push(searchString); + } const params = Array.from({ length: totalPages }, (_, i) => { const res = { @@ -1489,10 +1194,17 @@ export const getEventComments = const { currentSummitState } = getState(); const { currentSummit } = currentSummitState; const summitTZ = currentSummit.time_zone_id; + const filter = []; dispatch(startLoading()); - const filter = parseFilters(filters); + if (filters.is_public) { + filter.push("is_public==1"); + } + + if (filters.is_activity) { + filter.push("is_activity==1"); + } if (term) { const escapedTerm = escapeFilterValue(term); @@ -1500,9 +1212,6 @@ export const getEventComments = filter.push(searchString); } - // `is_public==${escapedTerm},`; - // `is_activity==${escapedTerm},`; - const params = { page, per_page: perPage, diff --git a/src/components/filters/select-filter-criteria/index.js b/src/components/filters/select-filter-criteria/index.js index 06c7c4a04..fa2c0b18d 100644 --- a/src/components/filters/select-filter-criteria/index.js +++ b/src/components/filters/select-filter-criteria/index.js @@ -9,15 +9,13 @@ * 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 PropTypes from "prop-types"; import T from "i18n-react/dist/i18n-react"; import Swal from "sweetalert2"; import AsyncSelect from "react-select/lib/Async"; - -import styles from "./index.module.less"; import { queryFilterCriterias } from "../../../actions/filter-criteria-actions"; const SelectFilterCriteria = ({ @@ -30,7 +28,6 @@ const SelectFilterCriteria = ({ }) => { const [selectedFilter, setSelectedFilter] = useState(null); const [defaultOptions, setDefaultOptions] = useState([]); - const [isLoading, setIsLoading] = useState(false); useEffect(() => { if (!selectedFilterCriteria) setSelectedFilter(null); @@ -44,9 +41,9 @@ const SelectFilterCriteria = ({ const handleFilterDelete = () => { Swal.fire({ title: T.translate("general.are_you_sure"), - text: - T.translate("select_filter_criteria.remove_filter_criteria_warning") + - `"${selectedFilter.label}"`, + text: `${T.translate( + "select_filter_criteria.remove_filter_criteria_warning" + )}"${selectedFilter.label}"`, type: "warning", showCancelButton: true, confirmButtonColor: "#DD6B55", @@ -59,7 +56,6 @@ const SelectFilterCriteria = ({ }; const getCriterias = (input, callback) => { - setIsLoading(true); // we need to map into value/label because of a bug in react-select 2 // https://github.com/JedWatson/react-select/issues/2998 @@ -69,7 +65,6 @@ const SelectFilterCriteria = ({ label: c.name, ...c })); - setIsLoading(false); callback(newOptions); }; @@ -88,18 +83,17 @@ const SelectFilterCriteria = ({ }; return ( -
+
diff --git a/src/components/filters/select-filter-criteria/index.module.less b/src/components/filters/select-filter-criteria/index.module.less deleted file mode 100644 index c9a629fe6..000000000 --- a/src/components/filters/select-filter-criteria/index.module.less +++ /dev/null @@ -1,3 +0,0 @@ -.selectFilterWrapper { - margin-bottom: 20px; -} diff --git a/src/components/tables/editable-table/EditableTable.js b/src/components/tables/editable-table/EditableTable.js deleted file mode 100644 index a8f959d07..000000000 --- a/src/components/tables/editable-table/EditableTable.js +++ /dev/null @@ -1,269 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { Tooltip } from "react-tooltip"; -import T from "i18n-react/dist/i18n-react"; -import EditableTableHeading from "./EditableTableHeading"; -import EditableTableRow from "./EditableTableRow"; -import { - INDEX_NOT_FOUND, - SORT_ASCENDING, - SORT_DESCENDING -} from "../../../utils/constants"; - -import styles from "./index.module.less"; - -const defaults = { - sortFunc: (a, b) => { - if (a < b) { - return SORT_DESCENDING; - } - if (a > b) { - return SORT_ASCENDING; - } - return 0; - }, - sortable: false, - sortCol: 0, - sortDir: 1, - colWidth: "" -}; - -function EditableTable(props) { - const { - options, - columns, - currentSummit, - page, - data, - handleSort, - updateData, - handleDeleteRow, - formattingFunction, - afterUpdate = [] - } = props; - let tableClass = options.hasOwnProperty("className") ? options.className : ""; - const [editButton, setEditButton] = useState(false); - const [editEnabled, setEditEnabled] = useState(false); - const [selected, setSelected] = useState([]); - const [selectAll, setSelectAll] = useState(false); - tableClass += options.actions?.edit ? " table-hover" : ""; - - const getSortDir = (columnKey, columnIndex, sortCol, sortDir) => { - if (columnKey && columnKey === sortCol) { - return sortDir; - } - if (sortCol === columnIndex) { - return sortDir; - } - return null; - }; - - const resetState = () => { - setSelectAll(false); - setEditButton(false); - setEditEnabled(false); - setSelected([]); - }; - - // reseting states on data changes/pagination - useEffect(() => { - resetState(); - }, [page]); - - useEffect(() => { - if (selected.length > 0) { - setEditButton(true); - } else { - setEditButton(false); - setEditEnabled(false); - } - }, [selected]); - - useEffect(() => { - if (selectAll) { - setSelected(data); - setSelectAll(true); - } else { - setSelectAll(false); - setSelected([]); - } - }, [selectAll]); - - const updateSelected = (row, checked) => { - const selectedRow = row; - const rowIndex = selected.findIndex((s) => s.id === selectedRow.id); - const exists = rowIndex !== INDEX_NOT_FOUND; - - if (checked) { - if (exists) { - // if already on selected list, replace with new data - const updatedSelected = { ...selectedRow }; - const newSelected = selected.slice(); - newSelected[rowIndex] = updatedSelected; - setSelected(newSelected); - } else { - // append to list - setSelected((currSelected) => [...currSelected, selectedRow]); - } - } else { - setSelected(selected.filter((se) => se.id !== selectedRow.id)); - } - }; - - const handledAfterUpdateData = () => { - const actionsAfterUpdate = []; - if (afterUpdate.length > 0) { - afterUpdate.forEach(({ action, propertyName }) => { - selected.forEach((row) => { - if (Array.isArray(row[propertyName])) { - row[propertyName].forEach((e) => { - actionsAfterUpdate.push(action(e)); - }); - } else { - actionsAfterUpdate.push(action(row[propertyName])); - } - }); - }); - } - return Promise.all(actionsAfterUpdate); - }; - - const onUpdateEvents = (evt) => { - evt.stopPropagation(); - evt.preventDefault(); - updateData(currentSummit.id, selected) - .then(() => handledAfterUpdateData()) - .then(() => resetState()) - .catch((error) => { - console.error("Error updating events:", error); - }); - }; - - return ( -
-
-
-
- {editEnabled ? ( - <> - - - - ) : ( - - )} -
-
-
- - - - - {columns.map((col, i) => { - const sortCol = - typeof options.sortCol !== "undefined" - ? options.sortCol - : defaults.sortCol; - const sortDir = - typeof options.sortDir !== "undefined" - ? options.sortDir - : defaults.sortDir; - const sortFunc = - typeof options.sortFunc !== "undefined" - ? options.sortFunc - : defaults.sortFunc; - const sortable = - typeof col.sortable !== "undefined" - ? col.sortable - : defaults.sortable; - const colWidth = - typeof col.width !== "undefined" - ? col.width - : defaults.colWidth; - - return ( - - {col.value} - - ); - })} - {options.actions && ( - - {options.actionsHeader || " "} - - )} - - - - {columns.length > 0 && - data.map((row, i) => { - if (Array.isArray(row) && row.length !== columns.length) { - console.warn( - `Data at row ${i} is ${row.length}. It should be ${columns.length}.` - ); - return ; - } - - return ( - - - - ); - })} - -
- setSelectAll(!selectAll)} - checked={selectAll} - /> -
-
- -
- ); -} - -export default EditableTable; diff --git a/src/components/tables/editable-table/EditableTableHeading.js b/src/components/tables/editable-table/EditableTableHeading.js deleted file mode 100644 index 17578effb..000000000 --- a/src/components/tables/editable-table/EditableTableHeading.js +++ /dev/null @@ -1,60 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { SORT_ASCENDING, SORT_DESCENDING } from "../../../utils/constants"; - -function EditableTableHeading(props) { - const { - editEnabled, - sortable, - sortDir, - onSort, - columnIndex, - columnKey, - sortFunc, - width, - children - } = props; - const getSortClass = () => { - // disable sorting if on edit mode - if (!sortable || editEnabled) return null; - - switch (sortDir) { - case SORT_ASCENDING: - return "sorting_asc"; - case SORT_DESCENDING: - return "sorting_desc"; - default: - return sortable ? "sorting" : null; - } - }; - - const handleSort = (e) => { - e.preventDefault(); - - if (!onSort || !sortable) return; - - onSort( - columnIndex, - columnKey, - sortDir ? sortDir * SORT_DESCENDING : SORT_ASCENDING, - sortFunc - ); - }; - - return ( - - {children} - - ); -} - -EditableTableHeading.propTypes = { - onSort: PropTypes.func, - sortDir: PropTypes.number, - columnIndex: PropTypes.number, - columnKey: PropTypes.any, - sortable: PropTypes.bool, - sortFunc: PropTypes.func -}; - -export default EditableTableHeading; diff --git a/src/components/tables/editable-table/EditableTableRow.js b/src/components/tables/editable-table/EditableTableRow.js deleted file mode 100644 index bcd49f6f7..000000000 --- a/src/components/tables/editable-table/EditableTableRow.js +++ /dev/null @@ -1,190 +0,0 @@ -import React, { useEffect, useState } from "react"; -import TextArea from "openstack-uicore-foundation/lib/components/inputs/textarea-input"; -import T from "i18n-react/dist/i18n-react"; -import history from "../../../history"; - -import styles from "./index.module.less"; - -function EditableTableRow(props) { - const { - row, - columns, - editEnabled, - selected, - updateSelected, - deleteRow, - selectAll, - currentSummit, - actions, - formattingFunction - } = props; - const [checked, setChecked] = useState(false); - const [editData, setEditData] = useState(row); - - const formattedData = formattingFunction(row, currentSummit); - - useEffect(() => { - setEditData(row); - }, [row]); - useEffect(() => { - updateSelected(editData, checked); - }, [checked, row]); - useEffect(() => { - setChecked(selectAll); - }, [selectAll]); - useEffect(() => { - if (selected.length === 0) { - setChecked(false); - } - }, [selected]); - useEffect(() => { - updateSelected(editData, checked); - }, [editData]); - useEffect(() => { - if (!editEnabled) { - setEditData(row); - } - }, [editEnabled]); - - const onRowChange = (ev) => { - const { value, id } = ev.target; - if (id.includes("___")) { - const parts = id.split("___"); // ['array property', '', 'element property'] - const arrayProp = parts[0]; - const elementId = parseInt(parts[1], 10); - const prop = parts[2]; - - const arrayToChange = editData[arrayProp].map((elem) => { - if (elem.id === elementId) { - return { ...elem, [prop]: value }; - } - return elem; - }); - - const newEventData = { ...editData, [arrayProp]: arrayToChange }; - setEditData(newEventData); - } else { - const newEventData = { ...editData, [id]: value }; - setEditData(newEventData); - } - }; - - const onRemoveOption = (rowId, id) => { - const currentRow = selected.find((r) => r.id === row.id); - const newOptions = currentRow[id].filter((s) => s.id !== rowId); - const newEventData = { ...editData, [id]: newOptions }; - setEditData(newEventData); - }; - - return ( - <> - - setChecked(!checked)} - checked={checked} - /> - - {row.id} - {selected.find((s) => s.id === row.id) && editEnabled && checked ? ( - <> - {columns.map((col) => { - if (col.columnKey === "id") { - return null; - } - if (col.editableField === true) { - // Default field as text - return ( - -