From 3576aa74efc58f3bcac674a8b6787f3f7b693b96 Mon Sep 17 00:00:00 2001 From: GnsP Date: Tue, 20 Feb 2024 10:15:11 +0530 Subject: [PATCH] ux improvements --- app/cdap/api/longRunningOperation.ts | 4 +- app/cdap/components/FooterContext/index.ts | 25 ++++++ .../SourceControlManagementForm.tsx | 24 +++--- .../SourceControlManagement/index.tsx | 2 - .../LocalPipelineListView/PipelineTable.tsx | 15 +--- .../LocalPipelineListView/index.tsx | 38 ++------- .../OperationAlert.tsx | 22 ++++- .../OperationsHistoryView/index.tsx | 31 +++++++ .../RemotePipelineTable.tsx | 4 +- .../RemotePipelineListView/index.tsx | 8 +- .../SourceControlManagement/SearchBox.tsx | 2 +- .../SyncStatusFilters.tsx | 2 +- .../SourceControlManagement/SyncTabs.tsx | 45 +++++++--- .../SourceControlManagement/helpers.ts | 30 +++---- .../SourceControlManagement/index.tsx | 10 ++- .../store/ActionCreator.ts | 32 ++++++- .../SourceControlManagement/store/index.ts | 20 +++++ .../SourceControlManagement/styles.ts | 4 +- app/cdap/main.js | 31 ++++++- app/cdap/styles/common.scss | 4 + app/cdap/text/text-en.yaml | 5 ++ package.json | 83 ++++++++++--------- yarn.lock | 5 ++ 23 files changed, 306 insertions(+), 140 deletions(-) create mode 100644 app/cdap/components/FooterContext/index.ts create mode 100644 app/cdap/components/SourceControlManagement/OperationsHistoryView/index.tsx diff --git a/app/cdap/api/longRunningOperation.ts b/app/cdap/api/longRunningOperation.ts index 63e57137ef7..9c8618144eb 100644 --- a/app/cdap/api/longRunningOperation.ts +++ b/app/cdap/api/longRunningOperation.ts @@ -27,13 +27,13 @@ export const LongRunningOperationApi = { dataSrc, 'GET', 'REQUEST', - `${basePath}/?pageSize=1&filter=${PUSH_FILTER}` + `${basePath}/?pageSize=10&filter=${PUSH_FILTER}` ), getLatestPull: apiCreator( dataSrc, 'GET', 'REQUEST', - `${basePath}/?pageSize=1&filter=${PULL_FILTER}` + `${basePath}/?pageSize=10&filter=${PULL_FILTER}` ), pollOperation: apiCreator(dataSrc, 'GET', 'POLL', `${basePath}/:operationId`), stopOperation: apiCreator(dataSrc, 'POST', 'REQUEST', `${basePath}/:operationId/stop`), diff --git a/app/cdap/components/FooterContext/index.ts b/app/cdap/components/FooterContext/index.ts new file mode 100644 index 00000000000..a03d2336f29 --- /dev/null +++ b/app/cdap/components/FooterContext/index.ts @@ -0,0 +1,25 @@ +import { createContext, useContext, useEffect } from 'react'; + +interface IFooterContext { + show: boolean; + setShow(val?: boolean): void; +} + +export const FooterContext = createContext({ + show: true, + setShow() { + return; + }, +}); + +export function useHideFooterInPage() { + const { setShow } = useContext(FooterContext); + + useEffect(() => { + setShow(false); + + return () => setShow(true); + }, []); + + return setShow; +} diff --git a/app/cdap/components/NamespaceAdmin/SourceControlManagement/SourceControlManagementForm.tsx b/app/cdap/components/NamespaceAdmin/SourceControlManagement/SourceControlManagementForm.tsx index 795a416c8f4..9d8e8792456 100644 --- a/app/cdap/components/NamespaceAdmin/SourceControlManagement/SourceControlManagementForm.tsx +++ b/app/cdap/components/NamespaceAdmin/SourceControlManagement/SourceControlManagementForm.tsx @@ -340,17 +340,19 @@ const SourceControlManagementForm = ({ : [] } /> - { - handleValueChange(val, 'username'); - }} - /> + {formState.config?.provider !== providers.github && ( + { + handleValueChange(val, 'username'); + }} + /> + )} )} diff --git a/app/cdap/components/NamespaceAdmin/SourceControlManagement/index.tsx b/app/cdap/components/NamespaceAdmin/SourceControlManagement/index.tsx index 2661504d494..3601a85e3f8 100644 --- a/app/cdap/components/NamespaceAdmin/SourceControlManagement/index.tsx +++ b/app/cdap/components/NamespaceAdmin/SourceControlManagement/index.tsx @@ -34,7 +34,6 @@ import { getCurrentNamespace } from 'services/NamespaceStore'; import { getSourceControlManagement } from '../store/ActionCreator'; import Alert from 'components/shared/Alert'; import ButtonLoadingHoc from 'components/shared/Buttons/ButtonLoadingHoc'; -import { useHistory } from 'react-router'; const PrimaryTextLoadingButton = ButtonLoadingHoc(PrimaryTextButton); @@ -58,7 +57,6 @@ export const SourceControlManagement = () => { const sourceControlManagementConfig: ISourceControlManagementConfig = useSelector( (state) => state.sourceControlManagementConfig ); - const history = useHistory(); const toggleForm = () => { setIsFormOpen(!isFormOpen); diff --git a/app/cdap/components/SourceControlManagement/LocalPipelineListView/PipelineTable.tsx b/app/cdap/components/SourceControlManagement/LocalPipelineListView/PipelineTable.tsx index f742fea5c39..17e416a5766 100644 --- a/app/cdap/components/SourceControlManagement/LocalPipelineListView/PipelineTable.tsx +++ b/app/cdap/components/SourceControlManagement/LocalPipelineListView/PipelineTable.tsx @@ -56,6 +56,7 @@ interface IRepositoryPipelineTableProps { multiPushEnabled?: boolean; disabled?: boolean; syncStatusFilter?: TSyncStatusFilter; + lastOperationInfoShown?: boolean; } export const LocalPipelineTable = ({ @@ -65,6 +66,7 @@ export const LocalPipelineTable = ({ multiPushEnabled = false, disabled = false, syncStatusFilter = 'all', + lastOperationInfoShown = true, }: IRepositoryPipelineTableProps) => { const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(25); @@ -149,7 +151,7 @@ export const LocalPipelineTable = ({ }; return ( - + @@ -167,14 +169,6 @@ export const LocalPipelineTable = ({ {T.translate(`${PREFIX}.pipelineName`)} {T.translate(`${PREFIX}.lastSyncDate`)} - -
- {T.translate(`${PREFIX}.gitStatus`)} - } showOn="Hover"> - {T.translate(`${PREFIX}.gitStatusHelperText`)} - -
-
{multiPushEnabled && ( @@ -235,9 +229,6 @@ export const LocalPipelineTable = ({ {pipeline.name} {timeInstantToString(pipeline.lastSyncDate)} - - {pipeline.fileHash ? T.translate(`${PREFIX}.connected`) : '--'} - {multiPushEnabled && ( {pipeline.syncStatus === undefined || diff --git a/app/cdap/components/SourceControlManagement/LocalPipelineListView/index.tsx b/app/cdap/components/SourceControlManagement/LocalPipelineListView/index.tsx index 27fff02a4a5..b743cf608cd 100644 --- a/app/cdap/components/SourceControlManagement/LocalPipelineListView/index.tsx +++ b/app/cdap/components/SourceControlManagement/LocalPipelineListView/index.tsx @@ -20,6 +20,7 @@ import { useSelector } from 'react-redux'; import { getCurrentNamespace } from 'services/NamespaceStore'; import { countPushFailedPipelines, + dismissOperationAlert, fetchLatestOperation, getNamespacePipelineList, pushMultipleSelectedPipelines, @@ -69,7 +70,7 @@ export const LocalPipelineListView = () => { syncStatusFilter, } = useSelector(({ push }) => push); - const { running: isAnOperationRunning, operation } = useSelector( + const { running: isAnOperationRunning, operation, showLastOperationInfo } = useSelector( ({ operationRun }) => operationRun ); @@ -158,6 +159,7 @@ export const LocalPipelineListView = () => { multiPushEnabled={multiPushEnabled} disabled={isAnOperationRunning} syncStatusFilter={syncStatusFilter} + lastOperationInfoShown={showLastOperationInfo} /> { return ( - {operation && multiPushEnabled && } - - {operation && multiPushEnabled && } - {selectedPipelines.length > 0 && ( - -
- {T.translate(`${PREFIX}.pipelinesSelected`, { - selected: selectedPipelines.length, - total: localPipelines.length, - })} -
- {!multiPushEnabled && pushFailedCount > 0 && ( - <> - - {pushFailedCount === 1 - ? T.translate(`${PREFIX}.pipelinePushedFail`) - : T.translate(`${PREFIX}.pipelinesPushedFail`, { - count: pushFailedCount.toString(), - })} - - - {showFailedOnly - ? T.translate('commons.showAll') - : T.translate('commons.showFailed')} - - - )} - {multiPushEnabled && pushFailedCount > 0 && ( - {T.translate(`${PREFIX}.pipelinesPushedFailMulti`)} - )} -
+ {operation && multiPushEnabled && showLastOperationInfo && ( + )} + {selectedPipelines.length > 0 && ( diff --git a/app/cdap/components/SourceControlManagement/OperationAlert.tsx b/app/cdap/components/SourceControlManagement/OperationAlert.tsx index fe7581ce1d3..8e4bcbb255e 100644 --- a/app/cdap/components/SourceControlManagement/OperationAlert.tsx +++ b/app/cdap/components/SourceControlManagement/OperationAlert.tsx @@ -33,10 +33,12 @@ import { getCurrentNamespace } from 'services/NamespaceStore'; import { OperationStatus } from './OperationStatus'; import ExpandLess from '@material-ui/icons/ExpandLess'; import ExpandMore from '@material-ui/icons/ExpandMore'; +import CloseIcon from '@material-ui/icons/Close'; import { AlertErrorView } from './styles'; interface IOperationBannerProps { operation: IOperationRun; + onClose?(): void; } const StyledDiv = styled.div` @@ -45,12 +47,15 @@ const StyledDiv = styled.div` const ExpandWrapper = styled.div` height: 100%; - padding-top: 12px; + padding-top: 10px; + + display: flex; + justify-content: flex-end; `; const PREFIX = 'features.SourceControlManagement'; -export const OperationAlert = ({ operation }: IOperationBannerProps) => { +export const OperationAlert = ({ operation, onClose }: IOperationBannerProps) => { const [viewErrorExpanded, setViewErrorExpanded] = useState(false); const getOperationAction = () => { @@ -87,11 +92,22 @@ export const OperationAlert = ({ operation }: IOperationBannerProps) => { > {viewErrorExpanded ? : } + {onClose && ( + + )} ); } - return undefined; + return ( + onClose && ( + + ) + ); }; const renderOperationTime = () => { diff --git a/app/cdap/components/SourceControlManagement/OperationsHistoryView/index.tsx b/app/cdap/components/SourceControlManagement/OperationsHistoryView/index.tsx new file mode 100644 index 00000000000..0cb979fc433 --- /dev/null +++ b/app/cdap/components/SourceControlManagement/OperationsHistoryView/index.tsx @@ -0,0 +1,31 @@ +import React, { useEffect } from 'react'; +import { useSelector } from 'react-redux'; +import styled from 'styled-components'; +import { OperationAlert } from '../OperationAlert'; +import { fetchLatestOperation } from '../store/ActionCreator'; +import { getCurrentNamespace } from 'services/NamespaceStore'; + +const OperationsHistoryContainer = styled.div` + display: flex; + flex-direction: column; + align-items: stretch; + + padding: 16px; + gap: 8px; +`; + +export function OperationsHistoryView() { + const { allOperations } = useSelector(({ operationRun }) => operationRun); + + useEffect(() => { + fetchLatestOperation(getCurrentNamespace()); + }, []); + + return ( + + {allOperations.map((op) => ( + + ))} + + ); +} diff --git a/app/cdap/components/SourceControlManagement/RemotePipelineListView/RemotePipelineTable.tsx b/app/cdap/components/SourceControlManagement/RemotePipelineListView/RemotePipelineTable.tsx index 0e2292afd16..31db1ec13c4 100644 --- a/app/cdap/components/SourceControlManagement/RemotePipelineListView/RemotePipelineTable.tsx +++ b/app/cdap/components/SourceControlManagement/RemotePipelineListView/RemotePipelineTable.tsx @@ -52,6 +52,7 @@ interface IRepositoryPipelineTableProps { multiPullEnabled?: boolean; disabled?: boolean; syncStatusFilter?: TSyncStatusFilter; + lastOperationInfoShown?: boolean; } export const RemotePipelineTable = ({ @@ -61,6 +62,7 @@ export const RemotePipelineTable = ({ multiPullEnabled = false, disabled = false, syncStatusFilter = 'all', + lastOperationInfoShown = true, }: IRepositoryPipelineTableProps) => { const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(25); @@ -146,7 +148,7 @@ export const RemotePipelineTable = ({ }; return ( - +
diff --git a/app/cdap/components/SourceControlManagement/RemotePipelineListView/index.tsx b/app/cdap/components/SourceControlManagement/RemotePipelineListView/index.tsx index 2351771be72..54ea28930ba 100644 --- a/app/cdap/components/SourceControlManagement/RemotePipelineListView/index.tsx +++ b/app/cdap/components/SourceControlManagement/RemotePipelineListView/index.tsx @@ -41,6 +41,7 @@ import { setRemoteSyncStatusFilter, setSyncStatusOfAllPipelines, refetchAllPipelines, + dismissOperationAlert, } from '../store/ActionCreator'; import { LoadingAppLevel } from 'components/shared/LoadingAppLevel'; import { getCurrentNamespace } from 'services/NamespaceStore'; @@ -78,7 +79,7 @@ export const RemotePipelineListView = ({ syncStatusFilter, } = useSelector(({ pull }) => pull); - const { running: isAnOperationRunning, operation } = useSelector( + const { running: isAnOperationRunning, operation, showLastOperationInfo } = useSelector( ({ operationRun }) => operationRun ); @@ -173,6 +174,7 @@ export const RemotePipelineListView = ({ multiPullEnabled={multiPullEnabled} disabled={isAnOperationRunning} syncStatusFilter={syncStatusFilter} + lastOperationInfoShown={showLastOperationInfo} /> setPullViewErrorMsg()} /> - {operation && multiPullEnabled && } + {operation && multiPullEnabled && showLastOperationInfo && ( + + )} {selectedPipelines.length > 0 && ( diff --git a/app/cdap/components/SourceControlManagement/SearchBox.tsx b/app/cdap/components/SourceControlManagement/SearchBox.tsx index cf252fa8099..54fd227a37e 100644 --- a/app/cdap/components/SourceControlManagement/SearchBox.tsx +++ b/app/cdap/components/SourceControlManagement/SearchBox.tsx @@ -32,7 +32,7 @@ const StyledTextField = styled(TextField)` `; const StyledDiv = styled.div` - margin: 12px 0px; + margin: 8px 0 4px 0px; `; interface ISearchBoxProps { diff --git a/app/cdap/components/SourceControlManagement/SyncStatusFilters.tsx b/app/cdap/components/SourceControlManagement/SyncStatusFilters.tsx index 012e4f69eef..caceb8c47d0 100644 --- a/app/cdap/components/SourceControlManagement/SyncStatusFilters.tsx +++ b/app/cdap/components/SourceControlManagement/SyncStatusFilters.tsx @@ -23,7 +23,7 @@ import { Button, ButtonGroup } from '@material-ui/core'; const PREFIX = 'features.SourceControlManagement.table'; const StyledDiv = styled.div` - margin: 12px 0px; + margin: 4px 0px; display: flex; justify-content: flex-end; flex-grow: 1; diff --git a/app/cdap/components/SourceControlManagement/SyncTabs.tsx b/app/cdap/components/SourceControlManagement/SyncTabs.tsx index 91a937baa67..d5a17fd3073 100644 --- a/app/cdap/components/SourceControlManagement/SyncTabs.tsx +++ b/app/cdap/components/SourceControlManagement/SyncTabs.tsx @@ -21,23 +21,30 @@ import { LocalPipelineListView } from './LocalPipelineListView'; import styled from 'styled-components'; import T from 'i18n-react'; import { RemotePipelineListView } from './RemotePipelineListView'; -import { FeatureProvider } from 'services/react/providers/featureFlagProvider'; import { getNamespacePipelineList, getRemotePipelineList, setSyncStatusOfAllPipelines, } from './store/ActionCreator'; import { getCurrentNamespace } from 'services/NamespaceStore'; +import { useFeatureFlagDefaultFalse } from 'services/react/customHooks/useFeatureFlag'; +import { OperationsHistoryView } from './OperationsHistoryView'; const PREFIX = 'features.SourceControlManagement'; const StyledDiv = styled.div` - padding: 10px; - margin-top: 10px; + padding: 4px 10px; +`; + +const StyledTabs = styled(Tabs)` + border-bottom: 1px solid #e8e8e8; `; const ScmSyncTabs = () => { const [tabIndex, setTabIndex] = useState(0); + const multiPushEnabled = useFeatureFlagDefaultFalse( + 'source.control.management.multi.app.enabled' + ); const { ready: pushStateReady, nameFilter } = useSelector(({ push }) => push); useEffect(() => { @@ -69,10 +76,26 @@ const ScmSyncTabs = () => { } }; + const renderTabContent = () => { + if (tabIndex === 0) { + return ; + } + + if (tabIndex === 1) { + return ; + } + + if (tabIndex === 2) { + return ; + } + + return null; + }; + return ( <> - { > - + {multiPushEnabled && ( + + )} + - - - {tabIndex === 0 ? : } - - + {renderTabContent()} ); }; diff --git a/app/cdap/components/SourceControlManagement/helpers.ts b/app/cdap/components/SourceControlManagement/helpers.ts index e0647bf08f3..9dccfa22513 100644 --- a/app/cdap/components/SourceControlManagement/helpers.ts +++ b/app/cdap/components/SourceControlManagement/helpers.ts @@ -21,7 +21,6 @@ import { IResource, IOperationResource, IOperationRun, - ITimeInstant, IOperationError, IOperationResourceScopedErrorMessage, IRepositoryPipeline, @@ -76,7 +75,7 @@ export const getOperationRunMessage = (operation: IOperationRun) => { return T.translate(`${PREFIX}.syncSuccessMulti`); } - if (operation.status === OperationStatus.FAILED || operation.status === OperationStatus.KILLED) { + if (operation.status === OperationStatus.FAILED) { if (operation.type === OperationType.PUSH_APPS) { return T.translate(`${PREFIX}.push.pushFailureMulti`, { n }); } @@ -86,6 +85,16 @@ export const getOperationRunMessage = (operation: IOperationRun) => { return T.translate(`${PREFIX}.syncFailreMulti`); } + if (operation.status === OperationStatus.KILLED) { + if (operation.type === OperationType.PUSH_APPS) { + return T.translate(`${PREFIX}.push.pushKilledMulti`, { n }); + } + if (operation.type === OperationType.PULL_APPS) { + return T.translate(`${PREFIX}.pull.pullKilledMulti`, { n }); + } + return T.translate(`${PREFIX}.syncKilledMulti`); + } + if (operation.type === OperationType.PUSH_APPS) { return T.translate(`${PREFIX}.push.pushAppMessageMulti`, { n }); } @@ -104,10 +113,14 @@ export const getOperationStatusType = (operation: IOperationRun) => { return 'success'; } - if (operation.status === OperationStatus.FAILED || operation.status === OperationStatus.KILLED) { + if (operation.status === OperationStatus.FAILED) { return 'error'; } + if (operation.status === OperationStatus.KILLED) { + return 'warning'; + } + return 'info'; }; @@ -160,17 +173,6 @@ export const getOperationRunTime = (operation: IOperationRun): string => { return null; }; -export const getOperationRunTime = (operation: IOperationRun): string => { - if (operation.metadata?.createTime && operation.metadata?.endTime) { - return moment - .duration( - (operation.metadata?.endTime.seconds - operation.metadata?.createTime.seconds) * 1000 - ) - .humanize(); - } - return null; -}; - const getSyncStatusWeight = (syncStatus?: TSyncStatus): number => { if (syncStatus === undefined) { return 0; diff --git a/app/cdap/components/SourceControlManagement/index.tsx b/app/cdap/components/SourceControlManagement/index.tsx index 6f4f5229408..ca9146fdf8f 100644 --- a/app/cdap/components/SourceControlManagement/index.tsx +++ b/app/cdap/components/SourceControlManagement/index.tsx @@ -15,7 +15,7 @@ */ import { EntityTopPanel } from 'components/EntityTopPanel'; -import React from 'react'; +import React, { useEffect } from 'react'; import { Provider } from 'react-redux'; import SourceControlManagementSyncStore from './store'; import T from 'i18n-react'; @@ -24,11 +24,15 @@ import { useHistory } from 'react-router'; import { useOnUnmount } from 'services/react/customHooks/useOnUnmount'; import { reset, resetRemote } from './store/ActionCreator'; import ScmSyncTabs from './SyncTabs'; +import { useHideFooterInPage } from 'components/FooterContext'; +import { FeatureProvider } from 'services/react/providers/featureFlagProvider'; const PREFIX = 'features.SourceControlManagement'; const SourceControlManagementSyncView = () => { const history = useHistory(); + useHideFooterInPage(); + useOnUnmount(() => { resetRemote(); reset(); @@ -48,7 +52,9 @@ const SourceControlManagementSyncView = () => { history.push(closeAndBackLink); }} /> - + + + ); }; diff --git a/app/cdap/components/SourceControlManagement/store/ActionCreator.ts b/app/cdap/components/SourceControlManagement/store/ActionCreator.ts index 5b22c042578..ba30d4da3b9 100644 --- a/app/cdap/components/SourceControlManagement/store/ActionCreator.ts +++ b/app/cdap/components/SourceControlManagement/store/ActionCreator.ts @@ -434,11 +434,30 @@ export const resetRemote = () => { }); }; +const updateOperationsHistory = (operations: IOperationRun[]) => { + const currentHistory = [ + ...SourceControlManagementSyncStore.getState().operationRun.allOperations, + ]; + const operationIds = new Set(currentHistory.map((op) => op.id)); + for (const operation of operations) { + if (!operationIds.has(operation.id)) { + currentHistory.push(operation); + } + } + + currentHistory.sort((a, b) => compareTimeInstant(b.metadata.createTime, a.metadata.createTime)); + SourceControlManagementSyncStore.dispatch({ + type: OperationRunActions.setAllOperations, + payload: currentHistory, + }); +}; + export const setLatestOperation = (namespace: string, operation: IOperationRun) => { SourceControlManagementSyncStore.dispatch({ type: OperationRunActions.setLatestOperation, payload: operation, }); + updateOperationsHistory([operation]); if (operation.done) { return; @@ -450,7 +469,7 @@ export const setLatestOperation = (namespace: string, operation: IOperationRun) }).subscribe((res: IOperationRun) => { if (res.done) { pollOperationStatus.unsubscribe(); - + updateOperationsHistory([res]); SourceControlManagementSyncStore.dispatch({ type: OperationRunActions.setLatestOperation, payload: res, @@ -474,6 +493,8 @@ export const fetchLatestPullOperation = (namespace: string) => { if (res.operations.length < 1) { return; } + updateOperationsHistory(res.operations); + const operation = res.operations[0]; if ( !currentLatest || @@ -494,6 +515,8 @@ export const fetchLatestPushOperation = (namespace: string) => { if (res.operations.length < 1) { return; } + updateOperationsHistory(res.operations); + const operation = res.operations[0]; if ( !currentLatest || @@ -525,3 +548,10 @@ export const refetchAllPipelines = () => { getNamespacePipelineList(getCurrentNamespace()); getRemotePipelineList(getCurrentNamespace()); }; + +export const dismissOperationAlert = () => { + SourceControlManagementSyncStore.dispatch({ + type: OperationRunActions.setShowLastOperationInfo, + payload: false, + }); +}; diff --git a/app/cdap/components/SourceControlManagement/store/index.ts b/app/cdap/components/SourceControlManagement/store/index.ts index 34ba8558e68..047d132651c 100644 --- a/app/cdap/components/SourceControlManagement/store/index.ts +++ b/app/cdap/components/SourceControlManagement/store/index.ts @@ -44,6 +44,8 @@ interface IPullViewState { interface IOperationRunState { running: boolean; operation?: IOperationRun; + showLastOperationInfo: boolean; + allOperations: IOperationRun[]; } interface IStore { @@ -79,6 +81,8 @@ export const PullFromGitActions = { export const OperationRunActions = { setLatestOperation: 'SET_RUNNING_OPERATION', unsetLatestOperation: 'UNSET_RUNNING_OPERATION', + setShowLastOperationInfo: 'SET_SHOW_LAST_OPERATION_INFO', + setAllOperations: 'SET_ALL_OPERATIONS', }; const defaultPushViewState: IPushViewState = { @@ -105,6 +109,8 @@ const defaultPullViewState: IPullViewState = { const defaultOperationRunState: IOperationRunState = { running: false, + showLastOperationInfo: true, + allOperations: [], }; const push = (state = defaultPushViewState, action: IAction) => { @@ -216,12 +222,26 @@ const operationRun = ( switch (action.type) { case OperationRunActions.setLatestOperation: return { + ...state, running: !action.payload?.done, operation: action.payload, + showLastOperationInfo: true, }; case OperationRunActions.unsetLatestOperation: return { + ...state, running: false, + showLastOperationInfo: false, + }; + case OperationRunActions.setShowLastOperationInfo: + return { + ...state, + showLastOperationInfo: action.payload, + }; + case OperationRunActions.setAllOperations: + return { + ...state, + allOperations: action.payload, }; default: return state; diff --git a/app/cdap/components/SourceControlManagement/styles.ts b/app/cdap/components/SourceControlManagement/styles.ts index e84ee62d622..0baf7cc0039 100644 --- a/app/cdap/components/SourceControlManagement/styles.ts +++ b/app/cdap/components/SourceControlManagement/styles.ts @@ -22,8 +22,8 @@ export const TableBox = styled(TableContainer)` box-shadow: 0 1px 2px 0 rgb(0 0 0 / 20%); margin-top: 10px; margin-bottom: 30px; - max-height: calc(80vh - 200px); - min-height: calc(80vh - 300px); + max-height: calc(80vh - ${(props) => (props.lastOperationInfoShown ? '280px' : '200px')}); + min-height: calc(80vh - ${(props) => (props.lastOperationInfoShown ? '380px' : '200px')}); `; export const StyledTableCell = styled(TableCell)` diff --git a/app/cdap/main.js b/app/cdap/main.js index 384fce67502..807d15e119b 100644 --- a/app/cdap/main.js +++ b/app/cdap/main.js @@ -19,7 +19,7 @@ import 'react-hot-loader/patch'; import './globals'; import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; -import React, { Component } from 'react'; +import React, { Component, useEffect, useState } from 'react'; import { Route, Router, Switch } from 'react-router-dom'; import SessionTokenStore, { fetchSessionToken } from 'services/SessionTokenStore'; import { Theme, applyTheme } from 'services/ThemeHelper'; @@ -65,6 +65,7 @@ import history from 'services/history'; import { CookieBanner } from 'components/CookieBanner'; // See ./graphql/fragements/README.md import introspectionQueryResultData from '../../graphql/fragments/fragmentTypes.json'; +import { FooterContext } from 'components/FooterContext'; require('../ui-utils/url-generator'); require('font-awesome-sass-loader!./styles/font-awesome.config.js'); @@ -409,7 +410,7 @@ class CDAP extends Component { ))} )} -
+ {this.props.showFooter &&
} @@ -420,10 +421,34 @@ class CDAP extends Component { CDAP.propTypes = { children: PropTypes.node, + showFooter: PropTypes.bool, }; +CDAP.defaultProps = { + showFooter: true, +}; + +function CdapWithFooterContext() { + const [show, setShow] = useState(true); + + useEffect(() => { + const appContainer = document.getElementById('app-container'); + if (!show) { + appContainer.classList.add('no-footer'); + } else { + appContainer.classList.remove('no-footer'); + } + }, [show]); + + return ( + + + + ); +} + const RootComp = hot(() => { - return } />; + return } />; }); ReactDOM.render(, document.getElementById('app-container')); diff --git a/app/cdap/styles/common.scss b/app/cdap/styles/common.scss index 8bf1f6dae50..049ef9d7f1d 100644 --- a/app/cdap/styles/common.scss +++ b/app/cdap/styles/common.scss @@ -60,6 +60,10 @@ body { margin-top: 48px; overflow-y: auto; height: calc(100vh - (#{$height-of-footer} + #{$height-of-header})); // Header + footer heights + + &.no-footer { + height: calc(100vh - #{$height-of-header}); + } } .container-fluid { padding-bottom: 52px; // footer height diff --git a/app/cdap/text/text-en.yaml b/app/cdap/text/text-en.yaml index 4198c21628a..8e0e52e2b3a 100644 --- a/app/cdap/text/text-en.yaml +++ b/app/cdap/text/text-en.yaml @@ -3234,6 +3234,7 @@ features: syncAppMessageMulti: Syncing pipelines with the remote repository now... syncSuccessMulti: Successfully synced {n} pipelines with the remote repository. syncFailureMulti: Failed to sync {n} pipelines with the remote repository. + syncKilledMulti: Cancelled syncing of {n} pipelines with the remote repository. pipelineSyncMessage: Syncing 1 pipeline with the remote repository pipelineSyncedSuccess: Successfully synced 1 pipeline with the remote repository pipelineSyncedFail: Failed to sync 1 pipeline with the remote repository @@ -3241,6 +3242,8 @@ features: operationStartedAt: Started at {startTime} operationRanFor: Started at {startTime}, Completed in {timeTaken} operationRunningFor: Started at {startTime}, Running for {timeTaken} + operationHistory: + tab: Operations history pull: emptyPipelineListMessage: There are no pipelines in the remote repository or no pipelines matching the search query "{query}" modalTitle: Pull pipeline from remote repository @@ -3257,6 +3260,7 @@ features: pullSuccess: Pipeline {pipelineName} updated pullSuccessMulti: Successfully pulled {n} pipelines from the remote repository. pullFailureMulti: Failed to pull {n} pipelines from the remote repository. + pullKilledMulti: Cancelled pulling {n} pipelines from the remote repository. tab: Repository pipelines upToDate: Pipeline is already up to date. stopOperation: STOP @@ -3277,6 +3281,7 @@ features: pushSuccess: Successfully pushed pipeline {pipelineName} pushSuccessMulti: Successfully pushed {n} pipelines to the remote repository. pushFailureMulti: Failed to push {n} pipelines to the remote repository. + pushKilledMulti: Cancelled pushing {n} pipelines to the remote repository. searchLabel: Search by batch pipeline name tab: Namespace pipelines stopOperation: STOP diff --git a/package.json b/package.json index bb86536be18..bab5537e137 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ }, "devDependencies": { "@babel/core": "7.7.4", + "@babel/helper-string-parser": "^7.23.4", "@babel/plugin-proposal-class-properties": "7.7.4", "@babel/plugin-proposal-decorators": "7.7.4", "@babel/plugin-proposal-export-namespace-from": "7.7.4", @@ -159,6 +160,46 @@ "@ajainarayanan/react-pan-zoom": "0.0.4", "@apollo/react-hooks": "3.1.3", "@babel/polyfill": "7.0.0", + "@bower_components/ace-builds": "ajaxorg/ace-builds#^1", + "@bower_components/angular": "angular/bower-angular#1.4.3", + "@bower_components/angular-animate": "angular/bower-angular-animate#1.4.3", + "@bower_components/angular-bootstrap": "angular-ui/bootstrap-bower#1.1.2", + "@bower_components/angular-breadcrumb": "ncuillery/angular-breadcrumb#0.3.3", + "@bower_components/angular-cookies": "angular/bower-angular-cookies#1.4.3", + "@bower_components/angular-cron-jobs": "jacobscarter/angular-cron-jobs#1.4.1", + "@bower_components/angular-file-saver": "elquimeras/ng-file-saver#1.1.4", + "@bower_components/angular-gridster": "ManifestWebDesign/angular-gridster#0.13.5", + "@bower_components/angular-inview": "thenikso/angular-inview#1.5.7", + "@bower_components/angular-loading-bar": "chieffancypants/angular-loading-bar#0.8.0", + "@bower_components/angular-marked": "Hypercubed/angular-marked#1.0.1", + "@bower_components/angular-mocks": "angular/bower-angular-mocks#1.4.3", + "@bower_components/angular-moment": "urish/angular-moment#0.10.3", + "@bower_components/angular-motion": "mgcrea/angular-motion#0.4.3", + "@bower_components/angular-resource": "angular/bower-angular-resource#1.4.3", + "@bower_components/angular-sanitize": "angular/bower-angular-sanitize#1.4.3", + "@bower_components/angular-strap": "mgcrea/angular-strap#2.3.10", + "@bower_components/angular-ui-ace": "angular-ui/ui-ace#bower", + "@bower_components/angular-ui-router": "angular-ui/angular-ui-router-bower#0.3.0", + "@bower_components/blob-polyfill": "bjornstar/blob-polyfill#~1.0.20150320", + "@bower_components/bootstrap": "twbs/bootstrap#3.3.6", + "@bower_components/d3": "mbostock-bower/d3-bower#3.5.5", + "@bower_components/d3-timeline": "jiahuang/d3-timeline#0.0.5", + "@bower_components/d3-tip": "Caged/d3-tip#0.6.7", + "@bower_components/dagre": "cpettitt/dagre#^0.7.3", + "@bower_components/dagre-d3": "cpettitt/dagre-d3#0.4.10", + "@bower_components/esprima": "ariya/esprima#2.0.0", + "@bower_components/file-saver": "eligrey/FileSaver.js#^2.0.4", + "@bower_components/graphlib": "cpettitt/graphlib#^1.0.5", + "@bower_components/javascript-detect-element-resize": "sdecima/javascript-detect-element-resize#~0.5.1", + "@bower_components/jquery": "jquery/jquery-dist#1.9.1 - 2", + "@bower_components/js-beautify": "einars/js-beautify#1.6.2", + "@bower_components/jsPlumb": "sporritt/jsPlumb#2.0.6", + "@bower_components/lodash": "lodash/lodash#^3.10.0", + "@bower_components/marked": "chjj/marked#~0.3.1", + "@bower_components/moment": "moment/moment#>=2.8.0 <2.11.0", + "@bower_components/ngInfiniteScroll": "ng-infinite-scroll/ng-infinite-scroll-bower#1.2.1", + "@bower_components/ngstorage": "gsklee/ngStorage#0.3.10", + "@bower_components/node-uuid": "broofa/node-uuid#1.4.3", "@hot-loader/react-dom": "16.13.0", "@material-ui/core": "^4.12.3", "@material-ui/icons": "^4.9.1", @@ -258,47 +299,7 @@ "vega-lite": "2.0.0-beta.16", "vega-tooltip": "0.4.3", "webpack-dev-server": "3.11.0", - "yml-loader": "2.1.0", - "@bower_components/ace-builds": "ajaxorg/ace-builds#^1", - "@bower_components/angular": "angular/bower-angular#1.4.3", - "@bower_components/angular-animate": "angular/bower-angular-animate#1.4.3", - "@bower_components/angular-bootstrap": "angular-ui/bootstrap-bower#1.1.2", - "@bower_components/angular-breadcrumb": "ncuillery/angular-breadcrumb#0.3.3", - "@bower_components/angular-cookies": "angular/bower-angular-cookies#1.4.3", - "@bower_components/angular-cron-jobs": "jacobscarter/angular-cron-jobs#1.4.1", - "@bower_components/angular-file-saver": "elquimeras/ng-file-saver#1.1.4", - "@bower_components/angular-gridster": "ManifestWebDesign/angular-gridster#0.13.5", - "@bower_components/angular-inview": "thenikso/angular-inview#1.5.7", - "@bower_components/angular-loading-bar": "chieffancypants/angular-loading-bar#0.8.0", - "@bower_components/angular-marked": "Hypercubed/angular-marked#1.0.1", - "@bower_components/angular-mocks": "angular/bower-angular-mocks#1.4.3", - "@bower_components/angular-moment": "urish/angular-moment#0.10.3", - "@bower_components/angular-motion": "mgcrea/angular-motion#0.4.3", - "@bower_components/angular-resource": "angular/bower-angular-resource#1.4.3", - "@bower_components/angular-sanitize": "angular/bower-angular-sanitize#1.4.3", - "@bower_components/angular-strap": "mgcrea/angular-strap#2.3.10", - "@bower_components/angular-ui-ace": "angular-ui/ui-ace#bower", - "@bower_components/angular-ui-router": "angular-ui/angular-ui-router-bower#0.3.0", - "@bower_components/blob-polyfill": "bjornstar/blob-polyfill#~1.0.20150320", - "@bower_components/bootstrap": "twbs/bootstrap#3.3.6", - "@bower_components/d3": "mbostock-bower/d3-bower#3.5.5", - "@bower_components/d3-timeline": "jiahuang/d3-timeline#0.0.5", - "@bower_components/d3-tip": "Caged/d3-tip#0.6.7", - "@bower_components/dagre": "cpettitt/dagre#^0.7.3", - "@bower_components/dagre-d3": "cpettitt/dagre-d3#0.4.10", - "@bower_components/esprima": "ariya/esprima#2.0.0", - "@bower_components/file-saver": "eligrey/FileSaver.js#^2.0.4", - "@bower_components/graphlib": "cpettitt/graphlib#^1.0.5", - "@bower_components/javascript-detect-element-resize": "sdecima/javascript-detect-element-resize#~0.5.1", - "@bower_components/jquery": "jquery/jquery-dist#1.9.1 - 2", - "@bower_components/js-beautify": "einars/js-beautify#1.6.2", - "@bower_components/jsPlumb": "sporritt/jsPlumb#2.0.6", - "@bower_components/lodash": "lodash/lodash#^3.10.0", - "@bower_components/marked": "chjj/marked#~0.3.1", - "@bower_components/moment": "moment/moment#>=2.8.0 <2.11.0", - "@bower_components/ngInfiniteScroll": "ng-infinite-scroll/ng-infinite-scroll-bower#1.2.1", - "@bower_components/ngstorage": "gsklee/ngStorage#0.3.10", - "@bower_components/node-uuid": "broofa/node-uuid#1.4.3" + "yml-loader": "2.1.0" }, "resolutions": { "**/cypress": "5.6.0" diff --git a/yarn.lock b/yarn.lock index 7dd77b49502..91d0d07d6bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -611,6 +611,11 @@ dependencies: "@babel/types" "^7.15.4" +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== + "@babel/helper-validator-identifier@^7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed"