diff --git a/dashboard/src/actions/overviewActions.js b/dashboard/src/actions/overviewActions.js index 4ad6a8bb67..87fdf73d2d 100644 --- a/dashboard/src/actions/overviewActions.js +++ b/dashboard/src/actions/overviewActions.js @@ -4,10 +4,10 @@ import * as TYPES from "./types"; import { DANGER, ERROR_MSG } from "assets/constants/toastConstants"; import API from "../utils/axiosInstance"; -import { uriTemplate } from "../utils/helper"; +import { clearCachedSession } from "./authActions"; import { findNoOfDays } from "utils/dateFunctions"; import { showToast } from "./toastActions"; -import { clearCachedSession } from "./authActions"; +import { uriTemplate } from "../utils/helper"; export const getDatasets = () => async (dispatch, getState) => { const alreadyRendered = getState().overview.loadingDone; @@ -65,8 +65,12 @@ const initializeRuns = () => (dispatch, getState) => { data.forEach((item) => { item[CONSTANTS.IS_EDIT] = false; item[CONSTANTS.NAME_COPY] = item.name; - item[CONSTANTS.IS_DIRTY] = false; - item[CONSTANTS.NAME_VALIDATED] = CONSTANTS.DEFAULT; + item[CONSTANTS.SERVER_DELETION_COPY] = + item.metadata[CONSTANTS.SERVER_DELETION]; + + clearEditableFields(item); + item[CONSTANTS.NAME_VALIDATED] = CONSTANTS.SUCCESS; + item[CONSTANTS.IS_ITEM_SEEN] = !!item?.metadata?.[CONSTANTS.DASHBOARD_SEEN]; item[CONSTANTS.IS_ITEM_FAVORITED] = !!item?.metadata?.[CONSTANTS.USER_FAVORITE]; @@ -107,38 +111,45 @@ const metaDataActions = { read: CONSTANTS.DASHBOARD_SEEN, favorite: CONSTANTS.USER_FAVORITE, datasetName: CONSTANTS.DATASET_NAME, + serverDelete: CONSTANTS.SERVER_DELETION, }; +export const getMetaDataActions = + (dataset, actionType, actionValue) => async (dispatch) => { + const method = metaDataActions[actionType]; + const payload = { [method]: actionValue }; + return dispatch(updateDataset(dataset, payload)); + }; /** * Function which return a thunk to be passed to a Redux dispatch() call * @function * @param {Object} dataset - Dataset which is being updated - * @param {string} actionType - Action (save, read, favorite) being performed - * @param {string} actionValue - Value to be updated (true/ false) - * @return {Object} - dispatch the action and update the state + * @param {Object} payload - Action (save, read, favorite, edit) being performed with the new value + * @return {Function} - dispatch the action and update the state */ export const updateDataset = - (dataset, actionType, actionValue) => async (dispatch, getState) => { + (dataset, payload) => async (dispatch, getState) => { try { dispatch({ type: TYPES.LOADING }); const runs = getState().overview.datasets; - - const method = metaDataActions[actionType]; - const endpoints = getState().apiEndpoint.endpoints; + const uri = uriTemplate(endpoints, "datasets_metadata", { dataset: dataset.resource_id, }); const response = await API.put(uri, { - metadata: { [method]: actionValue }, + metadata: { payload }, }); if (response.status === 200) { - const dataIndex = runs.findIndex( + const item = runs.find( (item) => item.resource_id === dataset.resource_id ); - runs[dataIndex].metadata[metaDataActions[actionType]] = - response.data.metadata[metaDataActions[actionType]]; + + for (const key in response.data.metadata) { + if (key in item.metadata) + item.metadata[key] = response.data.metadata[key]; + } dispatch({ type: TYPES.USER_RUNS, payload: runs, @@ -169,7 +180,7 @@ export const updateDataset = * Function to delete the dataset * @function * @param {Object} dataset - Dataset which is being updated * - * @return {Object} - dispatch the action and update the state + * @return {Function} - dispatch the action and update the state */ export const deleteDataset = (dataset) => async (dispatch, getState) => { try { @@ -230,7 +241,7 @@ export const updateMultipleDataset = selectedRuns.forEach((item) => method === "delete" ? dispatch(deleteDataset(item)) - : dispatch(updateDataset(item, method, value)) + : dispatch(getMetaDataActions(item, method, value)) ); const toastMsg = method === "delete" @@ -249,7 +260,7 @@ export const updateMultipleDataset = * @function * @param {Object} dataset - Dataset which is being updated * @param {string} updateValue - Access type value (Public/Private) - * @return {Object} - dispatch the action and update the state + * @return {Function} - dispatch the action and update the state */ export const publishDataset = (dataset, updateValue) => async (dispatch, getState) => { @@ -314,25 +325,31 @@ const updateDatasetType = (data, type) => { * @param {string} metadata - metadata that is being edited * * @param {string} rId - resource_id of the dataset which is being set to edit * @param {string} type - Type of the Dataset (Saved/New) - * @return {Object} - dispatch the action and update the state + * @return {Function} - dispatch the action and update the state */ export const editMetadata = (value, metadata, rId, type) => async (dispatch, getState) => { const data = filterDatasetType(type, getState); const rIndex = data.findIndex((item) => item.resource_id === rId); - data[rIndex][metadata] = value; - data[rIndex][CONSTANTS.IS_DIRTY] = true; - if (value.length > CONSTANTS.DATASET_NAME_LENGTH) { - data[rIndex][CONSTANTS.NAME_VALIDATED] = CONSTANTS.ERROR; - data[rIndex][ - CONSTANTS.NAME_ERROR_MSG - ] = `Length should be < ${CONSTANTS.DATASET_NAME_LENGTH}`; - } else if (value.length === 0) { - data[rIndex][CONSTANTS.NAME_VALIDATED] = CONSTANTS.ERROR; - data[rIndex][CONSTANTS.NAME_ERROR_MSG] = `Length cannot be 0`; - } else { - data[rIndex][CONSTANTS.NAME_VALIDATED] = CONSTANTS.SUCCESS; + + if (metadata === CONSTANTS.NAME_KEY) { + if (value.length > CONSTANTS.DATASET_NAME_LENGTH) { + data[rIndex][CONSTANTS.NAME_VALIDATED] = CONSTANTS.ERROR; + data[rIndex][ + CONSTANTS.NAME_ERROR_MSG + ] = `Length should be < ${CONSTANTS.DATASET_NAME_LENGTH}`; + } else if (value.length === 0) { + data[rIndex][CONSTANTS.NAME_VALIDATED] = CONSTANTS.ERROR; + data[rIndex][CONSTANTS.NAME_ERROR_MSG] = `Length cannot be 0`; + } else { + data[rIndex][CONSTANTS.NAME_VALIDATED] = CONSTANTS.SUCCESS; + data[rIndex][CONSTANTS.NAME_KEY] = value; + data[rIndex][CONSTANTS.IS_DIRTY_NAME] = true; + } + } else if (metadata === CONSTANTS.SERVER_DELETION_KEY) { + data[rIndex][CONSTANTS.IS_DIRTY_SERVER_DELETE] = true; + data[rIndex].metadata[CONSTANTS.SERVER_DELETION] = value; } dispatch(updateDatasetType(data, type)); }; @@ -342,7 +359,7 @@ export const editMetadata = * @param {string} rId - resource_id of the dataset which is being set to edit * @param {boolean} isEdit - Set/not set to edit * @param {string} type - Type of the Dataset (Saved/New) - * @return {Object} - dispatch the action and update the state + * @return {Function} - dispatch the action and update the state */ export const setRowtoEdit = (rId, isEdit, type) => async (dispatch, getState) => { @@ -353,8 +370,40 @@ export const setRowtoEdit = if (!isEdit) { data[rIndex].name = data[rIndex][CONSTANTS.NAME_COPY]; - data[rIndex][CONSTANTS.IS_DIRTY] = false; + data[rIndex].metadata[CONSTANTS.SERVER_DELETION] = + data[rIndex][CONSTANTS.SERVER_DELETION_COPY]; + clearEditableFields(data[rIndex]); data[rIndex][CONSTANTS.NAME_VALIDATED] = CONSTANTS.SUCCESS; } dispatch(updateDatasetType(data, type)); }; +/** + * Function to get the metadata that are edited and to be sent for update + * @function + * @param {Object} dataset - Dataset which is being updated + * @param {string} type - Type of the Dataset (Saved/New) + * @return {Function} - dispatch the action and update the state + */ +export const getEditedMetadata = + (dataset, type) => async (dispatch, getState) => { + const data = filterDatasetType(type, getState); + + const item = data.find((item) => item.resource_id === dataset.resource_id); + + const editedMetadata = {}; + if (item[CONSTANTS.IS_DIRTY_NAME]) { + editedMetadata[metaDataActions[CONSTANTS.DATASET_NAME_KEY]] = item.name; + } + + if (item[CONSTANTS.IS_DIRTY_SERVER_DELETE]) { + editedMetadata[metaDataActions[CONSTANTS.SERVER_DELETION_KEY]] = new Date( + item.metadata[CONSTANTS.SERVER_DELETION] + ).toISOString(); + } + dispatch(updateDataset(dataset, editedMetadata)); + }; + +const clearEditableFields = (item) => { + item[CONSTANTS.IS_DIRTY_NAME] = false; + item[CONSTANTS.IS_DIRTY_SERVER_DELETE] = false; +}; diff --git a/dashboard/src/assets/constants/overviewConstants.js b/dashboard/src/assets/constants/overviewConstants.js index 2d3df00adb..b96111c873 100644 --- a/dashboard/src/assets/constants/overviewConstants.js +++ b/dashboard/src/assets/constants/overviewConstants.js @@ -6,19 +6,24 @@ export const DASHBOARD_SEEN = "global.dashboard.seen"; export const DASHBOARD_LOAD_DELAY_MS = 1000; export const DATASET_ACCESS = "dataset.access"; export const DATASET_NAME = "dataset.name"; +export const DATASET_NAME_KEY = "datasetName"; export const DATASET_NAME_LENGTH = 1024; export const DATASET_OWNER = "dataset.owner"; export const DATASET_UPLOADED = "dataset.uploaded"; export const DEFAULT = "default"; export const ERROR = "error"; export const EXPIRATION_DAYS_LIMIT = 20; -export const IS_DIRTY = "isDirty"; +export const IS_DIRTY_NAME = "isDirtyName"; +export const IS_DIRTY_SERVER_DELETE = "isDirtyDate"; export const IS_EDIT = "isEdit"; export const IS_ITEM_FAVORITED = "isItemFavorited"; export const IS_ITEM_SEEN = "isItemSeen"; export const NAME_COPY = "name_copy"; export const NAME_ERROR_MSG = "name_errorMsg"; +export const NAME_KEY = "name"; export const NAME_VALIDATED = "name_validated"; export const SERVER_DELETION = "server.deletion"; +export const SERVER_DELETION_COPY = "server_deletion_copy"; +export const SERVER_DELETION_KEY = "serverDelete"; export const SUCCESS = "success"; export const USER_FAVORITE = "user.dashboard.favorite"; diff --git a/dashboard/src/modules/components/OverviewComponent/NewRunsComponent.jsx b/dashboard/src/modules/components/OverviewComponent/NewRunsComponent.jsx index bd35aa9404..f3afd062c6 100644 --- a/dashboard/src/modules/components/OverviewComponent/NewRunsComponent.jsx +++ b/dashboard/src/modules/components/OverviewComponent/NewRunsComponent.jsx @@ -3,6 +3,7 @@ import "./index.less"; import { DASHBOARD_SEEN, IS_ITEM_SEEN, + NAME_KEY, ROWS_PER_PAGE, START_PAGE_NUMBER, USER_FAVORITE, @@ -21,10 +22,10 @@ import React, { useCallback, useState } from "react"; import { deleteDataset, editMetadata, + getMetaDataActions, setRows, setRowtoEdit, setSelectedRuns, - updateDataset, } from "actions/overviewActions"; import { useDispatch, useSelector } from "react-redux"; @@ -80,21 +81,22 @@ const NewRunsComponent = () => { /* Selecting */ const makeFavorites = (dataset, isFavoriting = true) => { - dispatch(updateDataset(dataset, "favorite", isFavoriting)); + dispatch(getMetaDataActions(dataset, "favorite", isFavoriting)); }; - const saveRowData = (metadataType, dataset, value) => { - dispatch(updateDataset(dataset, metadataType, value)); + const saveRowData = (dataset) => { + const item = newRuns.find((run) => run.resource_id === dataset.resource_id); + dispatch(getMetaDataActions(dataset, "datasetName", item.name)); }; const moreActionItems = (dataset) => [ { title: "Save", - onClick: () => dispatch(updateDataset(dataset, "save", true)), + onClick: () => dispatch(getMetaDataActions(dataset, "save", true)), }, { title: dataset.metadata[DASHBOARD_SEEN] ? "Mark unread" : "Mark read", onClick: () => dispatch( - updateDataset(dataset, "read", !dataset.metadata[DASHBOARD_SEEN]) + getMetaDataActions(dataset, "read", !dataset.metadata[DASHBOARD_SEEN]) ), }, { @@ -103,7 +105,11 @@ const NewRunsComponent = () => { : "Mark favorite", onClick: () => dispatch( - updateDataset(dataset, "favorite", !dataset.metadata[USER_FAVORITE]) + getMetaDataActions( + dataset, + "favorite", + !dataset.metadata[USER_FAVORITE] + ) ), }, { @@ -179,7 +185,7 @@ const NewRunsComponent = () => { rowActions={rowActions} item={item} textInputEdit={(val) => - updateTblValue(val, "name", item.resource_id) + updateTblValue(val, NAME_KEY, item.resource_id) } /> diff --git a/dashboard/src/modules/components/OverviewComponent/SavedRunsComponent.jsx b/dashboard/src/modules/components/OverviewComponent/SavedRunsComponent.jsx index dd0d3400a7..b0a95d4bab 100644 --- a/dashboard/src/modules/components/OverviewComponent/SavedRunsComponent.jsx +++ b/dashboard/src/modules/components/OverviewComponent/SavedRunsComponent.jsx @@ -4,6 +4,8 @@ import { DASHBOARD_SEEN, DATASET_ACCESS, IS_ITEM_SEEN, + NAME_KEY, + SERVER_DELETION_KEY, } from "assets/constants/overviewConstants"; import { InnerScrollContainer, @@ -18,10 +20,11 @@ import React, { useCallback } from "react"; import { deleteDataset, editMetadata, + getEditedMetadata, + getMetaDataActions, publishDataset, setRowtoEdit, setSelectedSavedRuns, - updateDataset, } from "actions/overviewActions"; import { useDispatch, useSelector } from "react-redux"; @@ -65,7 +68,7 @@ const SavedRunsComponent = () => { title: dataset.metadata[DASHBOARD_SEEN] ? "Mark unread" : "Mark read", onClick: () => dispatch( - updateDataset(dataset, "read", !dataset.metadata[DASHBOARD_SEEN]) + getMetaDataActions(dataset, "read", !dataset.metadata[DASHBOARD_SEEN]) ), }, { @@ -75,11 +78,11 @@ const SavedRunsComponent = () => { ]; /* Actions Row */ const makeFavorites = (dataset, isFavoriting = true) => { - dispatch(updateDataset(dataset, "favorite", isFavoriting)); + dispatch(getMetaDataActions(dataset, "favorite", isFavoriting)); }; /* Edit Dataset */ - const saveRowData = (metadataType, dataset, value) => { - dispatch(updateDataset(dataset, metadataType, value)); + const saveRowData = (dataset) => { + dispatch(getEditedMetadata(dataset, "savedRuns")); }; const toggleEdit = useCallback( (rId, isEdit) => dispatch(setRowtoEdit(rId, isEdit, "savedRuns")), @@ -88,6 +91,7 @@ const SavedRunsComponent = () => { const updateTblValue = (newValue, metadata, rId) => { dispatch(editMetadata(newValue, metadata, rId, "savedRuns")); }; + /* Edit Dataset */ const columnNames = { result: "Result", @@ -136,9 +140,16 @@ const SavedRunsComponent = () => { onSelectRuns={onSelectRuns} isRowSelected={isRowSelected} textInputEdit={(val) => - updateTblValue(val, "name", item.resource_id) + updateTblValue(val, NAME_KEY, item.resource_id) } toggleEdit={toggleEdit} + onDateSelect={(str) => + updateTblValue( + str, + SERVER_DELETION_KEY, + item.resource_id + ) + } saveRowData={saveRowData} /> diff --git a/dashboard/src/modules/components/OverviewComponent/common-component.jsx b/dashboard/src/modules/components/OverviewComponent/common-component.jsx index 175a2eddd4..b6f4518acb 100644 --- a/dashboard/src/modules/components/OverviewComponent/common-component.jsx +++ b/dashboard/src/modules/components/OverviewComponent/common-component.jsx @@ -5,6 +5,7 @@ import * as CONSTANTS from "assets/constants/overviewConstants"; import { ActionsColumn, Td } from "@patternfly/react-table"; import { Button, + DatePicker, Dropdown, DropdownItem, DropdownToggle, @@ -175,6 +176,12 @@ export const RenderPagination = (props) => { }; export const EditRow = (props) => { + const isDirtyRow = () => { + return ( + props.item[CONSTANTS.IS_DIRTY_NAME] || + props.item[CONSTANTS.IS_DIRTY_SERVER_DELETE] + ); + }; return (
{!props.item[CONSTANTS.IS_EDIT] ? ( @@ -187,13 +194,11 @@ export const EditRow = (props) => {