diff --git a/src/components/app/list-new/AppList.tsx b/src/components/app/list-new/AppList.tsx index 3598f94efe..a9a2c77540 100644 --- a/src/components/app/list-new/AppList.tsx +++ b/src/components/app/list-new/AppList.tsx @@ -1,50 +1,52 @@ -import React, {useState, useEffect, useContext} from 'react'; -import {useLocation, useHistory, useParams} from 'react-router'; -import { Link, Switch, Route, NavLink } from 'react-router-dom'; -import {Progressing, Filter, showError, FilterOption, Modal, ErrorScreenManager, handleUTCTime } from '../../common'; -import {ReactComponent as Search} from '../../../assets/icons/ic-search.svg'; -import {ReactComponent as ChartIcon} from '../../../assets/icons/ic-charts.svg'; -import {ReactComponent as AddIcon} from '../../../assets/icons/ic-add.svg'; -import InstallDevtronFullImage from '../../../assets/img/install-devtron-full@2x.png'; -import EmptyState from '../../EmptyState/EmptyState'; -import {getInitData, buildClusterVsNamespace, getNamespaces} from './AppListService' -import {ServerErrors} from '../../../modals/commonTypes'; -import {AppListViewType} from '../config'; -import {URLS, AppListConstants, SERVER_MODE, DOCUMENTATION} from '../../../config'; -import {ReactComponent as Clear} from '../../../assets/icons/ic-error.svg'; -import DevtronAppListContainer from '../list/DevtronAppListContainer'; -import HelmAppList from './HelmAppList'; -import * as queryString from 'query-string'; -import { OrderBy, SortBy } from '../list/types'; -import { AddNewApp } from '../create/CreateApp'; -import { mainContext } from '../../common/navigation/NavigationRoutes'; -import '../list/list.css'; -import EAEmptyState, { EAEmptyStateType } from '../../common/eaEmptyState/EAEmptyState'; +import React, { useState, useEffect, useContext, Fragment } from 'react' +import { useLocation, useHistory, useParams } from 'react-router' +import { Link, Switch, Route, NavLink } from 'react-router-dom' +import { Progressing, Filter, showError, FilterOption, Modal, ErrorScreenManager, handleUTCTime } from '../../common' +import { ReactComponent as Search } from '../../../assets/icons/ic-search.svg' +import { ReactComponent as ChartIcon } from '../../../assets/icons/ic-charts.svg' +import { ReactComponent as AddIcon } from '../../../assets/icons/ic-add.svg' +import InstallDevtronFullImage from '../../../assets/img/install-devtron-full@2x.png' +import EmptyState from '../../EmptyState/EmptyState' +import { getInitData, buildClusterVsNamespace, getNamespaces } from './AppListService' +import { ServerErrors } from '../../../modals/commonTypes' +import { AppListViewType } from '../config' +import { URLS, AppListConstants, SERVER_MODE, DOCUMENTATION } from '../../../config' +import { ReactComponent as Clear } from '../../../assets/icons/ic-error.svg' +import DevtronAppListContainer from '../list/DevtronAppListContainer' +import HelmAppList from './HelmAppList' +import * as queryString from 'query-string' +import { OrderBy, SortBy } from '../list/types' +import { AddNewApp } from '../create/CreateApp' +import { mainContext } from '../../common/navigation/NavigationRoutes' +import '../list/list.css' +import EAEmptyState, { EAEmptyStateType } from '../../common/eaEmptyState/EAEmptyState' +import PageHeader from '../../common/header/PageHeader' +import { ReactComponent as DropDown } from '../../../assets/icons/ic-dropdown-filled.svg' export default function AppList() { - const location = useLocation(); - const history = useHistory(); - const params = useParams<{ appType: string}>(); - const {serverMode, setPageOverflowEnabled} = useContext(mainContext); - const [dataStateType, setDataStateType] = useState(AppListViewType.LOADING); - const [errorResponseCode, setErrorResponseCode] = useState(0); - const [lastDataSyncTimeString, setLastDataSyncTimeString] = useState(""); - const [lastDataSync, setLastDataSync] = useState(false); - const [fetchingNamespaces, setFetchingNamespaces] = useState(false); - const [fetchingNamespacesErrored, setFetchingNamespacesErrored] = useState(false); - - const [parsedPayloadOnUrlChange, setParsedPayloadOnUrlChange] = useState({}); - const [currentTab, setCurrentTab] = useState(undefined); - const [showCreateNewAppSelectionModal, setShowCreateNewAppSelectionModal] = useState(false); + const location = useLocation() + const history = useHistory() + const params = useParams<{ appType: string }>() + const { serverMode, setPageOverflowEnabled } = useContext(mainContext) + const [dataStateType, setDataStateType] = useState(AppListViewType.LOADING) + const [errorResponseCode, setErrorResponseCode] = useState(0) + const [lastDataSyncTimeString, setLastDataSyncTimeString] = useState('') + const [lastDataSync, setLastDataSync] = useState(false) + const [fetchingNamespaces, setFetchingNamespaces] = useState(false) + const [fetchingNamespacesErrored, setFetchingNamespacesErrored] = useState(false) + + const [parsedPayloadOnUrlChange, setParsedPayloadOnUrlChange] = useState({}) + const [currentTab, setCurrentTab] = useState(undefined) + const [showCreateNewAppSelectionModal, setShowCreateNewAppSelectionModal] = useState(false) // API master data - const [appCheckListRes, setAppCheckListRes] = useState({}); - const [projectListRes, setProjectListRes] = useState({result : []}); - const [environmentListRes, setEnvironmentListRes] = useState({result : []}); + const [appCheckListRes, setAppCheckListRes] = useState({}) + const [projectListRes, setProjectListRes] = useState({ result: [] }) + const [environmentListRes, setEnvironmentListRes] = useState({ result: [] }) // search - const [searchString, setSearchString] = useState(undefined); - const [searchApplied, setSearchApplied] = useState(false); + const [searchString, setSearchString] = useState(undefined) + const [searchApplied, setSearchApplied] = useState(false) // filters const [masterFilters, setMasterFilters] = useState({ @@ -52,111 +54,120 @@ export default function AppList() { environments: [], clusters: [], namespaces: [], - }); - const [showPulsatingDot, setShowPulsatingDot] = useState(false); - const [fetchingExternalApps, setFetchingExternalApps] = useState(false); - + }) + const [showPulsatingDot, setShowPulsatingDot] = useState(false) + const [fetchingExternalApps, setFetchingExternalApps] = useState(false) // on page load useEffect(() => { - let _currentTab = params.appType == AppListConstants.AppType.DEVTRON_APPS ? AppListConstants.AppTabs.DEVTRON_APPS : AppListConstants.AppTabs.HELM_APPS; - setCurrentTab(_currentTab); + let _currentTab = + params.appType == AppListConstants.AppType.DEVTRON_APPS + ? AppListConstants.AppTabs.DEVTRON_APPS + : AppListConstants.AppTabs.HELM_APPS + setCurrentTab(_currentTab) // set search data - let searchQuery = location.search; - let queryParams = queryString.parse(searchQuery); - if (queryParams.search){ - setSearchString(queryParams.search); - setSearchApplied(true); + let searchQuery = location.search + let queryParams = queryString.parse(searchQuery) + if (queryParams.search) { + setSearchString(queryParams.search) + setSearchApplied(true) } // set payload parsed from url - let payloadParsedFromUrl = onRequestUrlChange(); - setParsedPayloadOnUrlChange(payloadParsedFromUrl); + let payloadParsedFromUrl = onRequestUrlChange() + setParsedPayloadOnUrlChange(payloadParsedFromUrl) // fetch master filters data and some master data - getInitData(payloadParsedFromUrl, serverMode).then((initData) => { - setAppCheckListRes(initData.appCheckListRes); - setProjectListRes(initData.projectsRes); - setEnvironmentListRes(initData.environmentListRes); - setMasterFilters(initData.filters); - setDataStateType(AppListViewType.LIST); - if(serverMode == SERVER_MODE.EA_ONLY){ - applyClusterSelectionFilterOnPageLoadIfSingle(initData.filters.clusters, _currentTab); - } - }).catch((errors: ServerErrors) => { - showError(errors); - setDataStateType(AppListViewType.ERROR); - setErrorResponseCode(errors.code); - }) + getInitData(payloadParsedFromUrl, serverMode) + .then((initData) => { + setAppCheckListRes(initData.appCheckListRes) + setProjectListRes(initData.projectsRes) + setEnvironmentListRes(initData.environmentListRes) + setMasterFilters(initData.filters) + setDataStateType(AppListViewType.LIST) + if (serverMode == SERVER_MODE.EA_ONLY) { + applyClusterSelectionFilterOnPageLoadIfSingle(initData.filters.clusters, _currentTab) + } + }) + .catch((errors: ServerErrors) => { + showError(errors) + setDataStateType(AppListViewType.ERROR) + setErrorResponseCode(errors.code) + }) }, []) - // update lasy sync time on tab change useEffect(() => { - const _lastDataSyncTime = Date(); - setLastDataSyncTimeString("Last synced " + handleUTCTime(_lastDataSyncTime, true)); + const _lastDataSyncTime = Date() + setLastDataSyncTimeString('Last synced ' + handleUTCTime(_lastDataSyncTime, true)) const interval = setInterval(() => { - setLastDataSyncTimeString("Last synced " + handleUTCTime(_lastDataSyncTime, true)); - }, 1000); + setLastDataSyncTimeString('Last synced ' + handleUTCTime(_lastDataSyncTime, true)) + }, 1000) return () => { - clearInterval(interval); - }; + clearInterval(interval) + } }, [lastDataSync]) - useEffect(() => { - setParsedPayloadOnUrlChange(onRequestUrlChange()); + setParsedPayloadOnUrlChange(onRequestUrlChange()) }, [location.search]) - - const applyClusterSelectionFilterOnPageLoadIfSingle = (clusterFilters : any[], currentTab : string) : void => { + const applyClusterSelectionFilterOnPageLoadIfSingle = (clusterFilters: any[], currentTab: string): void => { // return if not single cluster - if(clusterFilters.length != 1) { - return; + if (clusterFilters.length != 1) { + return } let _cluster = clusterFilters[0] // return if any cluster filter applied - let _isAnyClusterFilterApplied = clusterFilters.some(_cluster => _cluster.isChecked); - if(_isAnyClusterFilterApplied){ - return; + let _isAnyClusterFilterApplied = clusterFilters.some((_cluster) => _cluster.isChecked) + if (_isAnyClusterFilterApplied) { + return } // auto check cluster - let _filterOptions : FilterOption[] = []; + let _filterOptions: FilterOption[] = [] _filterOptions.push({ - key : _cluster.key, - label : _cluster.label, - isSaved : _cluster.isSaved, - isChecked : true + key: _cluster.key, + label: _cluster.label, + isSaved: _cluster.isSaved, + isChecked: true, }) - applyFilter(AppListConstants.FilterType.CLUTSER, _filterOptions, currentTab); + applyFilter(AppListConstants.FilterType.CLUTSER, _filterOptions, currentTab) } - const onRequestUrlChange = () : any => { - let searchQuery = location.search; - - let params = queryString.parse(searchQuery); - let search = params.search || ""; - let environments = params.environment || ""; - let teams = params.team || ""; - let clustersAndNamespaces = params.namespace || ""; - - let _clusterVsNamespaceMap = buildClusterVsNamespace(clustersAndNamespaces); - let environmentsArr = environments.toString().split(",").map(env => +env).filter(item => item != 0); - let teamsArr = teams.toString().split(",").filter(team => team != '').map(team => Number(team)); + const onRequestUrlChange = (): any => { + let searchQuery = location.search + + let params = queryString.parse(searchQuery) + let search = params.search || '' + let environments = params.environment || '' + let teams = params.team || '' + let clustersAndNamespaces = params.namespace || '' + + let _clusterVsNamespaceMap = buildClusterVsNamespace(clustersAndNamespaces) + let environmentsArr = environments + .toString() + .split(',') + .map((env) => +env) + .filter((item) => item != 0) + let teamsArr = teams + .toString() + .split(',') + .filter((team) => team != '') + .map((team) => Number(team)) ////// update master filters data (check/uncheck) let filterApplied = { environments: new Set(environmentsArr), teams: new Set(teamsArr), - clusterVsNamespaceMap : _clusterVsNamespaceMap + clusterVsNamespaceMap: _clusterVsNamespaceMap, } - let _masterFilters = { projects: [], environments: [], clusters: [], namespaces: [] }; + let _masterFilters = { projects: [], environments: [], clusters: [], namespaces: [] } // set projects (check/uncheck) _masterFilters.projects = masterFilters.projects.map((project) => { @@ -164,7 +175,7 @@ export default function AppList() { key: project.key, label: project.label, isSaved: true, - isChecked: filterApplied.teams.has(project.key) + isChecked: filterApplied.teams.has(project.key), } }) @@ -174,7 +185,7 @@ export default function AppList() { key: cluster.key, label: cluster.label, isSaved: true, - isChecked: filterApplied.clusterVsNamespaceMap.has(cluster.key.toString()) + isChecked: filterApplied.clusterVsNamespaceMap.has(cluster.key.toString()), } }) @@ -184,11 +195,17 @@ export default function AppList() { key: namespace.key, label: namespace.label, isSaved: true, - isChecked: filterApplied.clusterVsNamespaceMap.has(namespace.clusterId.toString()) && filterApplied.clusterVsNamespaceMap.get(namespace.clusterId.toString()).includes(namespace.key.split("_")[1]), - toShow : filterApplied.clusterVsNamespaceMap.size == 0 || filterApplied.clusterVsNamespaceMap.has(namespace.clusterId.toString()), + isChecked: + filterApplied.clusterVsNamespaceMap.has(namespace.clusterId.toString()) && + filterApplied.clusterVsNamespaceMap + .get(namespace.clusterId.toString()) + .includes(namespace.key.split('_')[1]), + toShow: + filterApplied.clusterVsNamespaceMap.size == 0 || + filterApplied.clusterVsNamespaceMap.has(namespace.clusterId.toString()), actualName: namespace.actualName, - clusterName : namespace.clusterName, - clusterId : namespace.clusterId + clusterName: namespace.clusterName, + clusterId: namespace.clusterId, } }) @@ -198,34 +215,39 @@ export default function AppList() { key: env.key, label: env.label, isSaved: true, - isChecked: filterApplied.environments.has(env.key) + isChecked: filterApplied.environments.has(env.key), } }) - setMasterFilters(_masterFilters); + setMasterFilters(_masterFilters) ////// update master filters data ends (check/uncheck) + let sortBy = params.orderBy || SortBy.APP_NAME + let sortOrder = params.sortOrder || OrderBy.ASC + let offset = +params.offset || 0 + let hOffset = +params.hOffset || 0 + let pageSize: number = +params.pageSize || 20 + let pageSizes = new Set([20, 40, 50]) - let sortBy = params.orderBy || SortBy.APP_NAME; - let sortOrder = params.sortOrder || OrderBy.ASC; - let offset = +params.offset || 0; - let hOffset = +params.hOffset || 0; - let pageSize: number = +params.pageSize || 20; - let pageSizes = new Set([20, 40, 50]); - - if (!pageSizes.has(pageSize)) { //handle invalid pageSize - pageSize = 20; + if (!pageSizes.has(pageSize)) { + //handle invalid pageSize + pageSize = 20 } - if ((offset % pageSize != 0)) { //pageSize must be a multiple of offset - offset = 0; + if (offset % pageSize != 0) { + //pageSize must be a multiple of offset + offset = 0 } - if ((hOffset % pageSize != 0)) { //pageSize must be a multiple of offset - hOffset = 0; + if (hOffset % pageSize != 0) { + //pageSize must be a multiple of offset + hOffset = 0 } let payload = { environments: environmentsArr, teams: teamsArr, - namespaces : clustersAndNamespaces.toString().split(",").filter(item => item != ""), + namespaces: clustersAndNamespaces + .toString() + .split(',') + .filter((item) => item != ''), appNameSearch: search, sortBy: sortBy, sortOrder: sortOrder, @@ -236,101 +258,106 @@ export default function AppList() { // check whether to fetch namespaces from backend if any cluster is selected and not same as old // do it only for non page load, as on pageload getInitData is handling this logic - if (dataStateType == AppListViewType.LIST){ - let _oldClusterIdsCsv = _getClusterIdsFromRequestUrl(parsedPayloadOnUrlChange); - let _newClusterIdsCsv = _getClusterIdsFromRequestUrl(payload); - if(_newClusterIdsCsv) { + if (dataStateType == AppListViewType.LIST) { + let _oldClusterIdsCsv = _getClusterIdsFromRequestUrl(parsedPayloadOnUrlChange) + let _newClusterIdsCsv = _getClusterIdsFromRequestUrl(payload) + if (_newClusterIdsCsv) { // check if cluster selection is changed - if(_oldClusterIdsCsv != _newClusterIdsCsv) { + if (_oldClusterIdsCsv != _newClusterIdsCsv) { // fetch namespaces _fetchAndSetNamespaces(payload, _newClusterIdsCsv, _masterFilters) } - }else{ + } else { // if all clusters are unselected, then reset namespaces - _masterFilters.namespaces = []; - setMasterFilters(_masterFilters); + _masterFilters.namespaces = [] + setMasterFilters(_masterFilters) } } - return payload; + return payload } const _forceFetchAndSetNamespaces = () => { - let _clusterIdsCsv = _getClusterIdsFromRequestUrl(parsedPayloadOnUrlChange); + let _clusterIdsCsv = _getClusterIdsFromRequestUrl(parsedPayloadOnUrlChange) _fetchAndSetNamespaces(parsedPayloadOnUrlChange, _clusterIdsCsv, masterFilters) } - const _fetchAndSetNamespaces = (_parsedPayloadOnUrlChange : any, _clusterIdsCsv : string, _masterFilters : any) => { + const _fetchAndSetNamespaces = (_parsedPayloadOnUrlChange: any, _clusterIdsCsv: string, _masterFilters: any) => { // fetch namespaces - setFetchingNamespaces(true); - setFetchingNamespacesErrored(false); - let _clusterVsNamespaceMap = buildClusterVsNamespace(_parsedPayloadOnUrlChange.namespaces.join(',')); - getNamespaces(_clusterIdsCsv, _clusterVsNamespaceMap).then((_namespaces) => { - _masterFilters.namespaces = _namespaces; - setMasterFilters(_masterFilters); - setFetchingNamespaces(false); - setFetchingNamespacesErrored(false); - }).catch((errors: ServerErrors) => { - setFetchingNamespaces(false); - setFetchingNamespacesErrored(true); - }) + setFetchingNamespaces(true) + setFetchingNamespacesErrored(false) + let _clusterVsNamespaceMap = buildClusterVsNamespace(_parsedPayloadOnUrlChange.namespaces.join(',')) + getNamespaces(_clusterIdsCsv, _clusterVsNamespaceMap) + .then((_namespaces) => { + _masterFilters.namespaces = _namespaces + setMasterFilters(_masterFilters) + setFetchingNamespaces(false) + setFetchingNamespacesErrored(false) + }) + .catch((errors: ServerErrors) => { + setFetchingNamespaces(false) + setFetchingNamespacesErrored(true) + }) } - const _getClusterIdsFromRequestUrl = (parsedPayload : any) : string => { - let _namespaces = parsedPayload["namespaces"] || []; - return [...buildClusterVsNamespace(_namespaces.join(',')).keys()].join(','); + const _getClusterIdsFromRequestUrl = (parsedPayload: any): string => { + let _namespaces = parsedPayload['namespaces'] || [] + return [...buildClusterVsNamespace(_namespaces.join(',')).keys()].join(',') } - const buildDevtronAppListUrl = () : string => { - return `${URLS.APP}/${URLS.APP_LIST}/${URLS.APP_LIST_DEVTRON}`; + const buildDevtronAppListUrl = (): string => { + return `${URLS.APP}/${URLS.APP_LIST}/${URLS.APP_LIST_DEVTRON}` } - const buildHelmAppListUrl = () : string => { - return `${URLS.APP}/${URLS.APP_LIST}/${URLS.APP_LIST_HELM}`; + const buildHelmAppListUrl = (): string => { + return `${URLS.APP}/${URLS.APP_LIST}/${URLS.APP_LIST_HELM}` } function openDevtronAppCreateModel(event: React.MouseEvent) { - let _prefix = (currentTab == AppListConstants.AppTabs.DEVTRON_APPS ? buildDevtronAppListUrl() : buildHelmAppListUrl()); + let _prefix = + currentTab == AppListConstants.AppTabs.DEVTRON_APPS ? buildDevtronAppListUrl() : buildHelmAppListUrl() let url = `${_prefix}/${AppListConstants.CREATE_DEVTRON_APP_URL}${location.search}` - history.push(`${url}`); + history.push(`${url}`) } function redirectToHelmAppDiscover(event: React.MouseEvent) { let url = `${URLS.CHARTS_DISCOVER}` - history.push(`${url}`); + history.push(`${url}`) } - const redirectToAppDetails = (appId : string | number, envId: number): string => { + const redirectToAppDetails = (appId: string | number, envId: number): string => { if (envId) { - return `/app/${appId}/details/${envId}`; + return `/app/${appId}/details/${envId}` } - return `/app/${appId}/trigger`; + return `/app/${appId}/trigger` } - const updateLastDataSync = () : void => { - setLastDataSync(!lastDataSync); + const updateLastDataSync = (): void => { + setLastDataSync(!lastDataSync) } - const handleAppSearchOperation = (_searchString : string): void => { - let qs = queryString.parse(location.search); - let keys = Object.keys(qs); - let query = {}; + const handleAppSearchOperation = (_searchString: string): void => { + let qs = queryString.parse(location.search) + let keys = Object.keys(qs) + let query = {} keys.map((key) => { - query[key] = qs[key]; + query[key] = qs[key] }) - if (_searchString){ - query['search'] = _searchString; - query['offset'] = 0; - query['hOffset'] = 0; - }else{ - delete query['search']; - delete query['offset']; - delete query['hOffset']; + if (_searchString) { + query['search'] = _searchString + query['offset'] = 0 + query['hOffset'] = 0 + } else { + delete query['search'] + delete query['offset'] + delete query['hOffset'] } - let queryStr = queryString.stringify(query); - let url = `${currentTab == AppListConstants.AppTabs.DEVTRON_APPS ? buildDevtronAppListUrl() : buildHelmAppListUrl()}?${queryStr}`; - history.push(url); + let queryStr = queryString.stringify(query) + let url = `${ + currentTab == AppListConstants.AppTabs.DEVTRON_APPS ? buildDevtronAppListUrl() : buildHelmAppListUrl() + }?${queryStr}` + history.push(url) } /** @@ -352,7 +379,7 @@ export default function AppList() { * - There is no namespace query param i.e. this is the first time selecting the cluster filter */ if (!query || Object.keys(query).length <= 0 || !query[AppListConstants.FilterType.NAMESPACE]) { - return ids.toString(); + return ids.toString() } /** @@ -361,9 +388,9 @@ export default function AppList() { * - checkedItemIds: Array of currently selected/checked items from filters list * - updatedAppliedFilters: Array of new filters to be applied */ - let currentlyAppliedFilters = query[AppListConstants.FilterType.NAMESPACE].split(','); - let checkedItemIds = ids.toString().split(','); - let updatedAppliedFilters = []; + let currentlyAppliedFilters = query[AppListConstants.FilterType.NAMESPACE].split(',') + let checkedItemIds = ids.toString().split(',') + let updatedAppliedFilters = [] /** * Step 3: Iterate through checkedItemIds, @@ -377,9 +404,9 @@ export default function AppList() { (item) => id.toString() === item || (filterType === AppListConstants.FilterType.CLUTSER && item.startsWith(`${id}_`)), - ); - updatedAppliedFilters.push(filterdIds.length > 0 ? filterdIds : id); - }); + ) + updatedAppliedFilters.push(filterdIds.length > 0 ? filterdIds : id) + }) /** * Step 4: If filterType is NAMESPACE, @@ -391,9 +418,9 @@ export default function AppList() { if (filterType === AppListConstants.FilterType.NAMESPACE) { currentlyAppliedFilters.forEach((filter) => { if (!checkedItemIds.some((itemId) => itemId.startsWith(`${filter.split('_')[0]}_`))) { - updatedAppliedFilters.push(filter.split('_')[0]); + updatedAppliedFilters.push(filter.split('_')[0]) } - }); + }) } /** @@ -402,59 +429,59 @@ export default function AppList() { * - Create a new Set out of filtered updatedAppliedFilters array to remove duplicate items. * - Create array out of new Set (as Set doesn't have toString() equivalent) and return as string. */ - return Array.from(new Set(updatedAppliedFilters.filter((filter) => filter !== ''))).toString(); - }; + return Array.from(new Set(updatedAppliedFilters.filter((filter) => filter !== ''))).toString() + } const applyFilter = (type: string, list: FilterOption[], selectedAppTab: string = undefined): void => { - let qs = queryString.parse(location.search); - let keys = Object.keys(qs); - let query = {}; + let qs = queryString.parse(location.search) + let keys = Object.keys(qs) + let query = {} keys.map((key) => { - query[key] = qs[key]; - }); + query[key] = qs[key] + }) let queryParamType = type == AppListConstants.FilterType.CLUTSER || type == AppListConstants.FilterType.NAMESPACE ? AppListConstants.FilterType.NAMESPACE - : type; - let checkedItems = list.filter((item) => item.isChecked); - let ids = checkedItems.map((item) => item.key); + : type + let checkedItems = list.filter((item) => item.isChecked) + let ids = checkedItems.map((item) => item.key) query[queryParamType] = queryParamType === AppListConstants.FilterType.NAMESPACE ? getUpdatedFiltersOnNamespaceChange(ids, type, query) - : ids.toString(); - query['offset'] = 0; - query['hOffset'] = 0; - let queryStr = queryString.stringify(query); - let _currentTab = selectedAppTab || currentTab; + : ids.toString() + query['offset'] = 0 + query['hOffset'] = 0 + let queryStr = queryString.stringify(query) + let _currentTab = selectedAppTab || currentTab let url = `${ _currentTab == AppListConstants.AppTabs.DEVTRON_APPS ? buildDevtronAppListUrl() : buildHelmAppListUrl() - }?${queryStr}`; - history.push(url); - }; + }?${queryStr}` + history.push(url) + } const removeFilter = (filter, filterType: string): void => { - let val = filter.key.toString(); - const clustId = val.split('_')[0]; // Specific to cluster & namespace filter removal - let qs = queryString.parse(location.search); - let keys = Object.keys(qs); - let query = {}; + let val = filter.key.toString() + const clustId = val.split('_')[0] // Specific to cluster & namespace filter removal + let qs = queryString.parse(location.search) + let keys = Object.keys(qs) + let query = {} keys.map((key) => { - query[key] = qs[key]; - }); - query['offset'] = 0; - query['hOffset'] = 0; + query[key] = qs[key] + }) + query['offset'] = 0 + query['hOffset'] = 0 let queryParamType = filterType === AppListConstants.FilterType.CLUTSER || filterType === AppListConstants.FilterType.NAMESPACE ? AppListConstants.FilterType.NAMESPACE - : filterType; - let appliedFilters = query[queryParamType]; - let arr = appliedFilters.split(','); + : filterType + let appliedFilters = query[queryParamType] + let arr = appliedFilters.split(',') if (filterType === AppListConstants.FilterType.CLUTSER) { - arr = arr.filter((item) => !item.startsWith(val)); + arr = arr.filter((item) => !item.startsWith(val)) } else { - arr = arr.filter((item) => item !== val); + arr = arr.filter((item) => item !== val) /** * Check if filterType is NAMESPACE & appliedFilters array doesn't contain any namespace @@ -464,227 +491,279 @@ export default function AppList() { filterType === AppListConstants.FilterType.NAMESPACE && !arr.some((item) => item.startsWith(`${clustId}_`)) ) { - arr.push(clustId); + arr.push(clustId) } } query[queryParamType] = - filterType === AppListConstants.FilterType.NAMESPACE && !arr.toString() ? clustId : arr.toString(); + filterType === AppListConstants.FilterType.NAMESPACE && !arr.toString() ? clustId : arr.toString() - if (query[queryParamType] == '') delete query[queryParamType]; - let queryStr = queryString.stringify(query); + if (query[queryParamType] == '') delete query[queryParamType] + let queryStr = queryString.stringify(query) let url = `${ currentTab == AppListConstants.AppTabs.DEVTRON_APPS ? buildDevtronAppListUrl() : buildHelmAppListUrl() - }?${queryStr}`; - history.push(url); - }; + }?${queryStr}` + history.push(url) + } const removeAllFilters = (): void => { - let qs = queryString.parse(location.search); - let keys = Object.keys(qs); - let query = {}; + let qs = queryString.parse(location.search) + let keys = Object.keys(qs) + let query = {} keys.map((key) => { - query[key] = qs[key]; + query[key] = qs[key] }) - query['offset'] = 0; - query['hOffset'] = 0; - delete query['environment']; - delete query['team']; - delete query['namespace']; - delete query['search']; + query['offset'] = 0 + query['hOffset'] = 0 + delete query['environment'] + delete query['team'] + delete query['namespace'] + delete query['search'] //delete search string - setSearchApplied(false); - setSearchString(""); + setSearchApplied(false) + setSearchString('') - let queryStr = queryString.stringify(query); - let url = `${currentTab == AppListConstants.AppTabs.DEVTRON_APPS ? buildDevtronAppListUrl() : buildHelmAppListUrl()}?${queryStr}`; - history.push(url); + let queryStr = queryString.stringify(query) + let url = `${ + currentTab == AppListConstants.AppTabs.DEVTRON_APPS ? buildDevtronAppListUrl() : buildHelmAppListUrl() + }?${queryStr}` + history.push(url) } const sortApplicationList = (key: string): void => { - let qs = queryString.parse(location.search); - let keys = Object.keys(qs); - let query = {}; + let qs = queryString.parse(location.search) + let keys = Object.keys(qs) + let query = {} keys.map((key) => { - query[key] = qs[key]; + query[key] = qs[key] }) - query["orderBy"] = key; - query["sortOrder"] = query["sortOrder"] == OrderBy.DESC ? OrderBy.ASC : OrderBy.DESC; - let queryStr = queryString.stringify(query); - let url = `${currentTab == AppListConstants.AppTabs.DEVTRON_APPS ? buildDevtronAppListUrl() : buildHelmAppListUrl()}?${queryStr}`; - history.push(url); + query['orderBy'] = key + query['sortOrder'] = query['sortOrder'] == OrderBy.DESC ? OrderBy.ASC : OrderBy.DESC + let queryStr = queryString.stringify(query) + let url = `${ + currentTab == AppListConstants.AppTabs.DEVTRON_APPS ? buildDevtronAppListUrl() : buildHelmAppListUrl() + }?${queryStr}` + history.push(url) } function changeAppTab(appTabType) { - if (appTabType == currentTab){ - return; + if (appTabType == currentTab) { + return } - let url = (appTabType == AppListConstants.AppTabs.DEVTRON_APPS) ? `${buildDevtronAppListUrl()}${location.search}` : `${buildHelmAppListUrl()}${location.search}`; - history.push(url); - setCurrentTab(appTabType); + let url = + appTabType == AppListConstants.AppTabs.DEVTRON_APPS + ? `${buildDevtronAppListUrl()}${location.search}` + : `${buildHelmAppListUrl()}${location.search}` + history.push(url) + setCurrentTab(appTabType) } const searchApp = (event: React.FormEvent) => { - event.preventDefault(); - setSearchApplied(true); - handleAppSearchOperation(searchString); + event.preventDefault() + setSearchApplied(true) + handleAppSearchOperation(searchString) } const clearSearch = (): void => { - setSearchApplied(false); - setSearchString(""); - handleAppSearchOperation(""); + setSearchApplied(false) + setSearchString('') + handleAppSearchOperation('') } const onChangeSearchString = (event: React.ChangeEvent): void => { - let str = event.target.value || ""; - str = str.toLowerCase(); - setSearchString(str); + let str = event.target.value || '' + str = str.toLowerCase() + setSearchString(str) } - const syncNow = () : void => { - window.location.reload(); + const syncNow = (): void => { + window.location.reload() } - const setFetchingExternalAppsState = (fetching : boolean) : void => { - setFetchingExternalApps(fetching); + const setFetchingExternalAppsState = (fetching: boolean): void => { + setFetchingExternalApps(fetching) } - const setShowPulsatingDotState = (show : boolean) : void => { - setShowPulsatingDot(show); + const setShowPulsatingDotState = (show: boolean): void => { + setShowPulsatingDot(show) } - const onShowHideFilterContent = (show : boolean) : void => { - setPageOverflowEnabled(!show); + const onShowHideFilterContent = (show: boolean): void => { + setPageOverflowEnabled(!show) + } + + const handleCreateButton = () => { + setShowCreateNewAppSelectionModal(!showCreateNewAppSelectionModal) } function renderPageHeader() { - return
-

Applications

- {serverMode == SERVER_MODE.FULL && - - } + return ( + + {showCreateNewAppSelectionModal && renderAppCreateSelectionModal()} -
+ + ) } function renderMasterFilters() { - let _isAnyClusterFilterApplied = masterFilters.clusters.some(_cluster => _cluster.isChecked); - return
-
-
- - - {searchApplied && - - } -
-
+ let _isAnyClusterFilterApplied = masterFilters.clusters.some((_cluster) => _cluster.isChecked) + return ( +
+
+
+ + + {searchApplied && ( + + )} +
+
Filter By - - { - serverMode == SERVER_MODE.FULL && + + {serverMode == SERVER_MODE.FULL && ( <> - + - } + )} - - namespace.toShow)} - labelKey="label" - searchKey="actualName" - buttonText="Namespace" - searchable multi - placeholder="Search Namespace" - type={AppListConstants.FilterType.NAMESPACE} - applyFilter={applyFilter} - isDisabled={!_isAnyClusterFilterApplied} - disableTooltipMessage={"Select a cluster first"} - isLabelHtml={true} - onShowHideFilterContent={onShowHideFilterContent} - loading={fetchingNamespaces} - errored={fetchingNamespacesErrored} - errorMessage={"Could not load namespaces"} - errorCallbackFunction={_forceFetchAndSetNamespaces} /> + + namespace.toShow)} + labelKey="label" + searchKey="actualName" + buttonText="Namespace" + searchable + multi + placeholder="Search Namespace" + type={AppListConstants.FilterType.NAMESPACE} + applyFilter={applyFilter} + isDisabled={!_isAnyClusterFilterApplied} + disableTooltipMessage={'Select a cluster first'} + isLabelHtml={true} + onShowHideFilterContent={onShowHideFilterContent} + loading={fetchingNamespaces} + errored={fetchingNamespacesErrored} + errorMessage={'Could not load namespaces'} + errorCallbackFunction={_forceFetchAndSetNamespaces} + />
+ ) } function renderAppliedFilters() { - let count = 0; - let keys = Object.keys(masterFilters); - let appliedFilters =
- {keys.map((key) => { - let filterType = ''; - let _filterKey = ''; - if (key == 'projects'){ - filterType = AppListConstants.FilterType.PROJECT; - _filterKey = 'project'; - }else if (key == 'clusters'){ - filterType = AppListConstants.FilterType.CLUTSER; - _filterKey = 'cluster'; - }else if (key == 'namespaces'){ - filterType = AppListConstants.FilterType.NAMESPACE; - _filterKey = 'namespace'; - }else if (key == 'environments'){ - filterType = AppListConstants.FilterType.ENVIRONMENT; - _filterKey = 'environment'; - } - return masterFilters[key].map((filter) => { - if (filter.isChecked) { - count++; - let _text = (filterType == AppListConstants.FilterType.NAMESPACE) ? filter.actualName + ' (' + filter.clusterName + ')' : filter.label; - return
- {_filterKey} - - {_text} - -
+ let count = 0 + let keys = Object.keys(masterFilters) + let appliedFilters = ( +
+ {keys.map((key) => { + let filterType = '' + let _filterKey = '' + if (key == 'projects') { + filterType = AppListConstants.FilterType.PROJECT + _filterKey = 'project' + } else if (key == 'clusters') { + filterType = AppListConstants.FilterType.CLUTSER + _filterKey = 'cluster' + } else if (key == 'namespaces') { + filterType = AppListConstants.FilterType.NAMESPACE + _filterKey = 'namespace' + } else if (key == 'environments') { + filterType = AppListConstants.FilterType.ENVIRONMENT + _filterKey = 'environment' } - }) - })} - -
+ return masterFilters[key].map((filter) => { + if (filter.isChecked) { + count++ + let _text = + filterType == AppListConstants.FilterType.NAMESPACE + ? filter.actualName + ' (' + filter.clusterName + ')' + : filter.label + return ( +
+ {_filterKey} + + {_text} + +
+ ) + } + }) + })} + +
+ ) - return - {count > 0 ? appliedFilters : null} - + return {count > 0 ? appliedFilters : null} } function renderAppTabs() { @@ -751,95 +830,131 @@ export default function AppList() { } const closeDevtronAppCreateModal = () => { - let _prefix = (currentTab == AppListConstants.AppTabs.DEVTRON_APPS ? buildDevtronAppListUrl() : buildHelmAppListUrl()); - let url = `${_prefix}${location.search}`; - history.push(`${url}`); + let _prefix = + currentTab == AppListConstants.AppTabs.DEVTRON_APPS ? buildDevtronAppListUrl() : buildHelmAppListUrl() + let url = `${_prefix}${location.search}` + history.push(`${url}`) } function renderAppCreateRouter() { - return - } - /> - } - /> - + return ( + + ( + + )} + /> + ( + + )} + /> + + ) } function renderAppCreateSelectionModal() { - return setShowCreateNewAppSelectionModal(!showCreateNewAppSelectionModal)} > + return ( + setShowCreateNewAppSelectionModal(!showCreateNewAppSelectionModal)} + >
- +
Custom app -
Connect a git repository to deploy
a custom application
+
+ Connect a git repository to deploy
a custom application +
- +
From Chart store -
Deploy apps using third party helm
charts (eg. prometheus, redis etc.)
+
+ Deploy apps using third party helm
charts (eg. prometheus, redis etc.) +
-
+
+ ) } - return (
- { - dataStateType == AppListViewType.LOADING && + {dataStateType == AppListViewType.LOADING && (
- +
- } - { - dataStateType == AppListViewType.ERROR && + )} + {dataStateType == AppListViewType.ERROR && (
- } - { - dataStateType == AppListViewType.LIST && - <> -
- {renderPageHeader()} -
+ )} + {dataStateType == AppListViewType.LIST && ( + <> + {renderPageHeader()} {renderMasterFilters()} {renderAppliedFilters()} {renderAppTabs()} {serverMode == SERVER_MODE.FULL && renderAppCreateRouter()} - { - params.appType == AppListConstants.AppType.DEVTRON_APPS && serverMode == SERVER_MODE.FULL && - - } - { - params.appType == AppListConstants.AppType.DEVTRON_APPS && serverMode == SERVER_MODE.EA_ONLY && -
- + )} + {params.appType == AppListConstants.AppType.DEVTRON_APPS && serverMode == SERVER_MODE.EA_ONLY && ( +
+
- } - { - params.appType == AppListConstants.AppType.HELM_APPS && + )} + {params.appType == AppListConstants.AppType.HELM_APPS && ( <> - - { - fetchingExternalApps && + + {fetchingExternalApps && (
- } + )} - } - - } + )} + + )}
) } diff --git a/src/components/app/list/list.css b/src/components/app/list/list.css index c986b44384..348428e2c4 100644 --- a/src/components/app/list/list.css +++ b/src/components/app/list/list.css @@ -1,31 +1,3 @@ -.app-header { - background-color: var(--white); - overflow: visible; - width: 100%; - position: sticky; - top: 0px; - z-index: var(--page-header-index); - border-bottom: 1px solid #ddd; -} - -.app-header__title { - display: flex; - align-items: center; - justify-content: space-between; - padding: 12px 20px; -} - -.app-header__text { - margin: 0px; - font-size: 20px; - font-weight: 500; - font-style: normal; - font-stretch: normal; - line-height: 1.4; - letter-spacing: normal; - color: var(--N900); -} - .search-filter-section { padding: 12px 20px; display: flex; @@ -36,7 +8,6 @@ z-index: 1; } - .filters { flex: 1 1; display: flex; @@ -95,9 +66,9 @@ background-color: #f7fafc; } -.saved-filters__clear-btn{ - margin: 0; - padding: 0; +.saved-filters__clear-btn { + margin: 0; + padding: 0; } .app-list__header { @@ -117,7 +88,7 @@ z-index: var(--app-list-header-index); } -.saved-filters+.app-list .app-list__header { +.saved-filters + .app-list .app-list__header { top: 116px; } @@ -163,7 +134,7 @@ text-transform: uppercase; } -.app-list__cell--icon{ +.app-list__cell--icon { width: 40px; } @@ -187,7 +158,9 @@ background-image: url('../../../assets/icons/appstatus/bg-white.svg'); } -.app-list__cell--env, .app-list__cell--cluster, .app-list__cell--namespace { +.app-list__cell--env, +.app-list__cell--cluster, +.app-list__cell--namespace { flex-basis: 18%; justify-content: flex-start; } @@ -259,7 +232,7 @@ } .button-edit .fill-color { - fill: var(--N000) + fill: var(--N000); } .button-edit__icon { @@ -399,7 +372,7 @@ text-decoration: underline; } -@media(min-width: 1024px) { +@media (min-width: 1024px) { .search-filter-section { flex-direction: row; align-items: center; @@ -413,18 +386,18 @@ } } -.app-create-model-wrapper{ - position : fixed; - right : 24px; - top : 50px; +.app-create-model-wrapper { + position: fixed; + right: 24px; + top: 50px; } -.app-create-model-wrapper .app-create-child{ - padding : 10px 15px; +.app-create-model-wrapper .app-create-child { + padding: 10px 15px; display: flex; } -.app-create-model-wrapper .app-create-child:first-child{ +.app-create-model-wrapper .app-create-child:first-child { border-bottom: 1px solid #edf1f5; } @@ -436,24 +409,24 @@ cursor: pointer; } -.app-tabs-wrapper{ +.app-tabs-wrapper { position: relative; border-bottom: 1px solid #ddd; } -.app-tabs-wrapper .tab-list{ +.app-tabs-wrapper .tab-list { padding-left: 20px; background-color: white; } -.app-tabs-wrapper .app-tabs-sync{ +.app-tabs-wrapper .app-tabs-sync { position: absolute; right: 20px; top: 10px; } -.app-tabs-wrapper .app-tabs-sync button{ - margin-top : -2px; +.app-tabs-wrapper .app-tabs-sync button { + margin-top: -2px; } .filter-divider { @@ -462,7 +435,7 @@ background-color: var(--N200); } -.saved-filter-divider{ +.saved-filter-divider { height: 24px; width: 1px; background-color: var(--N200); @@ -478,18 +451,17 @@ border: solid 1px #ffcb5d; } -.helm-permission-message-strip .icon path[fill="#F33E3E"] { - fill: var(--Y700) +.helm-permission-message-strip .icon path[fill='#F33E3E'] { + fill: var(--Y700); } - .above-header-message { min-height: 32px; margin: 0 20px; padding: 6px; } -.ea-fetch-error-message{ +.ea-fetch-error-message { background-color: var(--R100); border: solid 1px var(--R200); -} \ No newline at end of file +} diff --git a/src/components/bulkEdits/BulkEdits.tsx b/src/components/bulkEdits/BulkEdits.tsx index a9ab15a6a2..4ad7aaf295 100644 --- a/src/components/bulkEdits/BulkEdits.tsx +++ b/src/components/bulkEdits/BulkEdits.tsx @@ -1,8 +1,8 @@ -import React, { Component } from 'react'; -import { AppListConstants, DOCUMENTATION, SERVER_MODE } from '../../config'; -import Tippy from '@tippyjs/react'; -import CodeEditor from '../CodeEditor/CodeEditor'; -import { ViewType } from '../../config'; +import React, { Component } from 'react' +import { AppListConstants, DOCUMENTATION, SERVER_MODE } from '../../config' +import Tippy from '@tippyjs/react' +import CodeEditor from '../CodeEditor/CodeEditor' +import { ViewType } from '../../config' import { BulkEditsProps, BulkEditsState, @@ -10,23 +10,24 @@ import { CMandSecretOutputKeys, DtOutputKeys, CMandSecretImpactedObjects, -} from './bulkEdits.type'; -import yamlJsParser from 'yaml'; -import { Progressing, showError, ErrorScreenManager } from '../common'; -import { ReactComponent as Question } from '../../assets/icons/ic-help-outline.svg'; -import { ReactComponent as Close } from '../../assets/icons/ic-close.svg'; -import { ReactComponent as PlayButton } from '../../assets/icons/ic-play.svg'; -import { updateBulkList, getSeeExample, updateImpactedObjectsList } from './bulkedits.service'; -import ReactSelect from 'react-select'; -import { DropdownIndicator } from '../charts/charts.util'; -import './bulkEdit.css'; -import { multiSelectStyles } from './bulkedit.utils'; -import { Option } from '../../components/v2/common/ReactSelect.utils'; -import { MarkDown } from '../charts/discoverChartDetail/DiscoverChartDetails'; -import { toast } from 'react-toastify'; -import '../charts/discoverChartDetail/DiscoverChartDetails.scss'; -import '../charts/modal/DeployChart.scss'; -import EAEmptyState, { EAEmptyStateType } from '../common/eaEmptyState/EAEmptyState'; +} from './bulkEdits.type' +import yamlJsParser from 'yaml' +import { Progressing, showError, ErrorScreenManager } from '../common' +import { ReactComponent as Question } from '../../assets/icons/ic-help-outline.svg' +import { ReactComponent as Close } from '../../assets/icons/ic-close.svg' +import { ReactComponent as PlayButton } from '../../assets/icons/ic-play.svg' +import { updateBulkList, getSeeExample, updateImpactedObjectsList } from './bulkedits.service' +import ReactSelect from 'react-select' +import { DropdownIndicator } from '../charts/charts.util' +import './bulkEdit.css' +import { multiSelectStyles } from './bulkedit.utils' +import { Option } from '../../components/v2/common/ReactSelect.utils' +import { MarkDown } from '../charts/discoverChartDetail/DiscoverChartDetails' +import { toast } from 'react-toastify' +import '../charts/discoverChartDetail/DiscoverChartDetails.scss' +import '../charts/modal/DeployChart.scss' +import EAEmptyState, { EAEmptyStateType } from '../common/eaEmptyState/EAEmptyState' +import PageHeader from '../common/header/PageHeader' export enum OutputObjectTabs { OUTPUT = 'Output', @@ -36,7 +37,7 @@ export enum OutputObjectTabs { const STATUS = { ERROR: "Please check the apiVersion and kind, apiVersion and kind provided by you don't exist", EMPTY_IMPACTED: 'We could not find any matching devtron applications.', -}; +} const OutputTabs: React.FC = ({ handleOutputTabs, outputName, value, name }) => { return ( @@ -44,12 +45,12 @@ const OutputTabs: React.FC = ({ handleOutputTabs, outputName, val
{name}
- ); -}; + ) +} export default class BulkEdits extends Component { constructor(props) { - super(props); + super(props) this.state = { view: ViewType.LOADING, @@ -66,34 +67,34 @@ export default class BulkEdits extends Component showOutputData: true, showImpactedtData: false, codeEditorPayload: undefined, - }; + } } componentDidMount = () => { - if(this.props.serverMode == SERVER_MODE.FULL){ + if (this.props.serverMode == SERVER_MODE.FULL) { this.setState({ view: ViewType.LOADING, - }); - this.initialise(); + }) + this.initialise() } - }; + } initialise() { getSeeExample() .then((res) => { - this.setState({ view: ViewType.LOADING }); - let bulkConfig = res.result; - let kind = bulkConfig.map((elm) => elm.script.kind); - kind = kind.toString().toLocaleLowerCase(); - let apiVersion = bulkConfig.map((elm) => elm.script.apiVersion); - apiVersion = apiVersion.toString(); - let readmeResult = bulkConfig.map((elm) => elm.readme); + this.setState({ view: ViewType.LOADING }) + let bulkConfig = res.result + let kind = bulkConfig.map((elm) => elm.script.kind) + kind = kind.toString().toLocaleLowerCase() + let apiVersion = bulkConfig.map((elm) => elm.script.apiVersion) + apiVersion = apiVersion.toString() + let readmeResult = bulkConfig.map((elm) => elm.readme) let updatedTemplate = bulkConfig.map((elm) => { return { value: elm.operation, label: elm.operation, - }; - }); + } + }) this.setState({ view: ViewType.FORM, @@ -101,45 +102,29 @@ export default class BulkEdits extends Component bulkConfig: bulkConfig, updatedTemplate: updatedTemplate, readmeResult: readmeResult, - }); + }) }) .catch((error) => { - showError(error); - this.setState({ view: ViewType.FORM, statusCode: error.code }); - }); + showError(error) + this.setState({ view: ViewType.FORM, statusCode: error.code }) + }) } - renderBulkEditHeader = () => { + renderBulkHeaderDescription = () => { return ( -
-
Bulk Edit - Learn more } - > +
+
+ +
+ Run scripts to bulk edit configurations for multiple devtron components. - + Learn more - -
-
- ); - }; - - renderBulkHeaderDescription = () => { - return ( -
-
- -
Run scripts to bulk edit configurations for multiple devtron components. - Learn more
/>
- ); - }; + ) + } handleRunButton = (e) => { - var outputDiv = document.querySelector('.code-editor-body'); - outputDiv.scrollTop = outputDiv.scrollHeight; + var outputDiv = document.querySelector('.code-editor-body') + outputDiv.scrollTop = outputDiv.scrollHeight this.setState({ view: ViewType.LOADING, outputName: 'output', - }); + }) - let configJson: any = {}; + let configJson: any = {} try { - configJson = yamlJsParser.parse(this.state.codeEditorPayload); + configJson = yamlJsParser.parse(this.state.codeEditorPayload) } catch (error) { //Invalid YAML, couldn't be parsed to JSON. Show error toast - toast.error('Invalid Yaml'); - this.setState({ view: ViewType.FORM }); - return; + toast.error('Invalid Yaml') + this.setState({ view: ViewType.FORM }) + return } - let errorMessage = []; - errorMessage.push(STATUS.ERROR); + let errorMessage = [] + errorMessage.push(STATUS.ERROR) - let payload = configJson; + let payload = configJson updateBulkList(payload) .then((response) => { - let outputResult = response.result; + let outputResult = response.result this.setState({ statusCode: 0, view: ViewType.FORM, @@ -185,38 +170,38 @@ export default class BulkEdits extends Component outputResult: outputResult, showImpactedtData: false, impactedObjects: undefined, - }); + }) }) .catch((error) => { - showError(error); - this.setState({ view: ViewType.FORM, statusCode: error.code, outputName: 'output' }); - }); - }; + showError(error) + this.setState({ view: ViewType.FORM, statusCode: error.code, outputName: 'output' }) + }) + } handleShowImpactedObjectButton = () => { - var outputDiv = document.querySelector('.code-editor-body'); - outputDiv.scrollTop = outputDiv.scrollHeight; + var outputDiv = document.querySelector('.code-editor-body') + outputDiv.scrollTop = outputDiv.scrollHeight this.setState({ view: ViewType.LOADING, outputName: 'impacted', - }); + }) - let configJson: any = {}; + let configJson: any = {} try { - configJson = yamlJsParser.parse(this.state.codeEditorPayload); + configJson = yamlJsParser.parse(this.state.codeEditorPayload) } catch (error) { //Invalid YAML, couldn't be parsed to JSON. Show error toast - toast.error('Invalid Yaml'); - this.setState({ view: ViewType.FORM }); - return; + toast.error('Invalid Yaml') + this.setState({ view: ViewType.FORM }) + return } - let payload = configJson; + let payload = configJson updateImpactedObjectsList(payload) .then((response) => { - let impactedObjects = response.result; + let impactedObjects = response.result this.setState({ statusCode: 0, view: ViewType.FORM, @@ -224,13 +209,13 @@ export default class BulkEdits extends Component outputResult: undefined, outputName: 'impacted', showImpactedtData: true, - }); + }) }) .catch((error) => { - showError(error); - this.setState({ view: ViewType.FORM, statusCode: error.code, outputName: 'impacted' }); - }); - }; + showError(error) + this.setState({ view: ViewType.FORM, statusCode: error.code, outputName: 'impacted' }) + }) + } renderCodeEditorHeader = () => { return ( @@ -262,14 +247,14 @@ export default class BulkEdits extends Component
) : null}
- ); - }; + ) + } handleConfigChange = (value) => { this.setState({ codeEditorPayload: value, - }); - }; + }) + } handleOutputTab = (e, key: string) => { if (key == 'output') { @@ -277,16 +262,16 @@ export default class BulkEdits extends Component outputName: 'output', showOutputData: true, showImpactedtData: false, - }); + }) } if (key == 'impacted') { this.setState({ outputName: 'impacted', showImpactedtData: true, showOutputData: false, - }); + }) } - }; + } renderCodeEditorBody = () => { return ( @@ -297,7 +282,7 @@ export default class BulkEdits extends Component value={this.state.codeEditorPayload} mode="yaml" onChange={(event) => { - this.handleConfigChange(event); + this.handleConfigChange(event) }} >
@@ -333,16 +318,18 @@ export default class BulkEdits extends Component
- ); - }; + ) + } renderConfigMapOutput = () => { return (
-
*CONFIGMAPS:
+
+ *CONFIGMAPS:

-
#Message:
+
+ #Message:

{this.state.outputResult.configMap?.message?.map((elm) => { return ( @@ -350,7 +337,7 @@ export default class BulkEdits extends Component {elm}
- ); + ) })}
-------------------------- @@ -364,7 +351,7 @@ export default class BulkEdits extends Component ) : ( <> {this.state.outputResult.configMap?.failure.map((elm) => { - return this.renderCmAndSecretResponseForOneApp(elm); + return this.renderCmAndSecretResponseForOneApp(elm) })} )} @@ -379,15 +366,15 @@ export default class BulkEdits extends Component ) : ( <> {this.state.outputResult.configMap?.successful.map((elm) => { - return this.renderCmAndSecretResponseForOneApp(elm); + return this.renderCmAndSecretResponseForOneApp(elm) })} )}
----------------------------------------------------
- ); - }; + ) + } renderDTResponseForOneApp = (DTOutputKeys: DtOutputKeys) => { return ( @@ -398,8 +385,8 @@ export default class BulkEdits extends Component Message: {DTOutputKeys.message}

- ); - }; + ) + } renderCmAndSecretResponseForOneApp = (CMandSecretOutputKeys: CMandSecretOutputKeys) => { return ( @@ -411,8 +398,8 @@ export default class BulkEdits extends Component Message: {CMandSecretOutputKeys.message}

- ); - }; + ) + } renderCMAndSecretImpObj = (CMandSecretImpactedObject: CMandSecretImpactedObjects) => { return ( @@ -423,16 +410,18 @@ export default class BulkEdits extends Component Names : {CMandSecretImpactedObject.names.join(', ')}

- ); - }; + ) + } renderDeploymentTemplateOutput = () => { return (
-
*DEPLOYMENT TEMPLATE:
+
+ *DEPLOYMENT TEMPLATE:

-
#Message:
+
+ #Message:

{this.state.outputResult.deploymentTemplate?.message.map((elm) => { return ( @@ -440,7 +429,7 @@ export default class BulkEdits extends Component {elm}
- ); + ) })}
-------------------------- @@ -454,7 +443,7 @@ export default class BulkEdits extends Component ) : ( <> {this.state.outputResult.deploymentTemplate?.failure.map((elm) => { - return this.renderDTResponseForOneApp(elm); + return this.renderDTResponseForOneApp(elm) })} )} @@ -470,23 +459,25 @@ export default class BulkEdits extends Component ) : ( <> {this.state.outputResult.deploymentTemplate?.successful.map((elm) => { - return this.renderDTResponseForOneApp(elm); + return this.renderDTResponseForOneApp(elm) })} )}
----------------------------------------------------
- ); - }; + ) + } renderSecretOutput = () => { return (
-
*SECRETS:
+
+ *SECRETS:

-
#Message:
+
+ #Message:

{this.state.outputResult.secret?.message.map((elm) => { return ( @@ -494,7 +485,7 @@ export default class BulkEdits extends Component {elm}
- ); + ) })}
-------------------------- @@ -508,7 +499,7 @@ export default class BulkEdits extends Component ) : ( <> {this.state.outputResult.secret?.failure.map((elm) => { - return this.renderCmAndSecretResponseForOneApp(elm); + return this.renderCmAndSecretResponseForOneApp(elm) })} )} @@ -524,21 +515,21 @@ export default class BulkEdits extends Component ) : ( <> {this.state.outputResult.secret?.successful.map((elm) => { - return this.renderCmAndSecretResponseForOneApp(elm); + return this.renderCmAndSecretResponseForOneApp(elm) })} )}
-----------------------------------------------------------------
- ); - }; + ) + } renderOutputs = () => { - let payloadStringWithoutSpaces = this.state.codeEditorPayload?.split(' ').join(''); - let deploymentTemplateInPayload = payloadStringWithoutSpaces?.includes('deploymentTemplate:\nspec:'); - let configMapInPayload = payloadStringWithoutSpaces?.includes('configMap:\nspec:'); - let secretInPayload = payloadStringWithoutSpaces?.includes('secret:\nspec:'); + let payloadStringWithoutSpaces = this.state.codeEditorPayload?.split(' ').join('') + let deploymentTemplateInPayload = payloadStringWithoutSpaces?.includes('deploymentTemplate:\nspec:') + let configMapInPayload = payloadStringWithoutSpaces?.includes('configMap:\nspec:') + let secretInPayload = payloadStringWithoutSpaces?.includes('secret:\nspec:') return this.state.view === ViewType.LOADING ? (
@@ -551,32 +542,34 @@ export default class BulkEdits extends Component {deploymentTemplateInPayload ? this.renderDeploymentTemplateOutput() : null} {secretInPayload ? this.renderSecretOutput() : null}
- ); - }; + ) + } renderConfigMapImpObj = () => { return (
-
*CONFIGMAPS:

+
+ *CONFIGMAPS:

{this.state.impactedObjects.configMap.length === 0 ? ( <>No Result Found ) : ( <> {this.state.impactedObjects.configMap.map((elm) => { - return this.renderCMAndSecretImpObj(elm); + return this.renderCMAndSecretImpObj(elm) })} )}
-----------------------------------------------------------------
- ); - }; + ) + } renderDeploymentTemplateImpObj = () => { return (
-
*DEPLOYMENT TEMPLATE:

+
+ *DEPLOYMENT TEMPLATE:

{this.state.impactedObjects.deploymentTemplate.length === 0 ? ( <>No Result Found ) : ( @@ -589,39 +582,40 @@ export default class BulkEdits extends Component Environment Id: {elm.envId}

- ); + ) })} )}
-----------------------------------------------------------------
- ); - }; + ) + } renderSecretImpObj = () => { return (
-
*SECRETS:

+
+ *SECRETS:

{this.state.impactedObjects.secret.length === 0 ? ( <>No Result Found ) : ( <> {this.state.impactedObjects.secret.map((elm) => { - return this.renderCMAndSecretImpObj(elm); + return this.renderCMAndSecretImpObj(elm) })} )}
-----------------------------------------------------------------
- ); - }; + ) + } renderImpactedObjects = () => { - let payloadStringWithoutSpaces = this.state.codeEditorPayload?.split(' ').join(''); - let deploymentTemplateInPayload = payloadStringWithoutSpaces?.includes('deploymentTemplate:\nspec:'); - let configMapInPayload = payloadStringWithoutSpaces?.includes('configMap:\nspec:'); - let secretInPayload = payloadStringWithoutSpaces?.includes('secret:\nspec:'); + let payloadStringWithoutSpaces = this.state.codeEditorPayload?.split(' ').join('') + let deploymentTemplateInPayload = payloadStringWithoutSpaces?.includes('deploymentTemplate:\nspec:') + let configMapInPayload = payloadStringWithoutSpaces?.includes('configMap:\nspec:') + let secretInPayload = payloadStringWithoutSpaces?.includes('secret:\nspec:') return this.state.view === ViewType.LOADING ? (
@@ -634,24 +628,24 @@ export default class BulkEdits extends Component {deploymentTemplateInPayload ? this.renderDeploymentTemplateImpObj() : null} {secretInPayload ? this.renderSecretImpObj() : null}
- ); - }; + ) + } handleUpdateTemplate = () => { - this.setState({ isReadmeLoading: true }); + this.setState({ isReadmeLoading: true }) getSeeExample() .then((res) => { - let readmeResult = res.result.map((elm) => elm.readme); + let readmeResult = res.result.map((elm) => elm.readme) this.setState({ isReadmeLoading: false, readmeResult: readmeResult, - }); + }) }) .catch((error) => { - showError(error); - this.setState({ isReadmeLoading: false, statusCode: error.code }); - }); - }; + showError(error) + this.setState({ isReadmeLoading: false, statusCode: error.code }) + }) + } renderSampleTemplateHeader = () => { return ( @@ -679,11 +673,11 @@ export default class BulkEdits extends Component onClick={() => this.setState({ showExamples: false })} />
- ); - }; + ) + } renderSampleTemplateBody = () => { - let readmeJson = this.state.readmeResult.toString(); + let readmeJson = this.state.readmeResult.toString() return this.state.isReadmeLoading ? (
@@ -694,8 +688,8 @@ export default class BulkEdits extends Component
- ); - }; + ) + } renderBulkCodeEditor = () => { return ( @@ -703,8 +697,8 @@ export default class BulkEdits extends Component {this.renderCodeEditorHeader()}
{this.renderCodeEditorBody()}
- ); - }; + ) + } renderReadmeSection = () => { return ( @@ -712,8 +706,8 @@ export default class BulkEdits extends Component {this.renderSampleTemplateHeader()} {this.renderSampleTemplateBody()}
- ); - }; + ) + } renderCodeEditorAndReadme = () => { return ( @@ -721,8 +715,8 @@ export default class BulkEdits extends Component
{this.renderBulkCodeEditor()}
{this.renderReadmeSection()}
- ); - }; + ) + } renderReadmeAndCodeEditor = () => { return ( @@ -734,8 +728,8 @@ export default class BulkEdits extends Component ) : null} - ); - }; + ) + } renderBulkEditBody = () => { return ( @@ -747,8 +741,8 @@ export default class BulkEdits extends Component this.renderCodeEditorAndReadme() )} - ); - }; + ) + } renderEmptyStateForEAOnlyMode = () => { return ( @@ -762,8 +756,8 @@ export default class BulkEdits extends Component knowMoreLink={DOCUMENTATION.HOME_PAGE} /> - ); - }; + ) + } render() { if (this.state.view === ViewType.ERROR) { @@ -771,14 +765,16 @@ export default class BulkEdits extends Component
- ); + ) } - + return (
- {this.renderBulkEditHeader()} - {this.props.serverMode == SERVER_MODE.EA_ONLY ? this.renderEmptyStateForEAOnlyMode() : this.renderBulkEditBody()} + + {this.props.serverMode == SERVER_MODE.EA_ONLY + ? this.renderEmptyStateForEAOnlyMode() + : this.renderBulkEditBody()}
- ); + ) } -} \ No newline at end of file +} diff --git a/src/components/bulkEdits/bulkEdit.css b/src/components/bulkEdits/bulkEdit.css index c0840d9c61..a0524922ea 100644 --- a/src/components/bulkEdits/bulkEdit.css +++ b/src/components/bulkEdits/bulkEdit.css @@ -1,23 +1,19 @@ -.select-width{ +.select-width { width: 240px; } -.updated-container{ +.updated-container { height: 'calc(100vh - 100px)'; min-height: 0; display: grid; grid-template-rows: 48px 1fr; } -.updated-container--sample{ +.updated-container--sample { height: calc(100vh - 150px); align-items: flex-start; } -.brdr-btm{ - box-shadow: inset 0 -1px 0 0 var(--N200); -} - .output-drawer { position: fixed; bottom: 0; @@ -29,12 +25,12 @@ height: 100%; } -.right-readme{ +.right-readme { height: 100%; overflow: auto; } -.bulk-desciription{ +.bulk-desciription { box-shadow: inset 0 -1px 0 0 var(--N200); background-color: #f3f0ff; } @@ -43,54 +39,54 @@ border-bottom: 1px solid #d0d4d9; } -.border-top{ - border-top: 1px solid #d0d4d9; +.border-top { + border-top: 1px solid #d0d4d9; } -.border-right{ - border-right: 1px solid #d0d4d9; +.border-right { + border-right: 1px solid #d0d4d9; } -.bulk-output-drawer{ +.bulk-output-drawer { height: 280px; } -.tertiary-tab__radio input:checked~.tertiary-output-tab{ +.tertiary-tab__radio input:checked ~ .tertiary-output-tab { color: var(--B500); border-bottom: solid 2px var(--B500); } -.height{ +.height { height: calc(100% - 100px); } -.code-editor-readme{ +.code-editor-readme { display: grid; - grid-template-columns: auto auto ; + grid-template-columns: auto auto; } -.bulk-container{ - display:grid; +.bulk-container { + display: grid; grid-template-columns: 50% 50%; height: 100%; background-color: white; } -.code-editor-body{ +.code-editor-body { overflow: auto; height: calc(100vh - 150px); background: white; } -.bulk-output-body{ +.bulk-output-body { letter-spacing: 0.2px; height: calc(100vh - 600px); overflow: scroll; font-family: monospace; } -.bulk-run-button{ +.bulk-run-button { max-height: 32px; min-width: 72px; padding: 0 12px; -} \ No newline at end of file +} diff --git a/src/components/common/eaEmptyState/EAEmptyState.tsx b/src/components/common/eaEmptyState/EAEmptyState.tsx index 8b2f739d2d..3f27a21ace 100644 --- a/src/components/common/eaEmptyState/EAEmptyState.tsx +++ b/src/components/common/eaEmptyState/EAEmptyState.tsx @@ -1,9 +1,9 @@ -import React from 'react'; -import appDetailEmpty from '../../../assets/img/ic-empty-ea-app-detail.png'; -import securityEmpty from '../../../assets/img/ic-empty-ea--security.png'; -import { URLS } from '../../../config'; -import './eaEmptyState.css'; -import { NavLink } from 'react-router-dom'; +import React from 'react' +import appDetailEmpty from '../../../assets/img/ic-empty-ea-app-detail.png' +import securityEmpty from '../../../assets/img/ic-empty-ea--security.png' +import { URLS } from '../../../config' +import './eaEmptyState.css' +import { NavLink } from 'react-router-dom' export enum EAEmptyStateType { DEVTRONAPPS = 'devtron_apps', @@ -16,15 +16,15 @@ function EAEmptyState({ title, msg, stateType, knowMoreLink, headerText = undefi return (
{headerText && ( -
+
{headerText}
)}
{title}
{msg}
-
- +
+ @@ -41,7 +41,7 @@ function EAEmptyState({ title, msg, stateType, knowMoreLink, headerText = undefi width="800" alt="no apps found" /> - ); + ) case EAEmptyStateType.BULKEDIT: return ( no apps found - ); + ) case EAEmptyStateType.DEPLOYMENTGROUPS: - return no apps found; + return no apps found case EAEmptyStateType.SECURITY: return ( no apps found - ); + ) } })()}
- ); + ) } -export default EAEmptyState; +export default EAEmptyState diff --git a/src/components/common/header/PageHeader.tsx b/src/components/common/header/PageHeader.tsx new file mode 100644 index 0000000000..a18ff418b9 --- /dev/null +++ b/src/components/common/header/PageHeader.tsx @@ -0,0 +1,59 @@ +import React from 'react' +import Tippy from '@tippyjs/react' +import { ReactComponent as Question } from '../../../assets/icons/ic-help-outline.svg' + +export interface PageHeaderType { + headerName: string + buttonText?: string + onClickCreateButton?: () => void + isTippyShown?: boolean + showCreateButton?: boolean + tippyRedirectLink?: string + CreateButtonIcon?: React.FunctionComponent + showIconBeforeText?: boolean +} + +function PageHeader({ + headerName, + buttonText, + onClickCreateButton, + isTippyShown = false, + showCreateButton = false, + tippyRedirectLink, + CreateButtonIcon, + showIconBeforeText, +}: PageHeaderType) { + return ( +
+

+ {headerName} + {isTippyShown && ( + Learn more } + > + + + + + )} +

+ {showCreateButton && ( + + )} +
+ ) +} + +export default PageHeader diff --git a/src/components/common/index.ts b/src/components/common/index.ts index c696f76405..07e4b11b0a 100644 --- a/src/components/common/index.ts +++ b/src/components/common/index.ts @@ -1,50 +1,51 @@ -export * from './errorScreens/error'; -export * from './errorScreens/error'; -export { default as ErrorBoundary } from './errorBoundary'; -export { default as RectangularEdge } from './edge/rectangularEdge'; -export { default as PopupMenu } from './PopupMenu/PopupMenu'; -export { default as Select } from './Select/Select'; -export * from './MultiSelect'; -export { default as List } from './List/List'; +export * from './errorScreens/error' +export * from './errorScreens/error' +export { default as ErrorBoundary } from './errorBoundary' +export { default as RectangularEdge } from './edge/rectangularEdge' +export { default as PopupMenu } from './PopupMenu/PopupMenu' +export { default as Select } from './Select/Select' +export * from './MultiSelect' +export { default as List } from './List/List' export { default as LazyImage } from './LazyImage/LazyImage' -export * from './formFields/CopyButton'; -export * from './formFields/Typeahead'; -export * from './formFields/ButtonWithLoader'; -export * from './formFields/Checkbox'; -export * from './formFields/DevtronSwitch'; -export * from './modals/OpaqueModal'; -export * from './modals/VisibleModal'; -export * from './modals/VisibleModal2'; -export * from './dialogs/DeleteDialog'; -export * from './modals/Modal'; -export * from './dialogs/DialogForm'; -export { default as ConfirmationDialog } from './dialogs/ConfirmationDialog'; -export { default as Toggle } from './Toggle/Toggle'; -export * from './icons/Icons'; -export * from './icons/Progressing'; -export * from './filter/filters'; -export * from './filter/types'; -export * from './pagination/Pagination'; -export { default as RadioGroup } from './RadioGroup/RadioGroup'; -export * from './helpers/Helpers'; -export * from './helpers/isSubset'; -export * from './helpers/workflowURL'; -export * from './helpers/isEmpty'; -export * from './helpers/git'; -export * from './helpers/UseWindowSize'; -export * from './helpers/util'; -export * from './helpers/time'; -export * from './ToastBody'; +export * from './formFields/CopyButton' +export * from './formFields/Typeahead' +export * from './formFields/ButtonWithLoader' +export * from './formFields/Checkbox' +export * from './formFields/DevtronSwitch' +export * from './modals/OpaqueModal' +export * from './modals/VisibleModal' +export * from './modals/VisibleModal2' +export * from './dialogs/DeleteDialog' +export * from './modals/Modal' +export * from './dialogs/DialogForm' +export { default as ConfirmationDialog } from './dialogs/ConfirmationDialog' +export { default as Toggle } from './Toggle/Toggle' +export * from './icons/Icons' +export * from './icons/Progressing' +export * from './filter/filters' +export * from './filter/types' +export * from './pagination/Pagination' +export { default as RadioGroup } from './RadioGroup/RadioGroup' +export * from './helpers/Helpers' +export * from './helpers/isSubset' +export * from './helpers/workflowURL' +export * from './helpers/isEmpty' +export * from './helpers/git' +export * from './helpers/UseWindowSize' +export * from './helpers/util' +export * from './helpers/time' +export * from './ToastBody' export { BreadCrumb, useBreadcrumb } from './BreadCrumb/BreadCrumb' export { default as BreadcrumbStore } from './BreadCrumb/BreadcrumbStore' -export { GitMaterialInfo, GitCommitDetailCard } from './GitMaterialInfo'; -export * from './GitMaterialInfo'; -export * from './Contexts'; -export { CustomPassword } from './formFields/CustomPassword'; -export { default as Drawer } from './Drawer/Drawer'; -export { CustomInput } from './formFields/CustomInput'; -export * from './security/ScanVulnerabilitiesTable'; -export * from './security/ScanDetailsModal'; -export * from './DatePickers/Calender'; -export * from './DatePickers/DayPickerRangeController'; -export * from './helpers/compareVersion'; \ No newline at end of file +export { GitMaterialInfo, GitCommitDetailCard } from './GitMaterialInfo' +export * from './GitMaterialInfo' +export * from './Contexts' +export { CustomPassword } from './formFields/CustomPassword' +export { default as Drawer } from './Drawer/Drawer' +export { CustomInput } from './formFields/CustomInput' +export * from './security/ScanVulnerabilitiesTable' +export * from './security/ScanDetailsModal' +export * from './DatePickers/Calender' +export * from './DatePickers/DayPickerRangeController' +export * from './helpers/compareVersion' +export * from './header/PageHeader' diff --git a/src/components/deploymentGroups/BulkActions.scss b/src/components/deploymentGroups/BulkActions.scss index 536f60a940..70537d1be5 100644 --- a/src/components/deploymentGroups/BulkActions.scss +++ b/src/components/deploymentGroups/BulkActions.scss @@ -7,7 +7,7 @@ $minWidth: 800px; .form-section { width: 100%; - flex: 1 0 calc( 100% - 64px); + flex: 1 0 calc(100% - 64px); .form-container { width: $minWidth; margin: 0 auto; @@ -81,7 +81,7 @@ .ci-pipelines, .environments { overflow: auto; - input[type="radio"] { + input[type='radio'] { margin-right: 4px; } } @@ -190,7 +190,7 @@ input { border: unset; background: transparent; - width: calc( 100% - 24px) + width: calc(100% - 24px); } } } @@ -216,7 +216,7 @@ height: 40px; font-size: 14px; color: var(--N900); - input[type="checkbox"] { + input[type='checkbox'] { margin-right: 12px; height: 16px; width: 16px; @@ -251,12 +251,6 @@ } } -.deployment-group-list-page{ - height: 100%; - display: grid; - grid-template-rows: 80px 1fr; -} - .deployment-details-body { background-color: var(--window-bg); padding: 20px 24px; @@ -489,7 +483,7 @@ table.group-details { fill: var(--N600); } -.deployment-group__actions .popup-button:hover svg path[fill="#999"] { +.deployment-group__actions .popup-button:hover svg path[fill='#999'] { fill: var(--N600); } @@ -510,4 +504,4 @@ table.group-details { .deployment-detail-page__more-actions--button { padding: 0 16px; height: 40px; -} \ No newline at end of file +} diff --git a/src/components/deploymentGroups/DeploymentGroupList.tsx b/src/components/deploymentGroups/DeploymentGroupList.tsx index 5695a5bc01..8114d19b3c 100644 --- a/src/components/deploymentGroups/DeploymentGroupList.tsx +++ b/src/components/deploymentGroups/DeploymentGroupList.tsx @@ -1,264 +1,339 @@ -import React, { Component } from 'react'; -import { RouteComponentProps } from 'react-router'; -import { ErrorScreenManager, Progressing, PopupMenu, showError, DeleteDialog } from '../common'; -import { ViewType, SourceTypeMap, URLS } from '../../config'; -import { deploymentGroupList, triggerGroupDeploy, getCDMaterialList, deleteDeploymentGroup } from './service'; +import React, { Component } from 'react' +import { RouteComponentProps } from 'react-router' +import { ErrorScreenManager, Progressing, PopupMenu, showError, DeleteDialog } from '../common' +import { ViewType, SourceTypeMap, URLS } from '../../config' +import { deploymentGroupList, triggerGroupDeploy, getCDMaterialList, deleteDeploymentGroup } from './service' import { ReactComponent as Add } from '../../assets/icons/ic-add.svg' import { ReactComponent as Branch } from '../../assets/icons/misc/branch.svg' -import { ReactComponent as Deploy } from '../../assets/icons/ic-deploy.svg'; -import { ReactComponent as Dots } from '../../assets/icons/appstatus/ic-menu-dots.svg'; -import { CDMaterial } from '../app/details/triggerView/cdMaterial'; -import { CDMaterialType } from '../app/details/triggerView/types'; -import noGroups from '../../assets/img/ic-feature-deploymentgroups@3x.png'; -import { Link } from 'react-router-dom'; -import { toast } from 'react-toastify'; -import Tippy from '@tippyjs/react'; -import { ReactComponent as Delete } from '../../assets/icons/ic-delete.svg'; +import { ReactComponent as Deploy } from '../../assets/icons/ic-deploy.svg' +import { ReactComponent as Dots } from '../../assets/icons/appstatus/ic-menu-dots.svg' +import { CDMaterial } from '../app/details/triggerView/cdMaterial' +import { CDMaterialType } from '../app/details/triggerView/types' +import noGroups from '../../assets/img/ic-feature-deploymentgroups@3x.png' +import { Link } from 'react-router-dom' +import { toast } from 'react-toastify' +import Tippy from '@tippyjs/react' +import { ReactComponent as Delete } from '../../assets/icons/ic-delete.svg' +import PageHeader from '../common/header/PageHeader' -export interface BulkActionListProps extends RouteComponentProps<{}> { - -} +export interface BulkActionListProps extends RouteComponentProps<{}> {} export interface BulkActionListState { - code: number; - view: string; - deploymentGroupId: number; - showCDModal: boolean; - envName: string; - materials: CDMaterialType[]; - isLoading: boolean; - showGroupDeleteModal: boolean; - list: any[]; + code: number + view: string + deploymentGroupId: number + showCDModal: boolean + envName: string + materials: CDMaterialType[] + isLoading: boolean + showGroupDeleteModal: boolean + list: any[] } -export default class DeploymentGroupList extends Component{ +export default class DeploymentGroupList extends Component { constructor(props) { - super(props); + super(props) this.state = { code: 0, view: ViewType.LOADING, deploymentGroupId: 0, - envName: "", + envName: '', showCDModal: false, materials: [], isLoading: false, list: [], showGroupDeleteModal: false, } - this.selectImage = this.selectImage.bind(this); - this.toggleSourceInfo = this.toggleSourceInfo.bind(this); - this.triggerDeploy = this.triggerDeploy.bind(this); - + this.selectImage = this.selectImage.bind(this) + this.toggleSourceInfo = this.toggleSourceInfo.bind(this) + this.triggerDeploy = this.triggerDeploy.bind(this) } componentDidMount() { - this.deploymentGroupList(); + this.deploymentGroupList() } deploymentGroupList() { - deploymentGroupList().then((response) => { - let view = ViewType.FORM; - if (response.result.length === 0) view = ViewType.EMPTY - this.setState({ view: view, list: response.result, deploymentGroupId: 0, showGroupDeleteModal: false, showCDModal: false }); - }).catch((error) => { - this.setState({ view: ViewType.ERROR }); - showError(error); - }) + deploymentGroupList() + .then((response) => { + let view = ViewType.FORM + if (response.result.length === 0) view = ViewType.EMPTY + this.setState({ + view: view, + list: response.result, + deploymentGroupId: 0, + showGroupDeleteModal: false, + showCDModal: false, + }) + }) + .catch((error) => { + this.setState({ view: ViewType.ERROR }) + showError(error) + }) } getCDMaterialList(deploymentGroup) { - getCDMaterialList(deploymentGroup.id).then((response) => { - if (response.result) { - this.setState({ - materials: response.result, - deploymentGroupId: deploymentGroup.id, - envName: deploymentGroup.environmentName, - showCDModal: true - }) - } - }).catch((error) => { - showError(error); - }) + getCDMaterialList(deploymentGroup.id) + .then((response) => { + if (response.result) { + this.setState({ + materials: response.result, + deploymentGroupId: deploymentGroup.id, + envName: deploymentGroup.environmentName, + showCDModal: true, + }) + } + }) + .catch((error) => { + showError(error) + }) } triggerDeploy() { this.setState({ isLoading: true }) - let material = this.state.materials.find((mat) => mat.isSelected); + let material = this.state.materials.find((mat) => mat.isSelected) let request = { DeploymentGroupId: this.state.deploymentGroupId, - CiArtifactId: material.id + CiArtifactId: material.id, } - triggerGroupDeploy(request).then((response) => { - if (response.code === 200) { - toast.success("Deployment Triggerd"); - this.setState({ showCDModal: false, isLoading: false }); - } - }).catch((error) => { - showError(error); - this.setState({ isLoading: false }) - }) + triggerGroupDeploy(request) + .then((response) => { + if (response.code === 200) { + toast.success('Deployment Triggerd') + this.setState({ showCDModal: false, isLoading: false }) + } + }) + .catch((error) => { + showError(error) + this.setState({ isLoading: false }) + }) } deleteDeploymentGroup() { - deleteDeploymentGroup(this.state.deploymentGroupId).then((response) => { - if (response.result) { - toast.success('Deployment Group Deleted'); - this.deploymentGroupList(); - } - }).catch((error) => { - showError(error); - }) + deleteDeploymentGroup(this.state.deploymentGroupId) + .then((response) => { + if (response.result) { + toast.success('Deployment Group Deleted') + this.deploymentGroupList() + } + }) + .catch((error) => { + showError(error) + }) } toggleSourceInfo(index: number) { - let { materials } = { ...this.state }; - materials[index].showSourceInfo = !materials[index].showSourceInfo; - this.setState({ materials }); + let { materials } = { ...this.state } + materials[index].showSourceInfo = !materials[index].showSourceInfo + this.setState({ materials }) } selectImage(index: number, materialType: string) { - let { materials } = { ...this.state }; + let { materials } = { ...this.state } materials = materials.map((mat, i) => { return { ...mat, - isSelected: index === i + isSelected: index === i, } }) - this.setState({ materials }); + this.setState({ materials }) } redirectToEdit(deploymentGroup) { - const LINK = `${URLS.DEPLOYMENT_GROUPS}/${deploymentGroup.id}/edit`; - this.props.history.push(LINK); + const LINK = `${URLS.DEPLOYMENT_GROUPS}/${deploymentGroup.id}/edit` + this.props.history.push(LINK) } renderListHeader() { - return <> -
-
Name
-
Source
-
Actions
-
- + return ( + <> +
+
Name
+
Source
+
Actions
+
+ + ) } renderListItems() { return this.state.list.map((deploymentGroup) => { - return
- {this.renderGroupName(deploymentGroup)} - {this.renderMaterials(deploymentGroup.ciMaterialDTOs)} - {this.renderActions(deploymentGroup)} -
+ return ( +
+ {this.renderGroupName(deploymentGroup)} + {this.renderMaterials(deploymentGroup.ciMaterialDTOs)} + {this.renderActions(deploymentGroup)} +
+ ) }) } renderGroupName(deploymentGroup) { - return
- {deploymentGroup.name} -
Connected to {deploymentGroup.appCount} apps on {`'${deploymentGroup.environmentName}'`}
-
+ return ( +
+ + {deploymentGroup.name} + +
+ Connected to {deploymentGroup.appCount} apps on {`'${deploymentGroup.environmentName}'`} +
+
+ ) } renderMaterials(materials: any[]) { - return
- {materials?.map((mat, idx) => { - return

- - {mat.name}/ - - {mat.type === SourceTypeMap.BranchFixed ? : null} - - {mat.value} -

- })} -
+ return ( +
+ {materials?.map((mat, idx) => { + return ( +

+ + {mat.name}/ + + {mat.type === SourceTypeMap.BranchFixed ? : null} + + {mat.value} +

+ ) + })} +
+ ) } renderActions(deploymentGroup) { - return
-
event.stopPropagation()}> - - - - - - - - -
    -
  • { this.redirectToEdit(deploymentGroup) }}>Edit
  • -
  • { this.setState({ deploymentGroupId: deploymentGroup.id, showGroupDeleteModal: true }, () => { }) }}> - Delete -
  • -
-
-
+ return ( +
+
event.stopPropagation()}> + + + + + + + + +
    +
  • { + this.redirectToEdit(deploymentGroup) + }} + > + Edit +
  • +
  • { + this.setState( + { deploymentGroupId: deploymentGroup.id, showGroupDeleteModal: true }, + () => {}, + ) + }} + > + Delete + +
  • +
+
+
+
-
+ ) } renderList() { - return <> - {this.renderListHeader()} - {this.renderListItems()} - + return ( + <> + {this.renderListHeader()} + {this.renderListItems()} + + ) } renderCDMaterial() { if (this.state.showCDModal) { - return { this.setState({ showCDModal: false }) }} - /> + return ( + { + this.setState({ showCDModal: false }) + }} + /> + ) } } renderDeleteDialog() { - let group = this.state.list.find(grp => grp.id === this.state.deploymentGroupId) + let group = this.state.list.find((grp) => grp.id === this.state.deploymentGroupId) if (this.state.showGroupDeleteModal) - return { this.setState({ showGroupDeleteModal: false }) }} - delete={() => { this.deleteDeploymentGroup() }} /> + return ( + { + this.setState({ showGroupDeleteModal: false }) + }} + delete={() => { + this.deleteDeploymentGroup() + }} + /> + ) + } + + redirectToCreateGroup = () => { + const LINK = `${URLS.DEPLOYMENT_GROUPS}/0/edit` + this.props.history.push(LINK) } render() { - return( -
-
-
Deployment Groups
- {this.state.view === ViewType.FORM &&
- - Create Group -
} -
+ return ( +
+ +
- { this.state.view === ViewType.LOADING && } - { this.state.view === ViewType.EMPTY && } - { this.state.view === ViewType.ERROR && } - {![ViewType.EMPTY, ViewType.ERROR, ViewType.LOADING].includes(this.state.view) && - {this.renderList()} - {this.renderCDMaterial()} - {this.renderDeleteDialog()} + {this.state.view === ViewType.LOADING && } + {this.state.view === ViewType.EMPTY && } + {this.state.view === ViewType.ERROR && } + {![ViewType.EMPTY, ViewType.ERROR, ViewType.LOADING].includes(this.state.view) && ( + + {this.renderList()} + {this.renderCDMaterial()} + {this.renderDeleteDialog()} - } + )}
-
) +
+ ) } } -function NoDeploymentGroups(){ - return( +function NoDeploymentGroups() { + return (
no apps found @@ -268,8 +343,8 @@ function NoDeploymentGroups(){

Use deployment groups to deploy multiple applications at once.

- Create Group - + Create Group +
) diff --git a/src/components/v2/devtronStackManager/DevtronStackManager.component.tsx b/src/components/v2/devtronStackManager/DevtronStackManager.component.tsx index c6d05632b5..8b88019f7b 100644 --- a/src/components/v2/devtronStackManager/DevtronStackManager.component.tsx +++ b/src/components/v2/devtronStackManager/DevtronStackManager.component.tsx @@ -222,13 +222,13 @@ export const NavItem = ({ ) } -export const PageHeader = ({ +export const StackPageHeader = ({ detailsMode, selectedModule, handleBreadcrumbClick, }: StackManagerPageHeaderType): JSX.Element => { return ( -
+
{!detailsMode &&
Devtron Stack Manager
} {detailsMode === 'discover' && (
diff --git a/src/components/v2/devtronStackManager/DevtronStackManager.tsx b/src/components/v2/devtronStackManager/DevtronStackManager.tsx index 7cd2e0b160..02d2b3f930 100644 --- a/src/components/v2/devtronStackManager/DevtronStackManager.tsx +++ b/src/components/v2/devtronStackManager/DevtronStackManager.tsx @@ -15,7 +15,7 @@ import { ModuleDetailsView, ModulesListingView, NavItem, - PageHeader, + StackPageHeader, } from './DevtronStackManager.component' import { getAllModules, getLogPodName, getModuleInfo, getReleasesNotes } from './DevtronStackManager.service' import { @@ -422,7 +422,7 @@ export default function DevtronStackManager({ : '' }`} > -