From 26f4895864cdab388a84af7b6202461e72a8e099 Mon Sep 17 00:00:00 2001 From: Ilank <63646693+ilan7empest@users.noreply.github.com> Date: Mon, 6 Nov 2023 19:39:18 +0800 Subject: [PATCH 01/10] Fix [Table] workflow name is not expandable (#2060) --- src/components/Table/TableView.js | 5 ++++- src/elements/TableLinkCell/tableLinkCell.scss | 5 +---- src/scss/main.scss | 9 +++++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/components/Table/TableView.js b/src/components/Table/TableView.js index bd711bdde..09e26785d 100644 --- a/src/components/Table/TableView.js +++ b/src/components/Table/TableView.js @@ -20,6 +20,7 @@ such restriction. import React from 'react' import PropTypes from 'prop-types' import { isEmpty } from 'lodash' +import classNames from 'classnames' import ArtifactsTableRow from '../../elements/ArtifactsTableRow/ArtifactsTableRow' import ConsumerGroupShardLagTableRow from '../../elements/ConsumerGroupShardLagTableRow/ConsumerGroupShardLagTableRow' @@ -76,11 +77,13 @@ const TableView = ({ tableHeadRef, tablePanelRef }) => { + const tableClass = classNames('table', !isEmpty(selectedItem) && 'table-with-details') + return (
- +
{pageData.tableHeaders && ( <> diff --git a/src/elements/TableLinkCell/tableLinkCell.scss b/src/elements/TableLinkCell/tableLinkCell.scss index 40827a8dd..777469ab1 100644 --- a/src/elements/TableLinkCell/tableLinkCell.scss +++ b/src/elements/TableLinkCell/tableLinkCell.scss @@ -15,10 +15,6 @@ color: $primary; font-weight: bold; - &:hover { - text-decoration: underline; - } - &.function-name { max-width: 120px; } @@ -36,6 +32,7 @@ .link { display: flex; width: 100%; + color: $primary; } } diff --git a/src/scss/main.scss b/src/scss/main.scss index 0effce373..1687cb9a9 100644 --- a/src/scss/main.scss +++ b/src/scss/main.scss @@ -245,8 +245,6 @@ main { position: sticky; left: 0; flex-basis: 240px; - min-width: 240px; - max-width: 240px; z-index: 1; } @@ -426,6 +424,13 @@ main { } } } + + &.table-with-details { + .table-cell-name { + min-width: 240px; + max-width: 240px; + } + } } .sortable-header { From 51916bc0d3626ea9a597dda846e75f77385c2a46 Mon Sep 17 00:00:00 2001 From: Ilank <63646693+ilan7empest@users.noreply.github.com> Date: Mon, 6 Nov 2023 19:39:44 +0800 Subject: [PATCH 02/10] Fix [Action Menu] Row background color on option hover (#2051) --- src/common/ActionsMenu/ActionsMenu.js | 153 +++++++++--------- src/common/ActionsMenu/actionsMenu.scss | 28 ++-- src/components/Table/table.scss | 4 + .../ActionMenuItem/ActionsMenuItem.js | 32 ++-- .../ActionMenuItem/actionsMenuItem.scss | 12 +- src/scss/main.scss | 4 +- 6 files changed, 121 insertions(+), 112 deletions(-) diff --git a/src/common/ActionsMenu/ActionsMenu.js b/src/common/ActionsMenu/ActionsMenu.js index 5a9b66b84..452a0ff05 100644 --- a/src/common/ActionsMenu/ActionsMenu.js +++ b/src/common/ActionsMenu/ActionsMenu.js @@ -17,14 +17,13 @@ illegal under applicable law, and the grant of the foregoing license under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ -import React, { useEffect, useRef, useState } from 'react' -import { createPortal } from 'react-dom' +import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react' import PropTypes from 'prop-types' import { isEmpty } from 'lodash' import classnames from 'classnames' import ActionsMenuItem from '../../elements/ActionMenuItem/ActionsMenuItem' -import { RoundedIcon } from 'igz-controls/components' +import { PopUpDialog, RoundedIcon } from 'igz-controls/components' import { ACTIONS_MENU } from '../../types' @@ -33,86 +32,78 @@ import { ReactComponent as ActionMenuIcon } from 'igz-controls/images/elipsis.sv import './actionsMenu.scss' const ActionsMenu = ({ dataItem, menu, time, withQuickActions }) => { - const [isShowMenu, setIsShowMenu] = useState(false) - const [isIconDisplayed, setIsIconDisplayed] = useState(false) const [actionMenu, setActionMenu] = useState(menu) - const [renderMenu, setRenderMenu] = useState(false) + const [isIconDisplayed, setIsIconDisplayed] = useState(false) + const [isShowMenu, setIsShowMenu] = useState(false) + const [position, setPosition] = useState('bottom-left') const actionMenuRef = useRef() + const actionMenuBtnRef = useRef() const dropDownMenuRef = useRef() const mainActionsWrapperRef = useRef() - const actionMenuBtnRef = useRef() + const { bottom: actionMenubottom } = actionMenuRef?.current?.getBoundingClientRect() || {} + + let idTimeout = null const actionMenuClassNames = classnames( 'actions-menu__container', withQuickActions && 'actions-menu__container_extended', isShowMenu && 'actions-menu__container-active' ) - const dropDownMenuClassNames = classnames('actions-menu__body', isShowMenu && 'show') - let idTimeout = null - const offset = 15 - - useEffect(() => { - if (!isEmpty(dataItem)) { - setActionMenu(typeof menu === 'function' ? menu(dataItem) : menu) - } - }, [dataItem, menu]) - - useEffect(() => { - setIsIconDisplayed(actionMenu[0].some(menuItem => menuItem.icon)) - }, [actionMenu]) - - const showActionsList = () => { - setIsShowMenu(show => !show) - const actionMenuBtnRect = actionMenuBtnRef.current.getBoundingClientRect() - const dropDownMenuRect = dropDownMenuRef.current.getBoundingClientRect() + const hideActionMenu = useCallback(event => { if ( - actionMenuBtnRect.top + actionMenuBtnRect.height + offset + dropDownMenuRect.height >= - window.innerHeight + !event.target.closest('.actions-menu-button') && + !event.target.closest('.actions-menu__body') ) { - dropDownMenuRef.current.style.top = `${actionMenuBtnRect.top - dropDownMenuRect.height}px` - dropDownMenuRef.current.style.left = `${ - actionMenuBtnRect.left - dropDownMenuRect.width + offset - }px` - } else { - dropDownMenuRef.current.style.top = `${actionMenuBtnRect.bottom}px` - dropDownMenuRef.current.style.left = `${ - actionMenuBtnRect.left - (dropDownMenuRect.width - offset) - }px` + setIsShowMenu(false) } - } + }, []) const onMouseOut = () => { if (isShowMenu) { idTimeout = setTimeout(() => { setIsShowMenu(false) - setRenderMenu(false) }, time) } } const handleMouseOver = event => { if (mainActionsWrapperRef.current?.contains(event.target)) { - setRenderMenu(false) setIsShowMenu(false) - } else { - setRenderMenu(true) } if (idTimeout) clearTimeout(idTimeout) } - const handleScroll = () => { - setIsShowMenu(false) - } + useLayoutEffect(() => { + if (dropDownMenuRef?.current) { + const { height } = dropDownMenuRef.current.getBoundingClientRect() + + actionMenubottom + height > window.innerHeight + ? setPosition('top-left') + : setPosition('bottom-left') + } + }, [isShowMenu, actionMenubottom]) useEffect(() => { - if (isShowMenu) { - window.addEventListener('scroll', handleScroll, true) + if (!isEmpty(dataItem)) { + setActionMenu(typeof menu === 'function' ? menu(dataItem) : menu) } + }, [dataItem, menu]) - return () => window.removeEventListener('scroll', handleScroll, true) - }, [isShowMenu]) + useEffect(() => { + setIsIconDisplayed(actionMenu[0].some(menuItem => menuItem.icon)) + }, [actionMenu]) + + useEffect(() => { + window.addEventListener('click', hideActionMenu) + window.addEventListener('scroll', hideActionMenu, true) + + return () => { + window.removeEventListener('click', hideActionMenu) + window.removeEventListener('scroll', hideActionMenu, true) + } + }, [hideActionMenu]) return (
{ ))}
)} - - - - {renderMenu && - createPortal( -
{ - setIsShowMenu(false) - setRenderMenu(false) - event.stopPropagation() +
+ { + setIsShowMenu(prevValue => !prevValue) + }} + ref={actionMenuBtnRef} + > + + + {isShowMenu && ( + - {actionMenu[0].map( - (menuItem, idx) => - !menuItem.hidden && ( - - ) - )} -
, - document.getElementById('overlay_container') +
    + {actionMenu[0].map( + (menuItem, idx) => + !menuItem.hidden && ( + + ) + )} +
+ )} +
) } diff --git a/src/common/ActionsMenu/actionsMenu.scss b/src/common/ActionsMenu/actionsMenu.scss index 7752f3851..20a257149 100644 --- a/src/common/ActionsMenu/actionsMenu.scss +++ b/src/common/ActionsMenu/actionsMenu.scss @@ -3,17 +3,20 @@ @import '~igz-controls/scss/shadows'; .actions-menu { + position: relative; + &__container { position: relative; display: none; &_extended { position: absolute; - right: 5px; + right: 0; display: none; align-items: center; justify-content: center; background-color: $ghostWhite; + height: 100%; &:before { content: ''; @@ -24,6 +27,10 @@ left: -30px; background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(245, 247, 255, 1) 100%); } + + .actions-menu { + padding: 0 5px 0 0; + } } &-active { @@ -38,17 +45,18 @@ } &__body { - position: fixed; min-width: 150px; max-width: 250px; - background: $white; - border: $primaryBorder; - border-radius: $mainBorderRadius; - box-shadow: $filterShadow; - visibility: hidden; - - &.show { - visibility: visible; + + .pop-up-dialog { + width: 100%; + padding: 0; } } + + &__list { + list-style-type: none; + margin: 0; + padding: 0; + } } diff --git a/src/components/Table/table.scss b/src/components/Table/table.scss index 28ac73eb5..02d2f135d 100644 --- a/src/components/Table/table.scss +++ b/src/components/Table/table.scss @@ -77,6 +77,10 @@ max-width: 150px; } } + + &:has(.actions-menu__container-active) { + background-color: $ghostWhite; + } } &.table { diff --git a/src/elements/ActionMenuItem/ActionsMenuItem.js b/src/elements/ActionMenuItem/ActionsMenuItem.js index 69d248f3a..7c940ea59 100644 --- a/src/elements/ActionMenuItem/ActionsMenuItem.js +++ b/src/elements/ActionMenuItem/ActionsMenuItem.js @@ -37,26 +37,26 @@ const ActionsMenuItem = ({ dataItem, index, isIconDisplayed, menuItem }) => { ) return ( - } - hidden={!menuItem.tooltip} - key={menuItem.label} +
  • { + if (!menuItem.disabled) { + menuItem.onClick(dataItem) + } else { + event.stopPropagation() + } + }} > -
    { - if (!menuItem.disabled) { - menuItem.onClick(dataItem) - } else { - event.stopPropagation() - } - }} + } + hidden={!menuItem.tooltip} + key={menuItem.label} > {menuItem.icon} {menuItem.label} -
    - + +
  • ) } diff --git a/src/elements/ActionMenuItem/actionsMenuItem.scss b/src/elements/ActionMenuItem/actionsMenuItem.scss index af7139b4f..f4a7c40b2 100644 --- a/src/elements/ActionMenuItem/actionsMenuItem.scss +++ b/src/elements/ActionMenuItem/actionsMenuItem.scss @@ -23,10 +23,6 @@ } &__option { - display: flex; - flex: 1; - align-items: center; - justify-content: flex-start; padding: 10px; color: $primary; cursor: pointer; @@ -57,5 +53,13 @@ } } } + + & > * { + display: flex; + flex: 1; + align-items: center; + justify-content: flex-start; + width: 100%; + } } } diff --git a/src/scss/main.scss b/src/scss/main.scss index 1687cb9a9..89d40b776 100644 --- a/src/scss/main.scss +++ b/src/scss/main.scss @@ -273,13 +273,11 @@ main { .table-cell-icon { .actions-menu { &__container_extended { - background-color: $white; - &:before { background: linear-gradient( 90deg, rgba(255, 255, 255, 0) 0%, - rgba(255, 255, 255, 1) 100% + rgba(245, 247, 255, 1) 100% ); } } From 6962e878ca767e7fb5e0d182001ef2ed740b0029 Mon Sep 17 00:00:00 2001 From: Ilank <63646693+ilan7empest@users.noreply.github.com> Date: Mon, 6 Nov 2023 19:39:57 +0800 Subject: [PATCH 03/10] Fix [Feature Store] inaccurate validation message for redis (#2059) --- .../FeatureSetsPanelTargetStoreView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/FeatureSetsPanelTargetStoreView.js b/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/FeatureSetsPanelTargetStoreView.js index 170103839..966cb7310 100644 --- a/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/FeatureSetsPanelTargetStoreView.js +++ b/src/components/FeatureSetsPanel/FeatureSetsPanelTargetStore/FeatureSetsPanelTargetStoreView.js @@ -133,7 +133,7 @@ const FeatureSetsPanelTargetStoreView = ({ invalid={!validation.isOnlineTargetPathValid} invalidText={ data.online.kind === REDISNOSQL && /[{}]/g.test(data.online.path) - ? 'Invalid Redis URL, change the URL to a valid URL in the form of :///[:port]' + ? 'Invalid Redis URL, change the URL to a valid URL in the form of ://[:port]' : '' } label="Path" From 6c95a762031de3e5c7b434d2112de2329262cae0 Mon Sep 17 00:00:00 2001 From: illia-prokopchuk <78905712+illia-prokopchuk@users.noreply.github.com> Date: Tue, 7 Nov 2023 18:18:45 +0200 Subject: [PATCH 04/10] Impl [Batch Run] Model Training (#2066) --- src/actions/functions.js | 4 +- src/common/NameFilter/nameFilter.scss | 2 + src/common/TargetPath/targetPath.util.js | 2 +- .../ArtifactsActionBar/ArtifactsActionBar.js | 39 ++++++++++------- src/components/Datasets/Datasets.js | 8 ++-- src/components/Datasets/DatasetsView.js | 14 ++++--- src/components/Datasets/datasets.util.js | 29 ++++++++++++- src/components/Files/FilesView.js | 14 ++++--- src/components/JobWizard/JobWizard.js | 24 ++++++++++- src/components/JobWizard/JobWizard.util.js | 28 +++++++++++-- .../JobWizardFunctionSelection.js | 42 ++++++++++++------- .../jobWizardFunctionSelection.util.js | 18 ++++++++ .../JobWizardRunDetails.js | 4 +- src/components/ModelsPage/Models/Models.js | 10 +++++ .../ModelsPage/Models/ModelsView.js | 28 +++++++++---- .../ProjectOverview/ProjectOverview.util.js | 5 +-- src/scss/main.scss | 6 ++- src/utils/generateTemplatesCategories.js | 16 ++++--- src/utils/panelPathScheme.js | 2 +- 19 files changed, 217 insertions(+), 78 deletions(-) diff --git a/src/actions/functions.js b/src/actions/functions.js index 42cbe89cd..01ccb07ee 100644 --- a/src/actions/functions.js +++ b/src/actions/functions.js @@ -287,13 +287,13 @@ const functionsActions = { type: FETCH_HUB_FUNCTION_TEMPLATE_FAILURE, payload: err }), - fetchHubFunctions: () => dispatch => { + fetchHubFunctions: allowedHubFunctions => dispatch => { dispatch(functionsActions.fetchHubFunctionsBegin()) return functionsApi .getHubFunctions() .then(({ data: functionTemplates }) => { - const templatesData = generateHubCategories(functionTemplates.catalog) + const templatesData = generateHubCategories(functionTemplates.catalog, allowedHubFunctions) dispatch(functionsActions.setHubFunctions(templatesData)) diff --git a/src/common/NameFilter/nameFilter.scss b/src/common/NameFilter/nameFilter.scss index 54382f0c9..fe294be76 100644 --- a/src/common/NameFilter/nameFilter.scss +++ b/src/common/NameFilter/nameFilter.scss @@ -16,6 +16,8 @@ such restriction. */ .name-filter { + min-width: 180px; + &__icon { cursor: pointer; } diff --git a/src/common/TargetPath/targetPath.util.js b/src/common/TargetPath/targetPath.util.js index b21f46d96..16def23a2 100644 --- a/src/common/TargetPath/targetPath.util.js +++ b/src/common/TargetPath/targetPath.util.js @@ -275,7 +275,7 @@ export const generateComboboxMatchesList = ( export const generateArtifactsList = artifacts => { const generatedArtifacts = artifacts .map(artifact => { - const key = artifact.link_iteration ? artifact.link_iteration.db_key : artifact.key ?? '' + const key = artifact.link_iteration?.db_key || artifact.db_key || artifact.key || '' return { label: key, id: key diff --git a/src/components/ArtifactsActionBar/ArtifactsActionBar.js b/src/components/ArtifactsActionBar/ArtifactsActionBar.js index da408556c..894298281 100644 --- a/src/components/ArtifactsActionBar/ArtifactsActionBar.js +++ b/src/components/ArtifactsActionBar/ArtifactsActionBar.js @@ -40,7 +40,7 @@ import detailsActions from '../../actions/details' import { ReactComponent as RefreshIcon } from 'igz-controls/images/refresh.svg' function ArtifactsActionBar({ - actionButton, + actionButtons, cancelRequest, filterMenuName, handleRefresh, @@ -154,13 +154,18 @@ function ArtifactsActionBar({
    - {actionButton && !actionButton.hidden && ( -
    + + + {preview.data.headers.map((header, index) => { + return ( + + ) + })} + + + + {content.map((contentItem, contentItemIndex) => ( + + {Array.isArray(contentItem) ? ( + contentItem.map((contentItemValue, contentItemValueIndex) => ( + )) ) : ( - } - > - {contentItem} - + )} - + ))} - - + +
    + }> + {header} +
    + } + > + {typeof contentItemValue === 'object' && contentItemValue !== null + ? JSON.stringify(contentItemValue) + : String(contentItemValue)} + + + }> + {contentItem} + +
    )} {preview?.type === 'text' &&
    {preview?.data.content}
    } {preview?.type === 'html' && ( diff --git a/src/components/ArtifactsPreview/artifactsPreview.scss b/src/components/ArtifactsPreview/artifactsPreview.scss index 78faa3b92..2bbb6b527 100644 --- a/src/components/ArtifactsPreview/artifactsPreview.scss +++ b/src/components/ArtifactsPreview/artifactsPreview.scss @@ -71,54 +71,7 @@ } &__table { - display: flex; - flex-direction: column; - align-items: flex-start; - width: 100%; - color: $mulledWine; - - &-header { - position: sticky; - top: 0; - z-index: 2; - font-weight: 500; - background-color: $white; - } - - &-body { - display: flex; - flex-direction: column; - min-width: 100%; - } - - &-row { - display: flex; - min-width: 100%; - padding: 15px 10px; - border-bottom: $secondaryBorder; - - &:last-child { - border-bottom: none; - } - - &:not(.table-header) { - &:hover { - background-color: $alabaster; - } - } - } - - &__header { - flex: 1; - width: 50px; - padding: 0 5px; - } - - &-content { - flex: 1; - width: 150px; - padding: 0 5px; - } + display: block; } &__image { @@ -142,7 +95,7 @@ height: 100%; min-height: 150px; - .wrapper { + .loader-wrapper { position: absolute; background-color: transparent; } diff --git a/src/components/Datasets/DatasetsView.js b/src/components/Datasets/DatasetsView.js index 5b13a37ba..b399b5d76 100644 --- a/src/components/Datasets/DatasetsView.js +++ b/src/components/Datasets/DatasetsView.js @@ -151,7 +151,7 @@ const DatasetsView = React.forwardRef( )} {artifactsStore?.preview?.isPreview && ( - + )} ) diff --git a/src/components/DetailsArtifacts/DetailsArtifacts.js b/src/components/DetailsArtifacts/DetailsArtifacts.js index 8aa4271bd..f800947f7 100644 --- a/src/components/DetailsArtifacts/DetailsArtifacts.js +++ b/src/components/DetailsArtifacts/DetailsArtifacts.js @@ -21,13 +21,21 @@ import React, { useState, useEffect, useCallback, useMemo } from 'react' import { connect, useSelector } from 'react-redux' import { useParams } from 'react-router-dom' import PropTypes from 'prop-types' +import classnames from 'classnames' -import DetailsArtifactsView from './DetailsArtifactsView' +import ArtifactsPreviewController from '../ArtifactsPreview/ArtifactsPreviewController' +import NoData from '../../common/NoData/NoData' +import { TextTooltipTemplate, Tooltip } from 'igz-controls/components' -import { generateContent, getJobAccordingIteration } from './detailsArtifacts.util' +import jobsActions from '../../actions/jobs' import { generateArtifactIndexes } from '../Details/details.util' +import { + generateArtifactsPreviewContent, + generateArtifactsTabContent, + getJobAccordingIteration +} from './detailsArtifacts.util' -import jobsActions from '../../actions/jobs' +import './detailsArtifacts.scss' const DetailsArtifacts = ({ fetchJob, @@ -37,11 +45,22 @@ const DetailsArtifacts = ({ setIteration, setIterationOption }) => { - const [content, setContent] = useState([]) + const [artifactsPreviewContent, setArtifactsPreviewContent] = useState([]) const [artifactsIndexes, setArtifactsIndexes] = useState([]) const iterationOptions = useSelector(store => store.detailsStore.iterationOptions) const params = useParams() + const showArtifact = useCallback( + index => { + generateArtifactIndexes(artifactsIndexes, index, setArtifactsIndexes) + }, + [artifactsIndexes, setArtifactsIndexes] + ) + + const artifactsTabContent = useMemo(() => { + return generateArtifactsTabContent(artifactsPreviewContent, params, iteration, showArtifact) + }, [artifactsPreviewContent, iteration, params, showArtifact]) + const bestIteration = useMemo( () => selectedItem.results?.best_iteration, [selectedItem.results?.best_iteration] @@ -87,33 +106,70 @@ const DetailsArtifacts = ({ fetchJob(params.projectName, params.jobId, iteration).then(job => { const selectedJob = getJobAccordingIteration(job) - setContent(generateContent(selectedJob)) + setArtifactsPreviewContent(generateArtifactsPreviewContent(selectedJob)) }) } else if (selectedItem.iterationStats.length === 0) { - setContent(generateContent(selectedItem)) + setArtifactsPreviewContent(generateArtifactsPreviewContent(selectedItem)) } return () => { - setContent([]) + setArtifactsPreviewContent([]) setArtifactsIndexes([]) } }, [fetchJob, iteration, params.jobId, params.projectName, selectedItem]) - const showArtifact = useCallback( - index => { - generateArtifactIndexes(artifactsIndexes, index, setArtifactsIndexes) - }, - [artifactsIndexes, setArtifactsIndexes] - ) - - return ( - + return jobsStore.loading ? null : artifactsPreviewContent.length === 0 ? ( + + ) : ( +
    +
    +
    +
    + {artifactsTabContent[0].map(({ headerId, headerLabel, className }) => ( +
    + {headerLabel} +
    + ))} +
    +
    +
    + {artifactsTabContent.map((artifactRow, artifactRowIndex) => ( +
    +
    + {artifactRow.map((artifactCell, artifactCellIndex) => ( +
    + {artifactCell.template ? ( + artifactCell.template + ) : ( + + } + > + {artifactCell.value} + + )} +
    + ))} +
    + +
    + ))} +
    +
    +
    ) } diff --git a/src/components/DetailsArtifacts/detailsArtifacts.scss b/src/components/DetailsArtifacts/detailsArtifacts.scss index 5bfa4bd0a..15d8c8b37 100644 --- a/src/components/DetailsArtifacts/detailsArtifacts.scss +++ b/src/components/DetailsArtifacts/detailsArtifacts.scss @@ -5,48 +5,21 @@ .item-artifacts { height: 100%; - &__arrow { - position: absolute; - top: 22px; - left: 10px; + .artifact-preview { + height: 300px; + overflow: auto; } - &__accordion-wrapper { - height: 70px; - } - - &__row { - position: relative; - display: flex; - min-height: 50px; - border-bottom: $secondaryBorder; - - &-item { - display: flex; - flex: 1; - align-items: center; - min-width: 100px; - padding: 8px 26px; - - &:first-child { - flex-direction: column; - align-items: flex-start; - justify-content: center; - min-width: 170px; - } - - &_long { - flex: 2; + .table { + .table-body__cell { + a { + width: auto; } } - } - .icon-download { - cursor: pointer; - } - - &__name { - width: 100%; - cursor: pointer; + .actions-cell { + min-width: 140px; + max-width: 140px; + } } } diff --git a/src/components/DetailsArtifacts/detailsArtifacts.util.js b/src/components/DetailsArtifacts/detailsArtifacts.util.js index b622a3a2c..2ebfacab9 100644 --- a/src/components/DetailsArtifacts/detailsArtifacts.util.js +++ b/src/components/DetailsArtifacts/detailsArtifacts.util.js @@ -17,10 +17,18 @@ illegal under applicable law, and the grant of the foregoing license under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ +import { Link } from 'react-router-dom' import prettyBytes from 'pretty-bytes' +import CopyToClipboard from '../../common/CopyToClipboard/CopyToClipboard' +import Download from '../../common/Download/Download' +import { DATASETS, MODELS_TAB, TAG_FILTER_LATEST } from '../../constants' +import { RoundedIcon, TextTooltipTemplate, Tooltip } from 'igz-controls/components' import { formatDatetime, parseKeyValues } from '../../utils' import { generateArtifactPreviewData } from '../../utils/generateArtifactPreviewData' +import { parseArtifacts } from '../../utils/parseArtifacts' + +import { ReactComponent as DetailsIcon } from 'igz-controls/images/view-details.svg' export const getJobAccordingIteration = selectedJob => { return { @@ -30,46 +38,107 @@ export const getJobAccordingIteration = selectedJob => { } } -export const generateContent = selectedJob => { - return selectedJob.artifacts.map(artifact => { - const artifactExtraData = artifact.extra_data || artifact.spec?.extra_data - const artifactSchema = artifact.schema || artifact.spec?.schema +export const generateArtifactsPreviewContent = selectedJob => { + const parsedArtifacts = parseArtifacts(selectedJob.artifacts) + + if (!parsedArtifacts) return [] + + return parsedArtifacts.map(artifact => { let generatedPreviewData = { preview: [] } - if (artifactExtraData) { - generatedPreviewData = generateArtifactPreviewData(artifactExtraData) + if (artifact.extra_data) { + generatedPreviewData = generateArtifactPreviewData(artifact.extra_data) } - const generatedArtifact = { + artifact.preview = artifact.schema ? artifact.preview : generatedPreviewData.preview + artifact.header = artifact.schema ? artifact.header : null + + artifact.ui = { + ...artifact.ui, date: formatDatetime(selectedJob.startTime), - db_key: artifact.db_key ?? artifact.spec?.db_key, - key: artifact.key ?? artifact.metadata?.key, - kind: artifact.kind ?? artifact.spec?.kind, - iter: artifact.iter ?? artifact.metadata?.iter, - preview: generatedPreviewData.preview, - size: - artifact.size || artifact.spec?.size - ? prettyBytes(artifact.size || artifact.spec?.size) - : 'N/A', - target_path: artifact.target_path ?? artifact.spec?.target_path, - tag: artifact.tag ?? artifact.metadata?.tag, - tree: artifact.tree ?? artifact.metadata?.tree, + size: artifact.size ? prettyBytes(artifact.size) : 'N/A', user: selectedJob?.labels ?.find(item => item.match(/v3io_user|owner/g)) ?.replace(/(v3io_user|owner): /, '') } - if (artifactSchema) { - return { - ...generatedArtifact, - header: artifact.header || artifact.spec.header, - preview: artifact.preview || artifact.status.preview, - schema: artifactSchema - } + return artifact + }) +} + +export const generateArtifactsTabContent = (artifacts, params, iteration, showArtifact) => { + return artifacts.map((artifact, index) => { + const artifactScreenLinks = { + model: `/projects/${params.projectName}/models/${MODELS_TAB}/${ + artifact.db_key || artifact.key + }/${artifact.tag ?? TAG_FILTER_LATEST}${iteration ? `/${iteration}` : ''}/overview`, + dataset: `/projects/${params.projectName}/${DATASETS}/${artifact.db_key || artifact.key}/${ + artifact.tag ?? TAG_FILTER_LATEST + }${iteration ? `/${iteration}` : ''}/overview` } - return generatedArtifact + return [ + { + headerId: 'name', + headerLabel: 'Name', + template: ( + }> + showArtifact(index)}> + {artifact.db_key || artifact.key} + + + ), + value: artifact.db_key || artifact.key, + className: 'table-cell-3' + }, + { + headerId: 'path', + headerLabel: 'Path', + value: artifact.target_path, + className: 'table-cell-6' + }, + { + headerId: 'size', + headerLabel: 'Size', + value: artifact.ui.size + }, + { + headerId: 'updated', + headerLabel: 'Updated', + value: artifact.ui.date, + className: 'table-cell-2' + }, + { + headerId: 'actions', + headerLabel: '', + className: 'actions-cell', + template: ( + <> + + + + + + + + + ) + } + ] }) } diff --git a/src/components/DetailsInputs/DetailsInputs.js b/src/components/DetailsInputs/DetailsInputs.js index f2b555679..894ece693 100644 --- a/src/components/DetailsInputs/DetailsInputs.js +++ b/src/components/DetailsInputs/DetailsInputs.js @@ -17,58 +17,44 @@ illegal under applicable law, and the grant of the foregoing license under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ -import React, { useCallback, useEffect, useState } from 'react' -import PropTypes from 'prop-types' -import { isEmpty } from 'lodash' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' -import { Link, useParams } from 'react-router-dom' +import { useParams } from 'react-router-dom' +import { isEmpty } from 'lodash' +import PropTypes from 'prop-types' import classnames from 'classnames' import ArtifactsPreviewController from '../ArtifactsPreview/ArtifactsPreviewController' -import NoData from '../../common/NoData/NoData' -import { Tooltip, TextTooltipTemplate, RoundedIcon } from 'igz-controls/components' import Loader from '../../common/Loader/Loader' +import NoData from '../../common/NoData/NoData' +import { Tooltip, TextTooltipTemplate } from 'igz-controls/components' +import { MLRUN_STORAGE_INPUT_PATH_SCHEME, TAG_FILTER_LATEST } from '../../constants' import { fetchArtifacts } from '../../reducers/artifactsReducer' import { generateArtifactIndexes } from '../Details/details.util' -import { - DATASETS, - MLRUN_STORAGE_INPUT_PATH_SCHEME, - MODELS_TAB, - TAG_FILTER_LATEST -} from '../../constants' - -import { ReactComponent as DetailsIcon } from 'igz-controls/images/view-details.svg' +import { generateArtifactLink, generateInputsTabContent } from './detailsInputs.util' import './detailsInputs.scss' const DetailsInputs = ({ inputs }) => { const [artifactsIndexes, setArtifactsIndexes] = useState([]) - const [content, setContent] = useState([]) + const [inputsContent, setInputsContent] = useState([]) + + const showArtifact = useCallback( + index => { + generateArtifactIndexes(artifactsIndexes, index, setArtifactsIndexes) + }, + [artifactsIndexes, setArtifactsIndexes] + ) + + const inputsTabContent = useMemo(() => { + return generateInputsTabContent(inputsContent, showArtifact) + }, [inputsContent, showArtifact]) const artifactsStore = useSelector(store => store.artifactsStore) const dispatch = useDispatch() const params = useParams() - const generateArtifactLink = useCallback( - artifact => { - const artifactLinks = { - model: `/projects/${params.projectName}/models/${MODELS_TAB}/${ - artifact.db_key || artifact.key - }/${TAG_FILTER_LATEST}${artifact.iter ? `/${artifact.iter}` : ''}/overview`, - dataset: `/projects/${params.projectName}/${DATASETS}/${ - artifact.db_key || artifact.key - }/${TAG_FILTER_LATEST}${artifact.iter ? `/${artifact.iter}` : ''}/overview`, - files: `/projects/${params.projectName}/files/${ - artifact.db_key || artifact.key - }/${TAG_FILTER_LATEST}${artifact.iter ? `/${artifact.iter}` : ''}/overview` - } - - return artifact ? artifactLinks[artifact.kind ?? 'files'] : '' - }, - [params.projectName] - ) - useEffect(() => { Object.entries(inputs || {}).forEach(([key, value]) => { if (value.startsWith(MLRUN_STORAGE_INPUT_PATH_SCHEME)) { @@ -89,25 +75,25 @@ const DetailsInputs = ({ inputs }) => { .unwrap() .then(artifacts => { if (artifacts.length) { - setContent(state => [ + setInputsContent(state => [ ...state, { ...artifacts[0], - key, - value, ui: { - artifactLink: generateArtifactLink(artifacts[0]), + inputName: key, + inputPath: value, + artifactLink: generateArtifactLink(artifacts[0], params.projectName), isPreviewable: artifacts.length > 0 } } ]) } else { - setContent(state => [ + setInputsContent(state => [ ...state, { - key, - value, ui: { + inputName: key, + inputPath: value, isPreviewable: false } } @@ -115,12 +101,12 @@ const DetailsInputs = ({ inputs }) => { } }) } else { - setContent(state => [ + setInputsContent(state => [ ...state, { - key, - value, ui: { + inputName: key, + inputPath: value, isPreviewable: false } } @@ -129,61 +115,55 @@ const DetailsInputs = ({ inputs }) => { }) return () => { - setContent([]) + setInputsContent([]) setArtifactsIndexes([]) } - }, [inputs, dispatch, generateArtifactLink]) - - const showArtifact = useCallback( - index => { - generateArtifactIndexes(artifactsIndexes, index, setArtifactsIndexes) - }, - [artifactsIndexes, setArtifactsIndexes] - ) + }, [inputs, dispatch, params.projectName]) return artifactsStore.loading ? ( - ) : isEmpty(inputs) || isEmpty(content) ? ( + ) : isEmpty(inputs) || isEmpty(inputsContent) ? ( ) : (
    - {content.map((artifact, index) => { - const keyClassNames = classnames(artifact.ui.isPreviewable && 'item-inputs__name link') - - return ( -
    -
    -
    - }> - artifact.ui.isPreviewable && showArtifact(index)} - > - {artifact.key} - - +
    +
    +
    + {inputsTabContent[0].map(({ headerId, headerLabel, className }) => ( +
    + {headerLabel}
    -
    - {artifact.value} + ))} +
    +
    +
    + {inputsTabContent.map((inputRow, inputRowIndex) => ( +
    +
    + {inputRow.map((inputCell, inputCellIndex) => ( +
    + {inputCell.template ? ( + inputCell.template + ) : ( + }> + {inputCell.value} + + )} +
    + ))}
    - {artifact.ui.isPreviewable && ( -
    - - - - - -
    - )} +
    - -
    - ) - })} + ))} +
    +
    ) } diff --git a/src/components/DetailsInputs/detailsInputs.scss b/src/components/DetailsInputs/detailsInputs.scss index 82c86b5e0..c71a5047a 100644 --- a/src/components/DetailsInputs/detailsInputs.scss +++ b/src/components/DetailsInputs/detailsInputs.scss @@ -1,31 +1,21 @@ @import '~igz-controls/scss/borders'; .item-inputs { - &__row { - position: relative; - display: flex; - min-height: 70px; - padding-left: 13px; - border-bottom: $secondaryBorder; - - &-item { - display: flex; - flex: 1; - align-items: center; - min-width: 100px; - padding: 12px 26px; - - &_path { - flex: 2; - } + .artifact-preview { + height: 300px; + overflow: auto; + } - &_preview { - flex: 0.2; + .table { + .table-body__cell { + a { + width: auto; } } - } - &__name { - cursor: pointer; + .actions-cell { + min-width: 100px; + max-width: 100px; + } } } diff --git a/src/components/DetailsInputs/detailsInputs.util.js b/src/components/DetailsInputs/detailsInputs.util.js new file mode 100644 index 000000000..9accc5df3 --- /dev/null +++ b/src/components/DetailsInputs/detailsInputs.util.js @@ -0,0 +1,91 @@ +/* +Copyright 2019 Iguazio Systems Ltd. + +Licensed under the Apache License, Version 2.0 (the "License") with +an addition restriction as set forth herein. You may not use this +file except in compliance with the License. You may obtain a copy of +the License at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. See the License for the specific language governing +permissions and limitations under the License. + +In addition, you may not use the software for any purposes that are +illegal under applicable law, and the grant of the foregoing license +under the Apache 2.0 license is conditioned upon your compliance with +such restriction. +*/ +import React from 'react' +import { Link } from 'react-router-dom' +import classnames from 'classnames' + +import CopyToClipboard from '../../common/CopyToClipboard/CopyToClipboard' +import { RoundedIcon, TextTooltipTemplate, Tooltip } from 'igz-controls/components' + +import { DATASETS, MODELS_TAB, TAG_FILTER_LATEST } from '../../constants' + +import { ReactComponent as DetailsIcon } from 'igz-controls/images/view-details.svg' + +export const generateArtifactLink = (artifact, projectName) => { + const artifactLinks = { + model: `/projects/${projectName}/models/${MODELS_TAB}/${ + artifact.db_key || artifact.key + }/${TAG_FILTER_LATEST}${artifact.iter ? `/${artifact.iter}` : ''}/overview`, + dataset: `/projects/${projectName}/${DATASETS}/${ + artifact.db_key || artifact.key + }/${TAG_FILTER_LATEST}${artifact.iter ? `/${artifact.iter}` : ''}/overview`, + files: `/projects/${projectName}/files/${artifact.db_key || artifact.key}/${TAG_FILTER_LATEST}${ + artifact.iter ? `/${artifact.iter}` : '' + }/overview` + } + + return artifact ? artifactLinks[artifact.kind ?? 'files'] : '' +} + +export const generateInputsTabContent = (artifacts, showArtifact) => { + return artifacts.map((artifact, index) => { + const keyClassNames = classnames(artifact.ui.isPreviewable && 'link') + + return [ + { + headerId: 'name', + headerLabel: 'Name', + className: 'table-cell-3', + template: ( + }> + artifact.ui.isPreviewable && showArtifact(index)} + > + {artifact.ui.inputName} + + + ) + }, + { + headerId: 'path', + headerLabel: 'Path', + className: 'table-cell-8', + value: artifact.ui.inputPath + }, + { + headerId: 'actions', + headerLabel: '', + className: 'actions-cell', + hidden: !artifact.ui.isPreviewable, + template: ( + <> + + + + + + + + ) + } + ] + }) +} diff --git a/src/components/DetailsResults/DetailsResults.js b/src/components/DetailsResults/DetailsResults.js index c5e1287f9..9244c9b38 100644 --- a/src/components/DetailsResults/DetailsResults.js +++ b/src/components/DetailsResults/DetailsResults.js @@ -19,14 +19,14 @@ such restriction. */ import React, { useMemo } from 'react' import PropTypes from 'prop-types' -import { isEmpty, isObjectLike } from 'lodash' +import { isEmpty } from 'lodash' import classNames from 'classnames' import NoData from '../../common/NoData/NoData' import { Tip, Tooltip, TextTooltipTemplate } from 'igz-controls/components' import { roundFloats } from '../../utils/roundFloats' -import { resultsTableContent, resultsTableHeaders } from '../../utils/resultsTable' +import { generateResultsContent } from '../../utils/resultsTable' import { useSortTable } from '../../hooks/useSortTable.hook' import { ALLOW_SORT_BY, DEFAULT_SORT_BY, EXCLUDE_SORT_BY } from 'igz-controls/types' @@ -35,18 +35,14 @@ import { ReactComponent as BestIteration } from 'igz-controls/images/best-iterat import './detailsResults.scss' const DetailsResults = ({ allowSortBy, defaultSortBy, defaultDirection, excludeSortBy, job }) => { - const tableHeaders = useMemo(() => { - return resultsTableHeaders(job) - }, [job]) - - const tableContent = useMemo(() => { - return resultsTableContent(job) + const resultsContent = useMemo(() => { + return generateResultsContent(job) }, [job]) const { sortTable, selectedColumnName, getSortingIcon, sortedTableContent, sortedTableHeaders } = useSortTable({ - headers: tableHeaders, - content: tableContent, + headers: resultsContent?.[0], + content: resultsContent, sortConfig: { allowSortBy, excludeSortBy, defaultSortBy, defaultDirection } }) @@ -57,69 +53,56 @@ const DetailsResults = ({ allowSortBy, defaultSortBy, defaultDirection, excludeS isSortable && selectedColumnName === headerId && 'sortable-header-cell_active' ) + const getHeaderTemplate = () => { + return ( + + + {sortedTableHeaders.map(({ headerLabel, headerId, isSortable, ...tableItem }) => { + return ( + sortTable(headerId) : null} + > + }> + + + {tableItem.tip && } + + ) + })} + + + ) + } + return (!job.iterationStats?.length && job.error) || (!job.iterationStats?.length && isEmpty(job.results) && !job.iterations?.length) ? ( ) : (
    - +
    {job.iterationStats && job.iterationStats.length !== 0 ? ( <> - - - {sortedTableHeaders.map(({ headerLabel, headerId, isSortable, ...tableItem }) => { - return ( - - ) - })} - - + {getHeaderTemplate()} - {sortedTableContent.map((tableContentItem, index) => ( - - {tableContentItem.map((contentItemValue, idx) => { + {sortedTableContent.map((rowData, rowIndex) => ( + + {rowData.map((cellData, cellIndex) => { if ( - typeof value === 'string' && - contentItemValue.match(/completed|running|error/gi) - ) { - return ( - - ) - } else if ( job.results && - contentItemValue === job.results.best_iteration && - idx === 0 + cellData.value === job.results.best_iteration && + cellIndex === 0 ) { return ( ) @@ -146,34 +129,25 @@ const DetailsResults = ({ allowSortBy, defaultSortBy, defaultDirection, excludeS ) : job.iterations?.length === 0 && Object.keys(job.results ?? {}).length !== 0 ? ( - - {Object.keys(job.results).map(key => { - const resultValue = isObjectLike(job.results[key]) - ? JSON.stringify(job.results[key]) - : job.results[key] - - return ( - - - + <> + {getHeaderTemplate()} + + {sortedTableContent.map((rowData, rowIndex) => ( + + {rowData.map((cellData, cellIndex) => ( + + ))} - ) - })} - + ))} + + ) : null}
    sortTable(headerId) : null} - > - }> - - - {tableItem.tip && } -
    - - } - > - - - - {contentItemValue} + {cellData.value} } className="best-iteration" @@ -130,12 +113,12 @@ const DetailsResults = ({ allowSortBy, defaultSortBy, defaultDirection, excludeS ) } else { return ( - + } + template={} > - {roundFloats(contentItemValue, 4)} + {roundFloats(cellData.value, 4)}
    - } - > - {key} - - - } - > - {String(resultValue ?? '')} - -
    + } + > + {cellData.value} + +
    diff --git a/src/components/DetailsResults/detailsResults.scss b/src/components/DetailsResults/detailsResults.scss index 84a49c498..1166cc8dc 100644 --- a/src/components/DetailsResults/detailsResults.scss +++ b/src/components/DetailsResults/detailsResults.scss @@ -4,31 +4,11 @@ .table__item-results { display: flex; - flex: 1; position: relative; - min-width: 100%; .table { - .table { - &-header { - &-item { - background-color: $white; - } - } - - .table-cell-wide { - flex: 0 0 250px; - width: 250px; - } - - .table-cell-full { - flex: 1 0 auto; - width: auto; - } - - i { - margin: 0 5px; - } + i { + margin: 0 5px; } } } diff --git a/src/components/Files/FilesView.js b/src/components/Files/FilesView.js index b73b94320..5ef66df8e 100644 --- a/src/components/Files/FilesView.js +++ b/src/components/Files/FilesView.js @@ -145,7 +145,7 @@ const FilesView = React.forwardRef( )} {artifactsStore?.preview?.isPreview && ( - + )} ) diff --git a/src/components/Jobs/Jobs.js b/src/components/Jobs/Jobs.js index 2bec3467c..b7abc35a2 100755 --- a/src/components/Jobs/Jobs.js +++ b/src/components/Jobs/Jobs.js @@ -168,7 +168,7 @@ const Jobs = ({ fetchJobFunction }) => { /> )} {artifactsStore?.preview?.isPreview && ( - + )} ) diff --git a/src/components/ModelsPage/ModelsPage.js b/src/components/ModelsPage/ModelsPage.js index 10cd5258f..3f168b0f0 100644 --- a/src/components/ModelsPage/ModelsPage.js +++ b/src/components/ModelsPage/ModelsPage.js @@ -51,7 +51,7 @@ const ModelsPage = () => { )} {artifactsStore?.preview?.isPreview && ( - + )} ) diff --git a/src/components/Table/table.scss b/src/components/Table/table.scss index 02d2f135d..f13e89061 100644 --- a/src/components/Table/table.scss +++ b/src/components/Table/table.scss @@ -67,6 +67,26 @@ flex: 5; } + &-6 { + flex: 6; + } + + &-7 { + flex: 7; + } + + &-8 { + flex: 8; + } + + &-9 { + flex: 9; + } + + &-10 { + flex: 10; + } + &-extra-small { flex: 1; max-width: 85px; @@ -147,20 +167,18 @@ } .table { - &-header { - &-item { - &.buttonPopout, - &.buttonDownload { - @include tableColumnFlex(0.5, 80px); - } + &-header-item { + &.buttonPopout, + &.buttonDownload { + @include tableColumnFlex(0.5, 80px); + } - &.align-right { - justify-content: flex-end; - } + &.align-right { + justify-content: flex-end; + } - .tip-container { - margin-left: 2px; - } + .tip-container { + margin-left: 2px; } } @@ -170,6 +188,7 @@ &__cell { &_hidden { visibility: hidden; + .chip { visibility: hidden; } diff --git a/src/elements/PreviewModal/PreviewModal.js b/src/elements/PreviewModal/PreviewModal.js index 4ba351b19..fe19c1f7a 100644 --- a/src/elements/PreviewModal/PreviewModal.js +++ b/src/elements/PreviewModal/PreviewModal.js @@ -36,7 +36,7 @@ import { closeArtifactsPreview } from '../../reducers/artifactsReducer' import './previewModal.scss' -const PreviewModal = ({ item }) => { +const PreviewModal = ({ artifact }) => { const [preview, setPreview] = useState([]) const [extraData, setExtraData] = useState([]) const [noData, setNoData] = useState(false) @@ -45,21 +45,21 @@ const PreviewModal = ({ item }) => { useEffect(() => { if (preview.length === 0) { - getArtifactPreview(params.projectName, item, noData, setNoData, setPreview) + getArtifactPreview(params.projectName, artifact, noData, setNoData, setPreview) } - }, [item, noData, params.projectName, preview.length]) + }, [artifact, noData, params.projectName, preview.length]) useEffect(() => { - if (item.extra_data && extraData.length === 0) { + if (artifact.extra_data && extraData.length === 0) { fetchArtifactPreviewFromExtraData( params.projectName, - item, + artifact, noData, setNoData, previewContent => setExtraData(state => [...state, previewContent]) ) } - }, [params.projectName, item, extraData.length, noData]) + }, [params.projectName, artifact, extraData.length, noData]) useEffect(() => { return () => { @@ -79,27 +79,33 @@ const PreviewModal = ({ item }) => {
    - }> - {item.db_key || item.key} + }> + {artifact.db_key || artifact.key}
    - }> - {item.target_path} + }> + {artifact.target_path}
    - {item.size && ( + {(artifact.ui.size || artifact.size) && (
    size: - {typeof item.size === 'string' ? item.size : prettyBytes(item.size)} + {artifact.ui.size + ? artifact.ui.size + : typeof artifact.size === 'string' + ? artifact.size + : prettyBytes(artifact.size)}
    )} -
    {formatDatetime(item.updated || item.date, 'N/A')}
    +
    + {formatDatetime(artifact.updated || artifact.ui.date, 'N/A')} +
    @@ -108,7 +114,9 @@ const PreviewModal = ({ item }) => { extraData={extraData} noData={noData} preview={preview} - showExtraDataLoader={item.extra_data && extraData.length !== item.extra_data.length} + showExtraDataLoader={ + artifact.extra_data && extraData.length !== artifact.extra_data.length + } />
    @@ -118,7 +126,7 @@ const PreviewModal = ({ item }) => { } PreviewModal.propTypes = { - item: PropTypes.shape({}).isRequired + artifact: PropTypes.shape({}).isRequired } export default PreviewModal diff --git a/src/hooks/useSortTable.hook.js b/src/hooks/useSortTable.hook.js index 170c449b1..3ebdfae68 100644 --- a/src/hooks/useSortTable.hook.js +++ b/src/hooks/useSortTable.hook.js @@ -45,12 +45,13 @@ export const useSortTable = ({ headers, content, sortConfig = {} }) => { const getValueByType = useCallback( columnIndex => rowData => { + const rowDataContent = rowData.content ? rowData.content : rowData + if ( - rowData.content && - rowData.content[columnIndex] instanceof Object && - Object.keys(rowData.content[columnIndex]).length + rowDataContent[columnIndex] instanceof Object && + Object.keys(rowDataContent[columnIndex]).length ) { - let valueToTest = rowData.content[columnIndex].value + let valueToTest = rowDataContent[columnIndex].value if (!isEmpty(valueToTest)) { if (valueToTest instanceof Array && valueToTest.length > 0) { diff --git a/src/layout/Content/Content.js b/src/layout/Content/Content.js index c24d46394..c0f1b8a70 100644 --- a/src/layout/Content/Content.js +++ b/src/layout/Content/Content.js @@ -183,7 +183,7 @@ const Content = ({ )}
    {artifactsStore?.preview?.isPreview && ( - + )} ) diff --git a/src/scss/main.scss b/src/scss/main.scss index adaec1cc5..a66e77608 100644 --- a/src/scss/main.scss +++ b/src/scss/main.scss @@ -214,6 +214,7 @@ main { position: relative; text-align: left; width: 100%; + border-spacing: 0; .table { &-row { @@ -233,6 +234,7 @@ main { display: inline-flex; flex: 1; align-items: center; + overflow: hidden; @include tableDet(8px, 5px, 8px, 10px); @@ -275,14 +277,15 @@ main { &__container_extended { &:before { background: linear-gradient( - 90deg, - rgba(255, 255, 255, 0) 0%, - rgba(245, 247, 255, 1) 100% + 90deg, + rgba(255, 255, 255, 0) 0%, + rgba(245, 247, 255, 1) 100% ); } } } } + &:hover { .table-cell-icon { .actions-menu { @@ -294,9 +297,9 @@ main { &:before { background: linear-gradient( - 90deg, - rgba(255, 255, 255, 0) 0%, - rgba(245, 247, 255, 1) 100% + 90deg, + rgba(255, 255, 255, 0) 0%, + rgba(245, 247, 255, 1) 100% ); } } diff --git a/src/utils/getArtifactPreview.js b/src/utils/getArtifactPreview.js index 6c5526d70..7f3d3e70f 100644 --- a/src/utils/getArtifactPreview.js +++ b/src/utils/getArtifactPreview.js @@ -64,7 +64,7 @@ export const fetchArtifactPreviewFromExtraData = ( fetchArtifactPreview( projectName, previewItem.path, - previewItem.path.startsWith('/User') && (artifact.user || artifact.producer.owner), + previewItem.path.startsWith('/User') && (artifact.ui.user || artifact.producer.owner), previewItem.path.replace(/.*\./g, ''), artifact.db_key, cancelToken diff --git a/src/utils/resultsTable.js b/src/utils/resultsTable.js index 54a27b88c..d0642ca2a 100644 --- a/src/utils/resultsTable.js +++ b/src/utils/resultsTable.js @@ -17,29 +17,45 @@ illegal under applicable law, and the grant of the foregoing license under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ -import { capitalize } from 'lodash' - -export const resultsTableHeaders = array => { - if (!array.iterationStats || !array.iterationStats.length) return [] - - const [headers] = array.iterationStats.slice(0, 1).map(item => { - return [item[1], item[0]].concat(item.slice(2)).map(header => { - const clearHeaderPrefix = String(header).replace(/^.+\./, '').toLocaleLowerCase() - - return { - headerId: clearHeaderPrefix, - headerLabel: capitalize(clearHeaderPrefix) - } +import { capitalize, isObjectLike } from 'lodash' + +export const generateResultsContent = job => { + let content = [] + + if (job.iterationStats && job.iterationStats.length !== 0) { + const headers = job.iterationStats[0] + content = job.iterationStats.slice(1).map(iterationStat => { + const generatedStatData = iterationStat.map((statValue, idx) => { + const clearHeaderPrefix = String(headers[idx]).replace(/^.+\./, '').toLocaleLowerCase() + return { + headerId: headers[idx], + headerLabel: capitalize(clearHeaderPrefix), + value: statValue ?? '' + } + }) + + return [generatedStatData[1], generatedStatData[0]].concat(generatedStatData.slice(2)) }) - }) - - return headers -} - -export const resultsTableContent = array => { - if (!array.iterationStats || !array.iterationStats.length) return [] + } else if (job.iterations?.length === 0 && Object.keys(job.results ?? {}).length !== 0) { + content = Object.keys(job.results).map(resultName => { + const resultValue = isObjectLike(job.results[resultName]) + ? JSON.stringify(job.results[resultName]) + : job.results[resultName] + + return [ + { + headerId: 'name', + headerLabel: 'Name', + value: resultName + }, + { + headerId: 'value', + headerLabel: 'Value', + value: String(resultValue ?? '') + } + ] + }) + } - return array.iterationStats.slice(1).map(contentItem => { - return [contentItem[1], contentItem[0]].concat(contentItem.slice(2)).map(item => item ?? '') - }) + return content } From 5940d5d1bb021993a6830580ae51919233e98f7f Mon Sep 17 00:00:00 2001 From: mariana-furyk <58301139+mariana-furyk@users.noreply.github.com> Date: Thu, 9 Nov 2023 10:04:36 +0200 Subject: [PATCH 06/10] Impl [Models] Metrics new look and feel in the table (#2062) --- src/common/Download/DownloadItem.js | 2 +- src/common/Download/downloadContainer.scss | 2 +- .../ArtifactsPreview/ArtifactsPreviewView.js | 2 +- src/components/Datasets/Datasets.js | 14 +- src/components/Datasets/DatasetsView.js | 8 +- .../DetailsArtifacts/DetailsArtifacts.js | 7 +- src/components/DetailsInputs/DetailsInputs.js | 4 +- .../DetailsResults/DetailsResults.js | 4 +- src/components/Files/Files.js | 14 +- src/components/Files/FilesView.js | 8 +- src/components/ModelsPage/Models/Models.js | 16 +- .../ModelsPage/Models/ModelsView.js | 4 +- src/components/ModelsPage/modelsPage.scss | 10 + src/components/Table/TableHead.js | 25 +- src/components/Table/TableView.js | 17 +- src/components/Table/table.scss | 24 +- .../JobsFunctionsTableRow.js | 2 +- .../ArtifactsTableRow/ArtifactsTableRow.js | 12 +- .../FeatureStoreTableRow.js | 18 +- .../FunctionsTableRow/FunctionsTableRow.js | 18 +- src/elements/JobsTableRow/JobsTableRow.js | 2 +- src/elements/TableCell/TableCell.js | 32 +- src/elements/TableLinkCell/TableLinkCell.js | 7 +- .../TableProducerCell/TableProducerCell.js | 38 ++- src/elements/TableTypeCell/TableTypeCell.js | 4 +- src/hooks/groupContent.hook.js | 14 +- src/scss/main.scss | 173 ++++++----- src/utils/createArtifactsContent.js | 278 +++++++++--------- src/utils/createConsumerGroupContent.js | 8 +- src/utils/createConsumerGroupsContent.js | 6 +- src/utils/createFeatureStoreContent.js | 54 ++-- src/utils/createFunctionsContent.js | 26 +- src/utils/createJobsContent.js | 58 ++-- 33 files changed, 528 insertions(+), 383 deletions(-) diff --git a/src/common/Download/DownloadItem.js b/src/common/Download/DownloadItem.js index c474e30c5..7afeb530a 100644 --- a/src/common/Download/DownloadItem.js +++ b/src/common/Download/DownloadItem.js @@ -99,7 +99,7 @@ const DownloadItem = ({ downloadItem }) => { timeoutRef.current = setTimeout(() => { dispatch(removeDownloadItem(downloadItem.id)) - }, 1000) + }, 5000) }) } }, [ diff --git a/src/common/Download/downloadContainer.scss b/src/common/Download/downloadContainer.scss index 88d9d55e6..5b64eddaa 100644 --- a/src/common/Download/downloadContainer.scss +++ b/src/common/Download/downloadContainer.scss @@ -6,7 +6,7 @@ right: 24px; bottom: -115px; opacity: 0; - z-index: 1000; + z-index: 9; width: 220px; min-height: 90px; max-height: 200px; diff --git a/src/components/ArtifactsPreview/ArtifactsPreviewView.js b/src/components/ArtifactsPreview/ArtifactsPreviewView.js index 430a19142..78e550e6a 100644 --- a/src/components/ArtifactsPreview/ArtifactsPreviewView.js +++ b/src/components/ArtifactsPreview/ArtifactsPreviewView.js @@ -67,7 +67,7 @@ const ArtifactsPreviewView = ({ className, preview, setShowErrorBody, showErrorB {preview.data.headers.map((header, index) => { return ( - + }> {header} diff --git a/src/components/Datasets/Datasets.js b/src/components/Datasets/Datasets.js index 08c02dff3..014a2f7da 100644 --- a/src/components/Datasets/Datasets.js +++ b/src/components/Datasets/Datasets.js @@ -65,6 +65,7 @@ import { getViewMode } from '../../utils/helper' import { copyToClipboard } from '../../utils/copyToClipboard' import { generateUri } from '../../utils/resources' import { setDownloadItem, setShowDownloadsList } from '../../reducers/downloadReducer' +import { useSortTable } from '../../hooks/useSortTable.hook' import { ReactComponent as TagIcon } from 'igz-controls/images/tag-icon.svg' import { ReactComponent as YamlIcon } from 'igz-controls/images/yaml.svg' @@ -291,6 +292,15 @@ const Datasets = () => { ) }, [datasets, filtersStore.groupBy, frontendSpec, latestItems, params.projectName]) + const tableHeaders = useMemo(() => tableContent[0]?.content ?? [], [tableContent]) + + const { sortTable, selectedColumnName, getSortingIcon, sortedTableContent, sortedTableHeaders } = + useSortTable({ + headers: tableHeaders, + content: tableContent, + sortConfig: { excludeSortBy: 'labels', defaultSortBy: 'updated', defaultDirection: 'desc' } + }) + useEffect(() => { if (params.name && params.tag && pageData.details.menu.length > 0) { isDetailsTabExists(params.tab, pageData.details.menu, navigate, location) @@ -369,7 +379,9 @@ const Datasets = () => { setDatasets={setDatasets} setSelectedDataset={setSelectedDataset} setSelectedRowData={setSelectedRowData} - tableContent={tableContent} + sortProps={{ sortTable, selectedColumnName, getSortingIcon }} + tableContent={sortedTableContent} + tableHeaders={sortedTableHeaders} toggleConvertedYaml={toggleConvertedYaml} viewMode={viewMode} urlTagOption={urlTagOption} diff --git a/src/components/Datasets/DatasetsView.js b/src/components/Datasets/DatasetsView.js index b399b5d76..253a6af37 100644 --- a/src/components/Datasets/DatasetsView.js +++ b/src/components/Datasets/DatasetsView.js @@ -36,6 +36,7 @@ import { registerDatasetTitle, filters } from './datasets.util' import { removeDataSet } from '../../reducers/artifactsReducer' import { ACTIONS_MENU } from '../../types' import { SECONDARY_BUTTON } from 'igz-controls/constants' +import { SORT_PROPS } from 'igz-controls/types' const DatasetsView = React.forwardRef( ( @@ -57,7 +58,9 @@ const DatasetsView = React.forwardRef( setDatasets, setSelectedDataset, setSelectedRowData, + sortProps, tableContent, + tableHeaders, toggleConvertedYaml, viewMode, urlTagOption @@ -115,7 +118,8 @@ const DatasetsView = React.forwardRef( pageData={pageData} retryRequest={handleRefresh} selectedItem={selectedDataset} - tableHeaders={tableContent[0]?.content ?? []} + sortProps={sortProps} + tableHeaders={tableHeaders ?? []} > {tableContent.map((tableItem, index) => (
    -
    +
    {artifactsTabContent[0].map(({ headerId, headerLabel, className }) => ( -
    +
    {headerLabel}
    ))} diff --git a/src/components/DetailsInputs/DetailsInputs.js b/src/components/DetailsInputs/DetailsInputs.js index 894ece693..818d756e0 100644 --- a/src/components/DetailsInputs/DetailsInputs.js +++ b/src/components/DetailsInputs/DetailsInputs.js @@ -128,9 +128,9 @@ const DetailsInputs = ({ inputs }) => {
    -
    +
    {inputsTabContent[0].map(({ headerId, headerLabel, className }) => ( -
    +
    {headerLabel}
    ))} diff --git a/src/components/DetailsResults/DetailsResults.js b/src/components/DetailsResults/DetailsResults.js index 9244c9b38..bc2f4d0de 100644 --- a/src/components/DetailsResults/DetailsResults.js +++ b/src/components/DetailsResults/DetailsResults.js @@ -48,7 +48,7 @@ const DetailsResults = ({ allowSortBy, defaultSortBy, defaultDirection, excludeS const getHeaderCellClasses = (headerId, isSortable) => classNames( - 'table-header-item', + 'table-header__cell', isSortable && 'sortable-header-cell', isSortable && selectedColumnName === headerId && 'sortable-header-cell_active' ) @@ -56,7 +56,7 @@ const DetailsResults = ({ allowSortBy, defaultSortBy, defaultDirection, excludeS const getHeaderTemplate = () => { return ( - + {sortedTableHeaders.map(({ headerLabel, headerId, isSortable, ...tableItem }) => { return ( { : files.map(contentItem => createFilesRowData(contentItem, params.projectName, frontendSpec)) }, [files, filtersStore.groupBy, frontendSpec, latestItems, params.projectName]) + const tableHeaders = useMemo(() => tableContent[0]?.content ?? [], [tableContent]) + + const { sortTable, selectedColumnName, getSortingIcon, sortedTableContent, sortedTableHeaders } = + useSortTable({ + headers: tableHeaders, + content: tableContent, + sortConfig: { excludeSortBy: 'labels', defaultSortBy: 'updated', defaultDirection: 'desc' } + }) + const applyDetailsChanges = useCallback( changes => { return handleApplyDetailsChanges( @@ -357,7 +367,9 @@ const Files = () => { setFiles={setFiles} setSelectedFile={setSelectedFile} setSelectedRowData={setSelectedRowData} - tableContent={tableContent} + sortProps={{ sortTable, selectedColumnName, getSortingIcon }} + tableContent={sortedTableContent} + tableHeaders={sortedTableHeaders} toggleConvertedYaml={toggleConvertedYaml} viewMode={viewMode} urlTagOption={urlTagOption} diff --git a/src/components/Files/FilesView.js b/src/components/Files/FilesView.js index 5ef66df8e..d91415336 100644 --- a/src/components/Files/FilesView.js +++ b/src/components/Files/FilesView.js @@ -36,6 +36,7 @@ import { registerArtifactTitle, filters } from './files.util' import { removeFile } from '../../reducers/artifactsReducer' import { ACTIONS_MENU } from '../../types' import { SECONDARY_BUTTON } from 'igz-controls/constants' +import { SORT_PROPS } from 'igz-controls/types' const FilesView = React.forwardRef( ( @@ -57,7 +58,9 @@ const FilesView = React.forwardRef( setFiles, setSelectedFile, setSelectedRowData, + sortProps, tableContent, + tableHeaders, toggleConvertedYaml, viewMode, urlTagOption @@ -109,7 +112,8 @@ const FilesView = React.forwardRef( pageData={pageData} retryRequest={handleRefresh} selectedItem={selectedFile} - tableHeaders={tableContent[0]?.content ?? []} + sortProps={sortProps} + tableHeaders={tableHeaders ?? []} > {tableContent.map((tableItem, index) => ( { const [models, setModels] = useState([]) @@ -275,11 +275,14 @@ const Models = ({ fetchModelFeatureVector }) => { ) }, [filtersStore.groupBy, frontendSpec, latestItems, models, params.projectName]) - const { sortTable, selectedColumnName, getSortingIcon, sortedTableContent } = useSortTable({ - headers: tableContent[0]?.content, - content: tableContent, - sortConfig: { defaultSortBy: 'updated', defaultDirection: 'desc' } - }) + const tableHeaders = useMemo(() => tableContent[0]?.content ?? [], [tableContent]) + + const { sortTable, selectedColumnName, getSortingIcon, sortedTableContent, sortedTableHeaders } = + useSortTable({ + headers: tableHeaders, + content: tableContent, + sortConfig: { excludeSortBy: 'labels', defaultSortBy: 'updated', defaultDirection: 'desc' } + }) const applyDetailsChanges = useCallback( changes => { @@ -412,6 +415,7 @@ const Models = ({ fetchModelFeatureVector }) => { setSelectedRowData={setSelectedRowData} sortProps={{ sortTable, selectedColumnName, getSortingIcon }} tableContent={sortedTableContent} + tableHeaders={sortedTableHeaders} viewMode={viewMode} urlTagOption={urlTagOption} /> diff --git a/src/components/ModelsPage/Models/ModelsView.js b/src/components/ModelsPage/Models/ModelsView.js index 90314557a..4b082cc04 100644 --- a/src/components/ModelsPage/Models/ModelsView.js +++ b/src/components/ModelsPage/Models/ModelsView.js @@ -59,6 +59,7 @@ const ModelsView = React.forwardRef( setSelectedRowData, sortProps, tableContent, + tableHeaders, viewMode, urlTagOption }, @@ -122,7 +123,7 @@ const ModelsView = React.forwardRef( selectedItem={selectedModel} sortProps={sortProps} tab={MODELS_TAB} - tableHeaders={tableContent[0]?.content ?? []} + tableHeaders={tableHeaders ?? []} > {tableContent.map((tableItem, index) => { return ( @@ -188,6 +189,7 @@ ModelsView.propTypes = { setSelectedRowData: PropTypes.func.isRequired, sortProps: SORT_PROPS, tableContent: PropTypes.arrayOf(PropTypes.object).isRequired, + tableHeaders: PropTypes.arrayOf(PropTypes.object).isRequired, viewMode: PropTypes.string, urlTagOption: PropTypes.string } diff --git a/src/components/ModelsPage/modelsPage.scss b/src/components/ModelsPage/modelsPage.scss index d3abc66d0..564338b71 100644 --- a/src/components/ModelsPage/modelsPage.scss +++ b/src/components/ModelsPage/modelsPage.scss @@ -1,3 +1,5 @@ +@import '~igz-controls/scss/colors'; + .models { display: flex; flex-direction: column; @@ -6,4 +8,12 @@ .filters { justify-content: flex-end; } + + .metrics-cell { + font-weight: bold; + + &_with-border { + border-left: 1px solid $titanWhite; + } + } } diff --git a/src/components/Table/TableHead.js b/src/components/Table/TableHead.js index 5c6923795..139029182 100644 --- a/src/components/Table/TableHead.js +++ b/src/components/Table/TableHead.js @@ -28,10 +28,17 @@ import { SORT_PROPS } from 'igz-controls/types' const TableHead = React.forwardRef( ({ content, hideActionsMenu, mainRowItemsCount, selectedItem, sortProps }, ref) => { - const getHeaderCellClasses = (headerId, isSortable, tableItemClass, index) => + const getHeaderCellClasses = ( + headerId, + isSortable, + tableItemClassName, + headerCellClassName, + index + ) => classNames( - 'table-header-item', - tableItemClass, + 'table-header__cell', + tableItemClassName, + headerCellClassName, isSortable && 'sortable-header-cell', isSortable && sortProps?.selectedColumnName === headerId && 'sortable-header-cell_active', !isEmpty(selectedItem) && index >= mainRowItemsCount && 'table-body__cell_hidden' @@ -39,11 +46,17 @@ const TableHead = React.forwardRef( return ( - + {content.map(({ headerLabel, headerId, isSortable, ...tableItem }, index) => { return tableItem.type !== 'hidden' && !tableItem.hidden ? ( sortProps.sortTable(headerId) : null} > @@ -58,7 +71,7 @@ const TableHead = React.forwardRef( ) : null })} - {!hideActionsMenu && } + {!hideActionsMenu && } ) diff --git a/src/components/Table/TableView.js b/src/components/Table/TableView.js index 09e26785d..1bf175719 100644 --- a/src/components/Table/TableView.js +++ b/src/components/Table/TableView.js @@ -20,7 +20,7 @@ such restriction. import React from 'react' import PropTypes from 'prop-types' import { isEmpty } from 'lodash' -import classNames from 'classnames' +import classnames from 'classnames' import ArtifactsTableRow from '../../elements/ArtifactsTableRow/ArtifactsTableRow' import ConsumerGroupShardLagTableRow from '../../elements/ConsumerGroupShardLagTableRow/ConsumerGroupShardLagTableRow' @@ -77,7 +77,7 @@ const TableView = ({ tableHeadRef, tablePanelRef }) => { - const tableClass = classNames('table', !isEmpty(selectedItem) && 'table-with-details') + const tableClass = classnames('table', !isEmpty(selectedItem) && 'table-with-details') return (
    @@ -88,11 +88,15 @@ const TableView = ({ <> - {pageData.tableHeaders?.map( - (item, index) => + {pageData.tableHeaders?.map((item, index) => { + const headerClassNames = classnames( + `table-header__cell ${item.className} ${item.headerCellClassName}` + ) + + return ( !item.hidden && ( @@ -101,7 +105,8 @@ const TableView = ({ ) - )} + ) + })} diff --git a/src/components/Table/table.scss b/src/components/Table/table.scss index f13e89061..a501f5d76 100644 --- a/src/components/Table/table.scss +++ b/src/components/Table/table.scss @@ -119,7 +119,7 @@ } } - .parent-row-expanded { + .parent-row_expanded { .row_grouped-by { .table-cell-name { border-right: none; @@ -167,18 +167,20 @@ } .table { - &-header-item { - &.buttonPopout, - &.buttonDownload { - @include tableColumnFlex(0.5, 80px); - } + &-header { + &__cell { + &.buttonPopout, + &.buttonDownload { + @include tableColumnFlex(0.5, 80px); + } - &.align-right { - justify-content: flex-end; - } + &.align-right { + justify-content: flex-end; + } - .tip-container { - margin-left: 2px; + .tip-container { + margin-left: 2px; + } } } diff --git a/src/components/Workflow/JobsFunctionsTableRow/JobsFunctionsTableRow.js b/src/components/Workflow/JobsFunctionsTableRow/JobsFunctionsTableRow.js index c8cfacf0f..64b9c65e0 100644 --- a/src/components/Workflow/JobsFunctionsTableRow/JobsFunctionsTableRow.js +++ b/src/components/Workflow/JobsFunctionsTableRow/JobsFunctionsTableRow.js @@ -32,7 +32,7 @@ const JobsFunctionsTableRow = ({ handleSelectItem, rowItem, selectedItem }) => { const rowClassNames = classnames( 'table-row', 'parent-row', - isWorkflowJobSelected(rowItem.data, selectedItem) && 'row_active' + isWorkflowJobSelected(rowItem.data, selectedItem) && 'table-row_active' ) return ( diff --git a/src/elements/ArtifactsTableRow/ArtifactsTableRow.js b/src/elements/ArtifactsTableRow/ArtifactsTableRow.js index 0ae3f69b7..996c007db 100644 --- a/src/elements/ArtifactsTableRow/ArtifactsTableRow.js +++ b/src/elements/ArtifactsTableRow/ArtifactsTableRow.js @@ -51,19 +51,19 @@ const ArtifactsTableRow = ({ 'parent-row', (selectedItem.db_key || selectedItem?.spec?.model) && getArtifactIdentifier(selectedItem, true) === rowItem.data.ui.identifierUnique && - !parent.current?.classList.value.includes('parent-row-expanded') && - 'row_active', - parent.current?.classList.value.includes('parent-row-expanded') && 'parent-row-expanded' + !parent.current?.classList.value.includes('parent-row_expanded') && + 'table-row_active', + parent.current?.classList.value.includes('parent-row_expanded') && 'parent-row_expanded' ) return ( - {parent.current?.classList.contains('parent-row-expanded') ? ( + {parent.current?.classList.contains('parent-row_expanded') ? ( <> @@ -102,7 +102,7 @@ const ArtifactsTableRow = ({ selectedItem.key && tableContentItem.data.ui.identifierUnique === getArtifactIdentifier(selectedItem, true) && - 'row_active' + 'table-row_active' ) return ( diff --git a/src/elements/FeatureStoreTableRow/FeatureStoreTableRow.js b/src/elements/FeatureStoreTableRow/FeatureStoreTableRow.js index 5e68331ab..4d709cc9f 100644 --- a/src/elements/FeatureStoreTableRow/FeatureStoreTableRow.js +++ b/src/elements/FeatureStoreTableRow/FeatureStoreTableRow.js @@ -54,19 +54,19 @@ const FeatureStoreTableRow = ({ 'parent-row', selectedItem?.name && getIdentifier(selectedItem, true) === rowItem.data.ui.identifierUnique && - !parent.current?.classList.value.includes('parent-row-expanded') && - 'row_active', - parent.current?.classList.value.includes('parent-row-expanded') && 'parent-row-expanded' + !parent.current?.classList.value.includes('parent-row_expanded') && + 'table-row_active', + parent.current?.classList.value.includes('parent-row_expanded') && 'parent-row_expanded' ) return ( - {parent.current?.classList.contains('parent-row-expanded') ? ( + {parent.current?.classList.contains('parent-row_expanded') ? ( <>
    @@ -116,11 +116,15 @@ const FeatureStoreTableRow = ({ 'table-row', selectedItem.name && getIdentifier(selectedItem, true) === tableContentItem.data.ui.identifierUnique && - 'row_active' + 'table-row_active' ) return ( -
    + diff --git a/src/elements/FunctionsTableRow/FunctionsTableRow.js b/src/elements/FunctionsTableRow/FunctionsTableRow.js index b7efc96f7..27d3e6f2b 100644 --- a/src/elements/FunctionsTableRow/FunctionsTableRow.js +++ b/src/elements/FunctionsTableRow/FunctionsTableRow.js @@ -46,19 +46,19 @@ const FunctionsTableRow = ({ 'table-row', 'parent-row', getFunctionIdentifier(selectedItem, true) === rowItem.data?.ui?.identifierUnique && - !parent.current?.classList.value.includes('parent-row-expanded') && - 'row_active', - parent.current?.classList.value.includes('parent-row-expanded') && 'parent-row-expanded' + !parent.current?.classList.value.includes('parent-row_expanded') && + 'table-row_active', + parent.current?.classList.value.includes('parent-row_expanded') && 'parent-row_expanded' ) return ( - {parent.current?.classList.contains('parent-row-expanded') ? ( + {parent.current?.classList.contains('parent-row_expanded') ? ( <>
    @@ -95,11 +95,15 @@ const FunctionsTableRow = ({ 'table-row', selectedItem.name && getFunctionIdentifier(selectedItem, true) === func.data.ui.identifierUnique && - 'row_active' + 'table-row_active' ) return ( -
    + diff --git a/src/elements/JobsTableRow/JobsTableRow.js b/src/elements/JobsTableRow/JobsTableRow.js index e126588e5..02d748a35 100644 --- a/src/elements/JobsTableRow/JobsTableRow.js +++ b/src/elements/JobsTableRow/JobsTableRow.js @@ -34,7 +34,7 @@ const JobsTableRow = ({ actionsMenu, handleSelectJob, rowItem, selectedJob }) => const rowClassNames = classnames( 'table-row', 'parent-row', - getJobIdentifier(selectedJob, true) === rowItem.data.ui.identifierUnique && 'row_active' + getJobIdentifier(selectedJob, true) === rowItem.data.ui.identifierUnique && 'table-row_active' ) return ( diff --git a/src/elements/TableCell/TableCell.js b/src/elements/TableCell/TableCell.js index 60c4b5c1a..d7d344f32 100644 --- a/src/elements/TableCell/TableCell.js +++ b/src/elements/TableCell/TableCell.js @@ -20,12 +20,12 @@ such restriction. import React from 'react' import PropTypes from 'prop-types' import { useDispatch } from 'react-redux' +import classnames from 'classnames' import ChipCell from '../../common/ChipCell/ChipCell' import CopyToClipboard from '../../common/CopyToClipboard/CopyToClipboard' import Download from '../../common/Download/Download' import TableLinkCell from '../TableLinkCell/TableLinkCell' -import TableProducerCell from '../TableProducerCell/TableProducerCell' import TableTypeCell from '../TableTypeCell/TableTypeCell' import { Tooltip, TextTooltipTemplate, RoundedIcon } from 'igz-controls/components' @@ -50,8 +50,16 @@ const TableCell = ({ }) => { const dispatch = useDispatch() const { value: stateValue, label: stateLabel, className: stateClassName } = item.state ?? {} + const cellClassNames = classnames( + 'table-body__cell', + data.className, + className, + data.bodyCellClassName + ) - if (link && data.type !== 'hidden') { + if (data.template) { + return data.template + } else if (link && data.type !== 'hidden') { return ( + ) - } else if (data.type === 'producer') { - return } else if (data.type === 'buttonPopout') { return ( - + return } else { return ( - ) } +TableProducerCell.defaultProps = { + bodyCellClassName: '', + className: '' +} + TableProducerCell.propTypes = { - data: PropTypes.shape({}).isRequired + bodyCellClassName: PropTypes.string, + className: PropTypes.string, + producer: PropTypes.shape({}).isRequired } export default TableProducerCell diff --git a/src/elements/TableTypeCell/TableTypeCell.js b/src/elements/TableTypeCell/TableTypeCell.js index 08aad3312..93d6abb5f 100644 --- a/src/elements/TableTypeCell/TableTypeCell.js +++ b/src/elements/TableTypeCell/TableTypeCell.js @@ -20,6 +20,7 @@ such restriction. import React from 'react' import PropTypes from 'prop-types' import { capitalize } from 'lodash' +import classnames from 'classnames' import { Tooltip, TextTooltipTemplate } from 'igz-controls/components' @@ -59,9 +60,10 @@ const TableTypeCell = ({ className, data }) => { [JOB_KIND_SPARK]: { label: 'Spark', icon: }, [JOB_KIND_WORKFLOW]: { label: 'Workflow', icon: } } + const cellClassNames = classnames('table-body__cell', data.className, data.bodyCellClassName) return ( - + producer && ( + + ) ) } diff --git a/src/utils/createArtifactsContent.js b/src/utils/createArtifactsContent.js index d8851169d..64d0b2246 100644 --- a/src/utils/createArtifactsContent.js +++ b/src/utils/createArtifactsContent.js @@ -159,7 +159,7 @@ export const createModelsRowData = (artifact, project, frontendSpec, showExpandB id: `producer.${artifact.ui.identifierUnique}`, headerId: 'producer', headerLabel: 'Producer', - value: artifact.producer.name || '', + value: artifact.producer?.name || '', template: , className: 'table-cell-1', type: 'producer' @@ -302,7 +302,7 @@ export const createFilesRowData = (artifact, project, frontendSpec, showExpandBu id: `producer.${artifact.ui.identifierUnique}`, headerId: 'producer', headerLabel: 'Producer', - value: artifact.producer.name || '', + value: artifact.producer?.name || '', template: , className: 'table-cell-1', type: 'producer' @@ -512,7 +512,7 @@ export const createDatasetsRowData = (artifact, project, frontendSpec, showExpan id: `producer.${artifact.ui.identifierUnique}`, headerId: 'producer', headerLabel: 'Producer', - value: artifact.producer.name || '', + value: artifact.producer?.name || '', template: , className: 'table-cell-1', type: 'producer' From ebea492fdf60af508cc2b18f131590dfc406fcdf Mon Sep 17 00:00:00 2001 From: Ilank <63646693+ilan7empest@users.noreply.github.com> Date: Sun, 12 Nov 2023 21:56:39 +0800 Subject: [PATCH 08/10] Bump DRC verion 1.7.1 (#2071) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f9b18868d..01bdcb080 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "final-form-arrays": "^3.0.2", "fs-extra": "^10.0.0", "identity-obj-proxy": "^3.0.0", - "iguazio.dashboard-react-controls": "1.7.0", + "iguazio.dashboard-react-controls": "1.7.1", "is-wsl": "^1.1.0", "js-base64": "^2.5.2", "js-yaml": "^3.13.1", From 1439190e6ebe62ac7e49c4eca5e6533c497163e6 Mon Sep 17 00:00:00 2001 From: Andrew Mavdryk Date: Sun, 12 Nov 2023 15:59:46 +0200 Subject: [PATCH 09/10] Impl [Projects] Provide validation rules on labels (#2069) --- src/actions/projects.js | 14 -- src/components/Details/details.util.js | 2 +- .../CreateProjectDialog.js | 162 +++++++++--------- .../createProjectDialog.scss | 26 +-- src/components/ProjectsPage/Projects.js | 37 ++-- src/components/ProjectsPage/ProjectsView.js | 14 -- src/constants.js | 4 - .../FormDataInputsRow/FormDataInputsRow.js | 2 +- .../FormEnvironmentVariablesRow.js | 2 +- .../FormParametersRow/FormParametersRow.js | 6 +- .../FormVolumesTable/formVolumesTable.util.js | 2 +- .../PanelResourcesUnits.js | 2 +- .../RegisterArtifactModalForm.js | 2 +- .../RegisterModelModal/RegisterModelModal.js | 2 +- src/reducers/projectReducer.js | 45 ----- src/scss/main.scss | 1 - 16 files changed, 107 insertions(+), 216 deletions(-) diff --git a/src/actions/projects.js b/src/actions/projects.js index 19f13c893..b8d5e1bb8 100644 --- a/src/actions/projects.js +++ b/src/actions/projects.js @@ -72,14 +72,10 @@ import { FETCH_PROJECTS_BEGIN, FETCH_PROJECTS_FAILURE, FETCH_PROJECTS_SUCCESS, - REMOVE_NEW_PROJECT, REMOVE_NEW_PROJECT_ERROR, REMOVE_PROJECT_SUMMARY, REMOVE_PROJECT_DATA, REMOVE_PROJECTS, - SET_NEW_PROJECT_DESCRIPTION, - SET_NEW_PROJECT_LABELS, - SET_NEW_PROJECT_NAME, SET_PROJECT_DATA, SET_PROJECT_LABELS, FETCH_PROJECTS_NAMES_BEGIN, @@ -587,20 +583,10 @@ const projectsAction = { type: FETCH_PROJECT_WORKFLOWS_SUCCESS, payload: workflows }), - removeNewProject: () => ({ type: REMOVE_NEW_PROJECT }), removeNewProjectError: () => ({ type: REMOVE_NEW_PROJECT_ERROR }), removeProjectData: () => ({ type: REMOVE_PROJECT_DATA }), removeProjectSummary: () => ({ type: REMOVE_PROJECT_SUMMARY }), removeProjects: () => ({ type: REMOVE_PROJECTS }), - setNewProjectDescription: description => ({ - type: SET_NEW_PROJECT_DESCRIPTION, - payload: description - }), - setNewProjectLabels: (label, labels) => ({ - type: SET_NEW_PROJECT_LABELS, - payload: { ...labels, ...label } - }), - setNewProjectName: name => ({ type: SET_NEW_PROJECT_NAME, payload: name }), setProjectData: data => ({ type: SET_PROJECT_DATA, payload: data diff --git a/src/components/Details/details.util.js b/src/components/Details/details.util.js index 0bda8d3b2..4eef8fbfb 100644 --- a/src/components/Details/details.util.js +++ b/src/components/Details/details.util.js @@ -97,7 +97,7 @@ export const generateArtifactsContent = (detailsType, selectedItem, projectName) name: 'common.tag', additionalRules: { name: 'tagUniqueness', - label: 'Artifact tag should be unique', + label: 'Artifact tag must be unique', pattern: isArtifactTagUnique(projectName, detailsType, selectedItem), async: true } diff --git a/src/components/ProjectsPage/CreateProjectDialog/CreateProjectDialog.js b/src/components/ProjectsPage/CreateProjectDialog/CreateProjectDialog.js index e6db5397e..1d3123741 100644 --- a/src/components/ProjectsPage/CreateProjectDialog/CreateProjectDialog.js +++ b/src/components/ProjectsPage/CreateProjectDialog/CreateProjectDialog.js @@ -18,31 +18,41 @@ under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ import React from 'react' -import { useSelector } from 'react-redux' import PropTypes from 'prop-types' +import arrayMutators from 'final-form-arrays' +import { Form } from 'react-final-form' +import { useSelector } from 'react-redux' import ErrorMessage from '../../../common/ErrorMessage/ErrorMessage' -import Input from '../../../common/Input/Input' import Loader from '../../../common/Loader/Loader' -import ProjectLabels from '../../Project/ProjectLabels/ProjectLabels' -import { Button, PopUpDialog } from 'igz-controls/components' +import { Button, FormChipCell, FormInput, FormTextarea, PopUpDialog } from 'igz-controls/components' -import { getValidationRules } from 'igz-controls/utils/validation.util' import { SECONDARY_BUTTON, TERTIARY_BUTTON } from 'igz-controls/constants' +import { createForm } from 'final-form' +import { getChipOptions } from '../../../utils/getChipOptions' +import { getValidationRules } from 'igz-controls/utils/validation.util' +import { setFieldState } from 'igz-controls/utils/form.util' import './createProjectDialog.scss' const CreateProjectDialog = ({ closeNewProjectPopUp, handleCreateProject, - isNameValid, - removeNewProjectError, - setNameValid, - setNewProjectDescription, - setNewProjectLabels, - setNewProjectName + removeNewProjectError }) => { const projectStore = useSelector(store => store.projectStore) + const initialValues = { + name: '', + description: '', + labels: [] + } + const formRef = React.useRef( + createForm({ + initialValues, + mutators: { ...arrayMutators, setFieldState }, + onSubmit: () => {} + }) + ) return ( {projectStore.loading && } -
    -
    - setNameValid(value)} - type="text" - value={projectStore.newProject.name} - validationRules={getValidationRules('project.name')} - /> - -
    - Labels: - -
    -
    - {projectStore.newProject.error && ( - { - if (projectStore.newProject.error) { - removeNewProjectError() - } - }} - message={projectStore.newProject.error} - /> - )} -
    -
    - +
    {}}> + {formState => { + return ( + <> +
    + +
    +
    + +
    +
    + +
    + {projectStore.newProject.error && ( +
    + { + if (projectStore.newProject.error) { + removeNewProjectError() + } + }} + message={projectStore.newProject.error} + /> +
    + )} +
    +
    + + ) + }} +
    ) } @@ -119,11 +131,7 @@ const CreateProjectDialog = ({ CreateProjectDialog.propTypes = { closeNewProjectPopUp: PropTypes.func.isRequired, handleCreateProject: PropTypes.func.isRequired, - isNameValid: PropTypes.bool.isRequired, - removeNewProjectError: PropTypes.func.isRequired, - setNameValid: PropTypes.func.isRequired, - setNewProjectDescription: PropTypes.func.isRequired, - setNewProjectName: PropTypes.func.isRequired + removeNewProjectError: PropTypes.func.isRequired } export default CreateProjectDialog diff --git a/src/components/ProjectsPage/CreateProjectDialog/createProjectDialog.scss b/src/components/ProjectsPage/CreateProjectDialog/createProjectDialog.scss index 34d959dff..c4830f62f 100644 --- a/src/components/ProjectsPage/CreateProjectDialog/createProjectDialog.scss +++ b/src/components/ProjectsPage/CreateProjectDialog/createProjectDialog.scss @@ -1,27 +1,5 @@ .create-project-dialog { - .pop-up-dialog { - &__footer-container { - align-items: center; - } - - &__form { - margin-bottom: 20px; - } - - .input-wrapper { - margin-bottom: 10px; - - &:last-child { - margin-bottom: 0; - } - } - - .chips-wrapper { - flex-wrap: wrap; - } - } - - .error-message { - max-width: 235px; + .error { + width: 100%; } } diff --git a/src/components/ProjectsPage/Projects.js b/src/components/ProjectsPage/Projects.js index 3e359aa7d..7fad69fdc 100644 --- a/src/components/ProjectsPage/Projects.js +++ b/src/components/ProjectsPage/Projects.js @@ -51,12 +51,8 @@ const Projects = ({ fetchProjectsNames, fetchProjectsSummary, projectStore, - removeNewProject, removeNewProjectError, - removeProjects, - setNewProjectDescription, - setNewProjectLabels, - setNewProjectName + removeProjects }) => { const [actionsMenu, setActionsMenu] = useState({}) const [confirmData, setConfirmData] = useState(null) @@ -66,7 +62,6 @@ const Projects = ({ const [filterByName, setFilterByName] = useState('') const [filterMatches, setFilterMatches] = useState([]) const [isDescendingOrder, setIsDescendingOrder] = useState(false) - const [isNameValid, setNameValid] = useState(true) const [selectedProjectsState, setSelectedProjectsState] = useState('active') const [sortProjectId, setSortProjectId] = useState('byName') const [source] = useState(axios.CancelToken.source()) @@ -356,34 +351,27 @@ const Projects = ({ removeNewProjectError() } - removeNewProject() - setNameValid(true) setCreateProject(false) - }, [projectStore.newProject.error, removeNewProject, removeNewProjectError]) + }, [projectStore.newProject.error, removeNewProjectError]) - const handleCreateProject = e => { + const handleCreateProject = (e, formState) => { e.preventDefault() - if (e.currentTarget.checkValidity()) { - if (projectStore.newProject.name.length === 0) { - setNameValid(false) - return false - } else if (isNameValid) { - setNameValid(true) - } - + if (e.currentTarget.checkValidity() && formState.valid) { createNewProject({ metadata: { - name: projectStore.newProject.name, - labels: projectStore.newProject.labels + name: formState.values.name, + labels: formState.values.labels?.reduce((acc, labelData) => { + acc[labelData.key] = labelData.value + return acc + }, {}) ?? {} }, spec: { - description: projectStore.newProject.description + description: formState.values.description } }).then(result => { if (result) { setCreateProject(false) - removeNewProject() refreshProjects() fetchProjectsNames() } @@ -406,7 +394,6 @@ const Projects = ({ handleSelectSortOption={handleSelectSortOption} handleSearchOnFocus={handleSearchOnFocus} isDescendingOrder={isDescendingOrder} - isNameValid={isNameValid} projectStore={projectStore} refreshProjects={refreshProjects} removeNewProjectError={removeNewProjectError} @@ -415,10 +402,6 @@ const Projects = ({ setFilterByName={setFilterByName} setFilterMatches={setFilterMatches} setIsDescendingOrder={setIsDescendingOrder} - setNameValid={setNameValid} - setNewProjectDescription={setNewProjectDescription} - setNewProjectName={setNewProjectName} - setNewProjectLabels={setNewProjectLabels} setSelectedProjectsState={setSelectedProjectsState} sortProjectId={sortProjectId} urlParams={urlParams} diff --git a/src/components/ProjectsPage/ProjectsView.js b/src/components/ProjectsPage/ProjectsView.js index 7ed66cdf9..9732e20ca 100644 --- a/src/components/ProjectsPage/ProjectsView.js +++ b/src/components/ProjectsPage/ProjectsView.js @@ -55,7 +55,6 @@ const ProjectsView = ({ handleSelectSortOption, handleSearchOnFocus, isDescendingOrder, - isNameValid, projectStore, refreshProjects, removeNewProjectError, @@ -64,10 +63,6 @@ const ProjectsView = ({ setFilterByName, setFilterMatches, setIsDescendingOrder, - setNameValid, - setNewProjectDescription, - setNewProjectLabels, - setNewProjectName, setSelectedProjectsState, sortProjectId }) => { @@ -83,12 +78,7 @@ const ProjectsView = ({ )} {confirmData && ( @@ -211,7 +201,6 @@ ProjectsView.propTypes = { handleCreateProject: PropTypes.func.isRequired, handleSearchOnFocus: PropTypes.func.isRequired, handleSelectSortOption: PropTypes.func.isRequired, - isNameValid: PropTypes.bool.isRequired, refreshProjects: PropTypes.func.isRequired, removeNewProjectError: PropTypes.func.isRequired, selectedProjectsState: PropTypes.string.isRequired, @@ -219,9 +208,6 @@ ProjectsView.propTypes = { setFilterByName: PropTypes.func.isRequired, setFilterMatches: PropTypes.func.isRequired, setIsDescendingOrder: PropTypes.func.isRequired, - setNewProjectDescription: PropTypes.func.isRequired, - setNameValid: PropTypes.func.isRequired, - setNewProjectName: PropTypes.func.isRequired, setSelectedProjectsState: PropTypes.func.isRequired, sortProjectId: PropTypes.string.isRequired } diff --git a/src/constants.js b/src/constants.js index deef4a8f7..387f949c4 100644 --- a/src/constants.js +++ b/src/constants.js @@ -423,14 +423,10 @@ export const FETCH_PROJECTS_SUMMARY_SUCCESS = 'FETCH_PROJECTS_SUMMARY_SUCCESS' export const FETCH_PROJECT_WORKFLOWS_BEGIN = 'FETCH_PROJECT_WORKFLOWS_BEGIN' export const FETCH_PROJECT_WORKFLOWS_FAILURE = 'FETCH_PROJECT_WORKFLOWS_FAILURE' export const FETCH_PROJECT_WORKFLOWS_SUCCESS = 'FETCH_PROJECT_WORKFLOWS_SUCCESS' -export const REMOVE_NEW_PROJECT = 'REMOVE_NEW_PROJECT' export const REMOVE_NEW_PROJECT_ERROR = 'REMOVE_NEW_PROJECT_ERROR' export const REMOVE_PROJECTS = 'REMOVE_PROJECTS' export const REMOVE_PROJECT_SUMMARY = 'REMOVE_PROJECT_SUMMARY' export const REMOVE_PROJECT_DATA = 'REMOVE_PROJECT_DATA' -export const SET_NEW_PROJECT_DESCRIPTION = 'SET_NEW_PROJECT_DESCRIPTION' -export const SET_NEW_PROJECT_LABELS = 'SET_NEW_PROJECT_LABELS' -export const SET_NEW_PROJECT_NAME = 'SET_NEW_PROJECT_NAME' /*=========== DETAILS =============*/ diff --git a/src/elements/FormDataInputsTable/FormDataInputsRow/FormDataInputsRow.js b/src/elements/FormDataInputsTable/FormDataInputsRow/FormDataInputsRow.js index b554bb929..2ae108c0d 100644 --- a/src/elements/FormDataInputsTable/FormDataInputsRow/FormDataInputsRow.js +++ b/src/elements/FormDataInputsTable/FormDataInputsRow/FormDataInputsRow.js @@ -93,7 +93,7 @@ const FormDataInputsRow = ({ validationRules={[ { name: 'uniqueness', - label: 'Name should be unique', + label: 'Name must be unique', pattern: newValue => uniquenessValidator(fields, newValue) } ]} diff --git a/src/elements/FormEnvironmentVariablesTable/FormEnvironmentVariablesRow/FormEnvironmentVariablesRow.js b/src/elements/FormEnvironmentVariablesTable/FormEnvironmentVariablesRow/FormEnvironmentVariablesRow.js index 8829aab88..22910a43f 100644 --- a/src/elements/FormEnvironmentVariablesTable/FormEnvironmentVariablesRow/FormEnvironmentVariablesRow.js +++ b/src/elements/FormEnvironmentVariablesTable/FormEnvironmentVariablesRow/FormEnvironmentVariablesRow.js @@ -81,7 +81,7 @@ const FormEnvironmentVariablesRow = ({ validationRules={[ { name: 'uniqueness', - label: 'Name should be unique', + label: 'Name must be unique', pattern: newValue => uniquenessValidator(fields, newValue) } ]} diff --git a/src/elements/FormParametersTable/FormParametersRow/FormParametersRow.js b/src/elements/FormParametersTable/FormParametersRow/FormParametersRow.js index e815a5a62..f35f394d5 100644 --- a/src/elements/FormParametersTable/FormParametersRow/FormParametersRow.js +++ b/src/elements/FormParametersTable/FormParametersRow/FormParametersRow.js @@ -182,9 +182,9 @@ const FormParametersRow = ({ const getValueTip = parameterType => { switch (parameterType) { case parameterTypeMap: - return 'The valid `map` type should be in the JSON format\n e.g. {"hello": "world"}' + return 'The valid `map` type must be in the JSON format\n e.g. {"hello": "world"}' case parameterTypeList: - return 'The valid `list` type should be in the JSON format\n e.g. ["hello", "world"]' + return 'The valid `list` type must be in the JSON format\n e.g. ["hello", "world"]' default: return '' } @@ -281,7 +281,7 @@ const FormParametersRow = ({ validationRules={[ { name: 'uniqueness', - label: 'Name should be unique', + label: 'Name must be unique', pattern: newValue => uniquenessValidator(fields, fieldsPath, newValue) } ]} diff --git a/src/elements/FormVolumesTable/formVolumesTable.util.js b/src/elements/FormVolumesTable/formVolumesTable.util.js index 2a8066bda..5c997ac9e 100644 --- a/src/elements/FormVolumesTable/formVolumesTable.util.js +++ b/src/elements/FormVolumesTable/formVolumesTable.util.js @@ -88,7 +88,7 @@ export const generateVolumeInputsData = (selectedItem, fields, editingItem) => { validationRules: [ { name: 'uniqueness', - label: 'Name should be unique', + label: 'Name must be unique', pattern: newValue => { return !fields.value.some(({ data: { name } }, index) => { return newValue.trim() === name && index !== editingItemIndex diff --git a/src/elements/PanelResourcesUnits/PanelResourcesUnits.js b/src/elements/PanelResourcesUnits/PanelResourcesUnits.js index be55bbe1f..62f5d21b9 100644 --- a/src/elements/PanelResourcesUnits/PanelResourcesUnits.js +++ b/src/elements/PanelResourcesUnits/PanelResourcesUnits.js @@ -160,7 +160,7 @@ const PanelResourcesUnits = ({ density="dense" disabled={isPanelEditMode} invalid={!validation.isGpuLimitValid} - invalidText="The minimum value should be 1" + invalidText="The minimum value must be 1" label="Limit" labelType="labelAtTop" min={1} diff --git a/src/elements/RegisterArtifactModalForm/RegisterArtifactModalForm.js b/src/elements/RegisterArtifactModalForm/RegisterArtifactModalForm.js index 81b82fd4d..26eeb1179 100644 --- a/src/elements/RegisterArtifactModalForm/RegisterArtifactModalForm.js +++ b/src/elements/RegisterArtifactModalForm/RegisterArtifactModalForm.js @@ -88,7 +88,7 @@ const RegisterArtifactModalForm = ({ tip={messagesByKind?.nameTip} validationRules={getValidationRules('artifact.name', { name: 'ArtifactExists', - label: 'Artifact name should be unique', + label: 'Artifact name must be unique', pattern: isArtifactNameUnique(projectName), async: true })} diff --git a/src/elements/RegisterModelModal/RegisterModelModal.js b/src/elements/RegisterModelModal/RegisterModelModal.js index 0f592e529..6ed08738d 100644 --- a/src/elements/RegisterModelModal/RegisterModelModal.js +++ b/src/elements/RegisterModelModal/RegisterModelModal.js @@ -168,7 +168,7 @@ function RegisterModelModal({ actions, isOpen, onResolve, projectName, refresh } tip="Artifacts names in the same project must be unique." validationRules={getValidationRules('artifact.name', { name: 'ArtifactExists', - label: 'Artifact name should be unique', + label: 'Artifact name must be unique', pattern: isArtifactNameUnique(projectName), async: true })} diff --git a/src/reducers/projectReducer.js b/src/reducers/projectReducer.js index 62e73a127..da3a75baa 100644 --- a/src/reducers/projectReducer.js +++ b/src/reducers/projectReducer.js @@ -64,14 +64,10 @@ import { FETCH_PROJECTS_BEGIN, FETCH_PROJECTS_FAILURE, FETCH_PROJECTS_SUCCESS, - REMOVE_NEW_PROJECT, REMOVE_NEW_PROJECT_ERROR, REMOVE_PROJECT_SUMMARY, REMOVE_PROJECT_DATA, REMOVE_PROJECTS, - SET_NEW_PROJECT_DESCRIPTION, - SET_NEW_PROJECT_LABELS, - SET_NEW_PROJECT_NAME, SET_PROJECT_LABELS, FETCH_PROJECT_FEATURE_SETS_BEGIN, FETCH_PROJECT_FEATURE_SETS_SUCCESS, @@ -95,9 +91,6 @@ const initialState = { error: null, loading: false, newProject: { - name: '', - description: '', - labels: null, error: null }, project: { @@ -221,7 +214,6 @@ const projectReducer = (state = initialState, { type, payload }) => { ...state, loading: false, newProject: { - ...state.newProject, error: payload } } @@ -230,7 +222,6 @@ const projectReducer = (state = initialState, { type, payload }) => { ...state, loading: false, newProject: { - ...state.newProject, error: null } } @@ -771,15 +762,6 @@ const projectReducer = (state = initialState, { type, payload }) => { } } } - case REMOVE_NEW_PROJECT: - return { - ...state, - newProject: { - name: '', - description: '', - labels: null - } - } case REMOVE_PROJECT_SUMMARY: return { ...state, @@ -805,36 +787,9 @@ const projectReducer = (state = initialState, { type, payload }) => { return { ...state, newProject: { - ...state.newProject, error: null } } - case SET_NEW_PROJECT_DESCRIPTION: - return { - ...state, - newProject: { - ...state.newProject, - description: payload - } - } - case SET_NEW_PROJECT_LABELS: - return { - ...state, - newProject: { - ...state.newProject, - labels: { - ...payload - } - } - } - case SET_NEW_PROJECT_NAME: - return { - ...state, - newProject: { - ...state.newProject, - name: payload - } - } case SET_PROJECT_DATA: { return { ...state, diff --git a/src/scss/main.scss b/src/scss/main.scss index f7924ff46..cc138cc51 100644 --- a/src/scss/main.scss +++ b/src/scss/main.scss @@ -208,7 +208,6 @@ main { display: inline-flex; flex: 1; align-items: center; - overflow: hidden; @include tableDet(8px, 5px, 8px, 10px); From faf8118d72bb4100ee93f8c0bc14032068396144 Mon Sep 17 00:00:00 2001 From: Andrew Mavdryk Date: Mon, 13 Nov 2023 11:38:55 +0200 Subject: [PATCH 10/10] Fix [API] browser caching (#2070) --- src/httpClient.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/httpClient.js b/src/httpClient.js index c6ad8906a..20fe8ff22 100755 --- a/src/httpClient.js +++ b/src/httpClient.js @@ -20,10 +20,14 @@ such restriction. import axios from 'axios' import qs from 'qs' +const headers = { + 'Cache-Control': 'no-cache' +} export const mainBaseUrl = `${process.env.PUBLIC_URL}/api/v1` export const mainHttpClient = axios.create({ baseURL: mainBaseUrl, + headers, // serialize a param with an array value as a repeated param, for example: // { label: ['host', 'owner=admin'] } => 'label=host&label=owner%3Dadmin' @@ -31,13 +35,16 @@ export const mainHttpClient = axios.create({ }) export const functionTemplatesHttpClient = axios.create({ - baseURL: `${process.env.PUBLIC_URL}/function-catalog` + baseURL: `${process.env.PUBLIC_URL}/function-catalog`, + headers }) export const nuclioHttpClient = axios.create({ - baseURL: `${process.env.PUBLIC_URL}/nuclio/api` + baseURL: `${process.env.PUBLIC_URL}/nuclio/api`, + headers }) export const iguazioHttpClient = axios.create({ - baseURL: process.env.NODE_ENV === 'production' ? '/api' : '/iguazio/api' + baseURL: process.env.NODE_ENV === 'production' ? '/api' : '/iguazio/api', + headers })
    {data && ( }> @@ -93,7 +101,7 @@ const TableCell = ({ return } else if (data.type === 'icons') { return ( -
    + {data.value.map((valueItem, index) => ( + + + + + }> {truncateUid(data.value)} @@ -171,10 +177,10 @@ const TableCell = ({ } else if (data.type === 'hidden') { return null } else if (data.type === 'component') { - return {data.value}{data.value} + } diff --git a/src/elements/TableLinkCell/TableLinkCell.js b/src/elements/TableLinkCell/TableLinkCell.js index 91def5cf8..942f32b79 100644 --- a/src/elements/TableLinkCell/TableLinkCell.js +++ b/src/elements/TableLinkCell/TableLinkCell.js @@ -40,7 +40,12 @@ const TableLinkCell = ({ selectedItem, showExpandButton }) => { - const tableCellClassNames = classnames('table-body__cell', data.class, className) + const tableCellClassNames = classnames( + 'table-body__cell', + data.className, + className, + data.bodyCellClassName + ) const itemNameClassNames = classnames('item-name') const { value: stateValue, label: stateLabel, className: stateClassName } = item.state ?? {} diff --git a/src/elements/TableProducerCell/TableProducerCell.js b/src/elements/TableProducerCell/TableProducerCell.js index 07d011f42..2c8a767c4 100644 --- a/src/elements/TableProducerCell/TableProducerCell.js +++ b/src/elements/TableProducerCell/TableProducerCell.js @@ -20,6 +20,7 @@ such restriction. import React from 'react' import { Link } from 'react-router-dom' import PropTypes from 'prop-types' +import classnames from 'classnames' import ProducerTooltipTemplate from '../TooltipTemplate/ProducerTooltipTemplate' import { Tooltip, TextTooltipTemplate } from 'igz-controls/components' @@ -28,45 +29,50 @@ import { getJobsDetailsMenu } from '../../components/Jobs/jobs.util' import { DETAILS_OVERVIEW_TAB, MONITOR_JOBS_TAB } from '../../constants' -const TableProducerCell = ({ data }) => { - const [project, uid] = data.value.uri?.split('/') || [] - const { name } = data.value +const TableProducerCell = ({ bodyCellClassName, className, producer }) => { + const [project, uid] = producer.uri?.split('/') || [] const overviewTab = getJobsDetailsMenu().find(tab => tab.id === DETAILS_OVERVIEW_TAB) || {} + const cellClassNames = classnames('table-body__cell', className, bodyCellClassName) return ( - - {data.value.name && uid && ( + + {producer.name && uid && (
    } > - {data.value.name} + {producer.name}
    )} - {data.value.name && !uid && ( - }> - {data.value.name} - + {producer.name && !uid && ( + }>{producer.name} )}
    + { const rows = [...document.getElementsByClassName('parent-row')] - rows.forEach(row => row.classList.remove('parent-row-expanded')) + rows.forEach(row => row.classList.remove('parent-row_expanded')) setExpand(false) setGroupedContent({}) @@ -66,14 +66,14 @@ export const useGroupContent = ( const handleExpandRow = (e, item) => { const parentRow = e.target.closest('.parent-row') - if (parentRow.classList.contains('parent-row-expanded')) { - parentRow.classList.remove('parent-row-expanded') + if (parentRow.classList.contains('parent-row_expanded')) { + parentRow.classList.remove('parent-row_expanded') handleRemoveRequestData && handleRemoveRequestData(item) setExpandedItems(prev => --prev) } else { - parentRow.classList.remove('row_active') - parentRow.classList.add('parent-row-expanded') + parentRow.classList.remove('table-row_active') + parentRow.classList.add('parent-row_expanded') handleRequestOnExpand && handleRequestOnExpand(item, groupedContent) setExpandedItems(prev => ++prev) @@ -86,12 +86,12 @@ export const useGroupContent = ( const rows = [...document.getElementsByClassName('parent-row')] if (collapseRows || expand) { - rows.forEach(row => row.classList.remove('parent-row-expanded')) + rows.forEach(row => row.classList.remove('parent-row_expanded')) setExpandedItems(0) handleExpandAllCallback && handleExpandAllCallback(true) } else { - rows.forEach(row => row.classList.add('parent-row-expanded')) + rows.forEach(row => row.classList.add('parent-row_expanded')) setExpandedItems(Object.keys(groupedContent).length) handleExpandAllCallback && handleExpandAllCallback(false, groupedContent) diff --git a/src/scss/main.scss b/src/scss/main.scss index a66e77608..f7924ff46 100644 --- a/src/scss/main.scss +++ b/src/scss/main.scss @@ -5,36 +5,6 @@ @import '~igz-controls/scss/borders'; @import '~igz-controls/scss/fonts'; -*, -::after, -::before { - box-sizing: border-box; -} - -* { - scrollbar-color: rgba($black, 0.25) rgba($black, 0.1); - scrollbar-width: thin; -} - -::-webkit-scrollbar { - width: 8px; - height: 8px; -} - -::-webkit-scrollbar-track { - background-color: rgba($black, 0.1); - border-radius: 10px; -} - -::-webkit-scrollbar-thumb { - background-color: rgba($black, 0.25); - border-radius: 10px; - - &:hover { - background-color: rgba($black, 0.4); - } -} - body { display: flex; flex: 1; @@ -69,6 +39,8 @@ body { } } +/*=========== CONTENT =============*/ + .content { position: relative; flex-direction: column; @@ -137,6 +109,8 @@ body { } } +/*=========== MAIN =============*/ + main { position: relative; z-index: 1; @@ -166,6 +140,8 @@ main { } } +/*=========== PAGE =============*/ + .page { flex: 1 1; min-width: 100%; @@ -193,7 +169,7 @@ main { .table { border-top: none; - .table-header-item, + .table-header__cell, .table-body__cell { color: $primaryTextColor; } @@ -207,6 +183,8 @@ main { } } +/*=========== TABLE =============*/ + .table { display: flex; flex-flow: column nowrap; @@ -225,11 +203,7 @@ main { background-color: inherit; border-bottom: $tableRowBorder; - &_header { - min-height: 40px; - } - - &:not(.parent-row-expanded) > * { + &:not(.parent-row_expanded) > * { position: relative; display: inline-flex; flex: 1; @@ -270,7 +244,7 @@ main { } &.parent-row { - &:not(.parent-row-expanded) { + &:not(.parent-row_expanded) { &:not(.parent-row_without-actions) { .table-cell-icon { .actions-menu { @@ -334,7 +308,7 @@ main { margin-top: 3px; } - &-expanded { + &_expanded { flex-direction: column; border: 0; @@ -352,7 +326,7 @@ main { .row_grouped-by { position: sticky; - top: 50px; + top: 40px; background-color: $white; padding: 0; z-index: 3; @@ -377,7 +351,11 @@ main { min-width: 100%; z-index: 3; - &-item { + &-row { + min-height: 40px; + } + + &__cell { min-width: 75px; @include tableHeader; @@ -415,7 +393,7 @@ main { background-color: $white; &:hover, - &.row_active { + &_active { background-color: $ghostWhite; } @@ -434,6 +412,8 @@ main { } } +/*=========== SORT =============*/ + .sortable-header { &-cell { position: relative; @@ -476,10 +456,7 @@ main { } } -a { - color: inherit; - text-decoration: none; -} +/*=========== STATE =============*/ [class^='state-active'], [class^='state-completed'], @@ -534,10 +511,7 @@ a { @include statusState($topaz); } -iframe { - width: 100%; - height: 100%; -} +/*=========== ERROR =============*/ .error-container { margin: auto; @@ -554,23 +528,7 @@ iframe { } } -.data-ellipsis { - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} - -.cursor-pointer { - cursor: pointer; -} - -.capitalize { - text-transform: capitalize; -} - -.wrap { - white-space: pre-wrap; -} +/*=========== ACTIONS =============*/ .actions { display: flex; @@ -596,6 +554,8 @@ iframe { } } +/*=========== INPUT =============*/ + .auto-resizable-input { position: relative; display: inline-grid; @@ -626,6 +586,9 @@ iframe { } } + +/*=========== COMBOBOX =============*/ + .combobox { .path-type { &-store { @@ -650,13 +613,7 @@ iframe { } } -.visibility-hidden { - visibility: hidden; -} - -.pointer { - cursor: pointer; -} +/*=========== GRAPH =============*/ .graph-container { display: flex; @@ -694,6 +651,8 @@ iframe { } } +/*=========== FORM =============*/ + .form { color: $primary; @@ -785,6 +744,9 @@ iframe { } } + +/*=========== SORT ICON =============*/ + .sort-icon { width: 20px; transition: transform 0.3s ease-in-out; @@ -823,6 +785,71 @@ iframe { } } + +/*=========== GENERAL =============*/ + +.visibility-hidden { + visibility: hidden; +} + +.data-ellipsis { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.cursor-pointer { + cursor: pointer; +} + +.capitalize { + text-transform: capitalize; +} + +.wrap { + white-space: pre-wrap; +} + +iframe { + width: 100%; + height: 100%; +} + +a { + color: inherit; + text-decoration: none; +} + +*, +::after, +::before { + box-sizing: border-box; +} + +* { + scrollbar-color: rgba($black, 0.25) rgba($black, 0.1); + scrollbar-width: thin; +} + +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background-color: rgba($black, 0.1); + border-radius: 10px; +} + +::-webkit-scrollbar-thumb { + background-color: rgba($black, 0.25); + border-radius: 10px; + + &:hover { + background-color: rgba($black, 0.4); + } +} + //TODO: Delete after removing Textarea and Input and using DRC components @mixin fieldWrapperOld { position: relative; diff --git a/src/utils/createArtifactsContent.js b/src/utils/createArtifactsContent.js index 32e440cad..d8851169d 100644 --- a/src/utils/createArtifactsContent.js +++ b/src/utils/createArtifactsContent.js @@ -18,7 +18,8 @@ under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ import React from 'react' -import { isNumber } from 'lodash' +import { isEmpty, isNil, isNumber } from 'lodash' +import classnames from 'classnames' import { ARTIFACTS_PAGE, @@ -35,6 +36,7 @@ import { parseUri } from './parseUri' import { generateFunctionDetailsLink } from './generateFunctionDetailsLink' import { generateLinkToDetailsPanel } from './generateLinkToDetailsPanel' import { validateArguments } from './validateArguments' +import TableProducerCell from '../elements/TableProducerCell/TableProducerCell' import { ReactComponent as SeverityOk } from 'igz-controls/images/severity-ok.svg' import { ReactComponent as SeverityWarning } from 'igz-controls/images/severity-warning.svg' @@ -112,113 +114,125 @@ export const getIsTargetPathValid = (artifact, frontendSpec) => export const createModelsRowData = (artifact, project, frontendSpec, showExpandButton) => { const iter = getIter(artifact) - - return { - data: { - ...artifact - }, - content: [ - { - id: `key.${artifact.ui.identifierUnique}`, - headerId: 'name', - headerLabel: 'Name', - value: artifact.db_key, - class: 'table-cell-name', - getLink: tab => - validateArguments(artifact.db_key, tab, artifact.tree) - ? generateLinkToDetailsPanel( - project, - MODELS_TAB, - MODELS_TAB, - artifact.db_key, - artifact.tag, - tab, - artifact.tree, - artifact.iter - ) - : '', - expandedCellContent: { - class: 'table-cell-name', - showTag: true, - tooltip: artifact.tag ? `${artifact.tag}${iter}` : `${artifact.tree}${iter}`, - type: 'date', - value: formatDatetime(artifact.updated, 'N/A') - }, - rowExpanded: { - getLink: false - }, + const content = [ + { + id: `key.${artifact.ui.identifierUnique}`, + headerId: 'name', + headerLabel: 'Name', + value: artifact.db_key, + className: 'table-cell-name', + getLink: tab => + validateArguments(artifact.db_key, tab, artifact.tree) + ? generateLinkToDetailsPanel( + project, + MODELS_TAB, + MODELS_TAB, + artifact.db_key, + artifact.tag, + tab, + artifact.tree, + artifact.iter + ) + : '', + expandedCellContent: { + className: 'table-cell-name', showTag: true, - showExpandButton + tooltip: artifact.tag ? `${artifact.tag}${iter}` : `${artifact.tree}${iter}`, + type: 'date', + value: formatDatetime(artifact.updated, 'N/A') }, - { - id: `labels.${artifact.ui.identifierUnique}`, - headerId: 'labels', - headerLabel: 'Labels', - value: parseKeyValues(artifact.labels), - class: 'table-cell-1', - type: 'labels' - }, - { - id: `producer.${artifact.ui.identifierUnique}`, - headerId: 'producer', - headerLabel: 'Producer', - value: artifact.producer, - class: 'table-cell-1', - type: 'producer' + rowExpanded: { + getLink: false }, - { - id: `owner.${artifact.ui.identifierUnique}`, - headerId: 'owner', - headerLabel: 'Owner', - value: artifact.producer?.owner, - class: 'table-cell-1', - type: 'owner' - }, - { - id: `updated.${artifact.ui.identifierUnique}`, - headerId: 'updated', - headerLabel: 'Updated', - value: formatDatetime(artifact.updated, 'N/A'), - class: 'table-cell-1' - }, - { - id: `metrics.${artifact.ui.identifierUnique}`, - headerId: 'metrics', - headerLabel: 'Metrics', - value: parseKeyValues(artifact.metrics), - class: 'table-cell-1', - type: 'metrics' - }, - { - id: `frameWorkAndAlgorithm.${artifact.ui.identifierUnique}`, - headerId: 'frameWorkAndAlgorithm', - headerLabel: ( + showTag: true, + showExpandButton + }, + { + id: `labels.${artifact.ui.identifierUnique}`, + headerId: 'labels', + headerLabel: 'Labels', + value: parseKeyValues(artifact.labels), + className: 'table-cell-1', + type: 'labels' + }, + { + id: `producer.${artifact.ui.identifierUnique}`, + headerId: 'producer', + headerLabel: 'Producer', + value: artifact.producer.name || '', + template: , + className: 'table-cell-1', + type: 'producer' + }, + { + id: `owner.${artifact.ui.identifierUnique}`, + headerId: 'owner', + headerLabel: 'Owner', + value: artifact.producer?.owner, + className: 'table-cell-1', + type: 'owner' + }, + { + id: `updated.${artifact.ui.identifierUnique}`, + headerId: 'updated', + headerLabel: 'Updated', + value: formatDatetime(artifact.updated, 'N/A'), + className: 'table-cell-1' + }, + { + id: `frameWorkAndAlgorithm.${artifact.ui.identifierUnique}`, + headerId: 'frameWorkAndAlgorithm', + headerLabel: ( + + Framework & +
    + Algorithm +
    + ), + value: + artifact.framework || artifact.algorithm ? ( - Framework & + {artifact.framework}
    - Algorithm + {artifact.algorithm}
    + ) : ( + '' ), - value: - artifact.framework || artifact.algorithm ? ( - - {artifact.framework} -
    - {artifact.algorithm} -
    - ) : ( - '' - ), - class: 'table-cell-1' - }, - { - id: `version.${artifact.ui.identifierUnique}`, - headerId: 'tag', - value: artifact.tag, - class: 'table-cell-1', - type: 'hidden' - } - ] + className: 'table-cell-1' + }, + { + id: `version.${artifact.ui.identifierUnique}`, + headerId: 'tag', + value: artifact.tag, + className: 'table-cell-1', + type: 'hidden' + } + ] + + if (!isNil(artifact.metrics) && !isEmpty(artifact.metrics)) { + Object.entries(artifact.metrics).forEach(([key, value], index) => { + const bodyCellClassName = classnames( + 'metrics-cell', + index === 0 && 'metrics-cell_with-border' + ) + + content.push({ + id: `${key}.${artifact.ui.identifierUnique}`, + headerId: key, + headerLabel: key, + value: parseFloat(value), + className: 'table-cell-1', + bodyCellClassName + }) + }) + } + + return { + data: { + ...artifact + }, + content } } @@ -235,7 +249,7 @@ export const createFilesRowData = (artifact, project, frontendSpec, showExpandBu headerId: 'name', headerLabel: 'Name', value: artifact.db_key, - class: 'table-cell-name', + className: 'table-cell-name', getLink: tab => validateArguments(artifact.db_key, tab, artifact.tree) ? generateLinkToDetailsPanel( @@ -250,7 +264,7 @@ export const createFilesRowData = (artifact, project, frontendSpec, showExpandBu ) : '', expandedCellContent: { - class: 'table-cell-name', + className: 'table-cell-name', showTag: true, tooltip: artifact.tag ? `${artifact.tag}${iter}` : `${artifact.tree}${iter}`, type: 'date', @@ -266,7 +280,7 @@ export const createFilesRowData = (artifact, project, frontendSpec, showExpandBu id: `version.${artifact.ui.identifierUnique}`, headerId: 'tag', value: artifact.tag, - class: 'table-cell-1', + className: 'table-cell-1', type: 'hidden' }, { @@ -274,22 +288,23 @@ export const createFilesRowData = (artifact, project, frontendSpec, showExpandBu headerId: 'type', headerLabel: 'Type', value: artifact.kind, - class: 'table-cell-small' + className: 'table-cell-small' }, { id: `labels.${artifact.ui.identifierUnique}`, headerId: 'labels', headerLabel: 'Labels', value: parseKeyValues(artifact.labels), - class: 'table-cell-1', + className: 'table-cell-1', type: 'labels' }, { id: `producer.${artifact.ui.identifierUnique}`, headerId: 'producer', headerLabel: 'Producer', - value: artifact.producer || {}, - class: 'table-cell-1', + value: artifact.producer.name || '', + template: , + className: 'table-cell-1', type: 'producer' }, { @@ -297,7 +312,7 @@ export const createFilesRowData = (artifact, project, frontendSpec, showExpandBu headerId: 'owner', headerLabel: 'Owner', value: artifact.producer?.owner, - class: 'table-cell-1', + className: 'table-cell-1', type: 'owner' }, { @@ -305,14 +320,14 @@ export const createFilesRowData = (artifact, project, frontendSpec, showExpandBu headerId: 'updated', headerLabel: 'Updated', value: formatDatetime(artifact.updated, 'N/A'), - class: 'table-cell-1' + className: 'table-cell-1' }, { id: `size.${artifact.ui.identifierUnique}`, headerId: 'size', headerLabel: 'Size', value: isNumber(artifact.size) && artifact.size >= 0 ? convertBytes(artifact.size) : 'N/A', - class: 'table-cell-1' + className: 'table-cell-1' } ] } @@ -352,7 +367,7 @@ export const createModelEndpointsRowData = (artifact, project) => { headerId: 'name', headerLabel: 'Name', value: name, - class: 'table-cell-name', + className: 'table-cell-name', getLink: tab => validateArguments(artifact.metadata?.uid, name) ? generateLinkToDetailsPanel( @@ -372,7 +387,7 @@ export const createModelEndpointsRowData = (artifact, project) => { headerId: 'function', headerLabel: 'Function', value: functionName, - class: 'table-cell-1', + className: 'table-cell-1', getLink: () => generateFunctionDetailsLink(artifact.spec?.function_uri), tooltip: functionUri }, @@ -380,7 +395,7 @@ export const createModelEndpointsRowData = (artifact, project) => { id: `state.${artifact.ui.identifierUnique}`, headerId: 'state', value: artifact.status?.state, - class: 'table-cell-small', + className: 'table-cell-small', type: 'hidden' }, { @@ -388,21 +403,21 @@ export const createModelEndpointsRowData = (artifact, project) => { headerId: 'version', headerLabel: 'Version', value: artifact?.status?.children ? 'Router' : tag, - class: 'table-cell-small' + className: 'table-cell-small' }, { id: `modelClass.${artifact.ui.identifierUnique}`, headerId: 'class', headerLabel: 'Class', value: artifact.spec?.model_class, - class: 'table-cell-1' + className: 'table-cell-1' }, { id: `labels.${artifact.ui.identifierUnique}`, headerId: 'labels', headerLabel: 'Labels', value: parseKeyValues(artifact.metadata?.labels), - class: 'table-cell-1', + className: 'table-cell-1', type: 'labels' }, { @@ -410,35 +425,35 @@ export const createModelEndpointsRowData = (artifact, project) => { headerId: 'uptime', headerLabel: 'Uptime', value: formatDatetime(artifact.status?.first_request, '-'), - class: 'table-cell-1' + className: 'table-cell-1' }, { id: `lastRequest.${artifact.ui.identifierUnique}`, headerId: 'lastprediction', headerLabel: 'Last prediction', value: formatDatetime(artifact.status?.last_request, '-'), - class: 'table-cell-1' + className: 'table-cell-1' }, { id: `averageLatency.${artifact.ui.identifierUnique}`, headerId: 'averagelatency', headerLabel: 'Average latency', value: averageLatency ? `${(averageLatency / 1000).toFixed(2)}ms` : '-', - class: 'table-cell-1' + className: 'table-cell-1' }, { id: `errorCount.${artifact.ui.identifierUnique}`, headerId: 'errorcount', headerLabel: 'Error count', value: artifact.status?.error_count ?? '-', - class: 'table-cell-1' + className: 'table-cell-1' }, { id: `driftStatus.${artifact.ui.identifierUnique}`, headerId: 'drift', headerLabel: 'Drift', value: driftStatusIcons[artifact.status?.drift_status]?.value, - class: 'table-cell-small', + className: 'table-cell-small', tooltip: driftStatusIcons[artifact.status?.drift_status]?.tooltip } ] @@ -458,7 +473,7 @@ export const createDatasetsRowData = (artifact, project, frontendSpec, showExpan headerId: 'name', headerLabel: 'Name', value: artifact.db_key, - class: 'table-cell-name', + className: 'table-cell-name', getLink: tab => validateArguments(artifact.db_key, tab, artifact.tree) ? generateLinkToDetailsPanel( @@ -473,7 +488,7 @@ export const createDatasetsRowData = (artifact, project, frontendSpec, showExpan ) : '', expandedCellContent: { - class: 'table-cell-name', + className: 'table-cell-name', showTag: true, tooltip: artifact.tag ? `${artifact.tag}${iter}` : `${artifact.tree}${iter}`, type: 'date', @@ -490,15 +505,16 @@ export const createDatasetsRowData = (artifact, project, frontendSpec, showExpan headerId: 'labels', headerLabel: 'Labels', value: parseKeyValues(artifact.labels), - class: 'table-cell-1', + className: 'table-cell-1', type: 'labels' }, { id: `producer.${artifact.ui.identifierUnique}`, headerId: 'producer', headerLabel: 'Producer', - value: artifact.producer, - class: 'table-cell-1', + value: artifact.producer.name || '', + template: , + className: 'table-cell-1', type: 'producer' }, { @@ -506,7 +522,7 @@ export const createDatasetsRowData = (artifact, project, frontendSpec, showExpan headerId: 'owner', headerLabel: 'Owner', value: artifact.producer?.owner, - class: 'table-cell-1', + className: 'table-cell-1', type: 'owner' }, { @@ -514,20 +530,20 @@ export const createDatasetsRowData = (artifact, project, frontendSpec, showExpan headerId: 'updated', headerLabel: 'Updated', value: formatDatetime(artifact.updated, 'N/A'), - class: 'table-cell-1' + className: 'table-cell-1' }, { id: `size.${artifact.ui.identifierUnique}`, headerId: 'size', headerLabel: 'Size', value: isNumber(artifact.size) && artifact.size >= 0 ? convertBytes(artifact.size) : 'N/A', - class: 'table-cell-1' + className: 'table-cell-1' }, { id: `version.${artifact.ui.identifierUnique}`, headerId: 'tag', value: artifact.tag, - class: 'table-cell-1', + className: 'table-cell-1', type: 'hidden' } ] diff --git a/src/utils/createConsumerGroupContent.js b/src/utils/createConsumerGroupContent.js index b151c9c9c..59e36641e 100644 --- a/src/utils/createConsumerGroupContent.js +++ b/src/utils/createConsumerGroupContent.js @@ -28,24 +28,24 @@ const createConsumerGroupsContent = content => { shardLagId: { id: `shardLagId.${identifier}`, value: contentItem.shardLagId, - class: 'table-cell-1', + className: 'table-cell-1', identifier: identifier, identifierUnique: identifier }, lagMsgBehind: { id: `lagMsgBehind.${identifier}`, value: contentItem.lag, - class: 'table-cell-1' + className: 'table-cell-1' }, lastSequence: { id: `lastSequence.${identifier}`, value: contentItem.current, - class: 'table-cell-1' + className: 'table-cell-1' }, committedOffset: { id: `committedOffset.${identifier}`, value: contentItem.committed, - class: 'table-cell-1' + className: 'table-cell-1' } } } diff --git a/src/utils/createConsumerGroupsContent.js b/src/utils/createConsumerGroupsContent.js index de3ebfe8c..db9ea22f5 100644 --- a/src/utils/createConsumerGroupsContent.js +++ b/src/utils/createConsumerGroupsContent.js @@ -29,7 +29,7 @@ const createConsumerGroupsContent = (content, params) => { consumerGroup: { id: `consumerGroup.${identifier}`, value: contentItem?.consumerGroup, - class: 'table-cell-1 text-bold', + className: 'table-cell-1 text-bold', identifier: identifier, identifierUnique: identifier, getLink: () => { @@ -39,7 +39,7 @@ const createConsumerGroupsContent = (content, params) => { streamPath: { id: `streamPath.${identifier}`, value: contentItem?.containerName + contentItem?.streamPath, - class: 'table-cell-1' + className: 'table-cell-1' }, realTimeFunction: { id: `realTimeFunction.${identifier}`, @@ -50,7 +50,7 @@ const createConsumerGroupsContent = (content, params) => { ) }, linkIsExternal: true, - class: 'table-cell-1' + className: 'table-cell-1' } } } diff --git a/src/utils/createFeatureStoreContent.js b/src/utils/createFeatureStoreContent.js index d4a9ecb1d..48b3c0189 100644 --- a/src/utils/createFeatureStoreContent.js +++ b/src/utils/createFeatureStoreContent.js @@ -63,7 +63,7 @@ export const createFeatureSetsRowData = (featureSet, pageTab, project, showExpan headerId: 'name', headerLabel: 'Name', value: featureSet.name, - class: 'table-cell-name', + className: 'table-cell-name', getLink: tab => validateArguments(featureSet.name, featureSet.tag, tab) ? generateLinkToDetailsPanel( @@ -79,7 +79,7 @@ export const createFeatureSetsRowData = (featureSet, pageTab, project, showExpan showTag: true, showStatus: true, expandedCellContent: { - class: 'table-cell-name', + className: 'table-cell-name', value: featureSet.tag || truncateUid(featureSet.uid), tooltip: featureSet.tag || featureSet.uid, showTag: true, @@ -92,21 +92,21 @@ export const createFeatureSetsRowData = (featureSet, pageTab, project, showExpan headerId: 'description', headerLabel: 'Description', value: featureSet.description, - class: 'table-cell-2' + className: 'table-cell-2' }, { id: `labels.${featureSet.ui.identifierUnique}`, headerId: 'labels', headerLabel: 'Labels', value: parseKeyValues(featureSet.labels), - class: 'table-cell-4', + className: 'table-cell-4', type: 'labels' }, { id: `version.${featureSet.ui.identifierUnique}`, headerId: 'tag', value: featureSet.tag, - class: 'table-cell-2', + className: 'table-cell-2', type: 'hidden' }, { @@ -115,14 +115,14 @@ export const createFeatureSetsRowData = (featureSet, pageTab, project, showExpan headerLabel: 'Entities', value: featureSet.entities?.slice(0, 2).map(entity => entity.name) || '', type: 'labels', - class: 'table-cell-2' + className: 'table-cell-2' }, { ...getFeatureSetTargetCellValue(featureSet.targets) }, { id: `buttonCopy.${featureSet.ui.identifierUnique}`, headerId: 'copy', value: '', - class: 'table-cell-icon', + className: 'table-cell-icon', type: BUTTON_COPY_URI_CELL_TYPE, actionHandler: item => generateUri(item, pageTab) } @@ -142,9 +142,9 @@ export const createFeaturesRowData = (feature, isTablePanelOpen, showExpandButto headerLabel: 'Feature Name', type: feature.ui.type, value: feature.name, - class: 'table-cell-name', + className: 'table-cell-name', expandedCellContent: { - class: 'table-cell-name', + className: 'table-cell-name', value: feature.metadata?.tag }, showExpandButton @@ -154,7 +154,7 @@ export const createFeaturesRowData = (feature, isTablePanelOpen, showExpandButto headerId: 'featureset', headerLabel: 'Feature set', value: feature.metadata?.name, - class: 'table-cell-2', + className: 'table-cell-2', getLink: tab => validateArguments(feature.metadata?.name, feature.metadata?.tag, tab) ? generateLinkToDetailsPanel( @@ -167,7 +167,7 @@ export const createFeaturesRowData = (feature, isTablePanelOpen, showExpandButto ) : '', expandedCellContent: { - class: 'table-cell-2', + className: 'table-cell-2', value: '' }, rowExpanded: { @@ -189,7 +189,7 @@ export const createFeaturesRowData = (feature, isTablePanelOpen, showExpandButto headerId: 'type', headerLabel: 'Type', value: feature.value_type, - class: 'table-cell-1' + className: 'table-cell-1' }, { id: `entity.${feature.ui.identifierUnique}`, @@ -197,21 +197,21 @@ export const createFeaturesRowData = (feature, isTablePanelOpen, showExpandButto headerLabel: 'Entities', type: 'labels', value: feature.spec?.entities.map(entity => entity.name) || '', - class: 'table-cell-2' + className: 'table-cell-2' }, { id: `description.${feature.ui.identifierUnique}`, headerId: 'description', headerLabel: 'Description', value: feature?.description ?? '', - class: 'table-cell-2' + className: 'table-cell-2' }, { id: `labels.${feature.ui.identifierUnique}`, headerId: 'labels', headerLabel: 'Labels', value: parseKeyValues(feature.labels), - class: 'table-cell-4', + className: 'table-cell-4', type: 'labels', hidden: isTablePanelOpen }, @@ -224,7 +224,7 @@ export const createFeaturesRowData = (feature, isTablePanelOpen, showExpandButto headerId: 'validator', headerLabel: 'Validator', value: , - class: 'table-cell-2', + className: 'table-cell-2', type: 'component', hidden: isTablePanelOpen }, @@ -232,7 +232,7 @@ export const createFeaturesRowData = (feature, isTablePanelOpen, showExpandButto id: `addFeature.${feature.ui.identifierUnique}`, headerId: 'addfeature', value: feature.ui.type === 'feature' && , - class: 'table-cell-2 align-right', + className: 'table-cell-2 align-right', type: 'component', hidden: !isTablePanelOpen } @@ -268,7 +268,7 @@ const getFeatureSetTargetCellValue = (targets, identifierUnique) => ({ ) .sort((icon, otherIcon) => (icon.tooltip < otherIcon.tooltip ? -1 : 1)), id: `targets.${identifierUnique}`, - class: 'targets-cell table-cell-2', + className: 'targets-cell table-cell-2', type: 'icons' }) @@ -283,7 +283,7 @@ export const createFeatureVectorsRowData = (featureVector, pageTab, project, sho headerId: 'name', headerLabel: 'Name', value: featureVector.name, - class: 'table-cell-name', + className: 'table-cell-name', getLink: tab => validateArguments(featureVector.name, featureVector.tag, tab) ? generateLinkToDetailsPanel( @@ -299,7 +299,7 @@ export const createFeatureVectorsRowData = (featureVector, pageTab, project, sho showTag: true, showStatus: true, expandedCellContent: { - class: 'table-cell-name', + className: 'table-cell-name', value: featureVector.tag || truncateUid(featureVector.uid), tooltip: featureVector.tag || featureVector.uid, showTag: true, @@ -312,21 +312,21 @@ export const createFeatureVectorsRowData = (featureVector, pageTab, project, sho headerId: 'description', headerLabel: 'Description', value: featureVector.description, - class: 'table-cell-3' + className: 'table-cell-3' }, { id: `labels.${featureVector.ui.identifierUnique}`, headerId: 'labels', headerLabel: 'Labels', value: parseKeyValues(featureVector.labels), - class: 'table-cell-4', + className: 'table-cell-4', type: 'labels' }, { id: `version.${featureVector.ui.identifierUnique}`, headerId: 'tag', value: featureVector.tag, - class: 'table-cell-2', + className: 'table-cell-2', type: 'hidden' }, { @@ -334,14 +334,14 @@ export const createFeatureVectorsRowData = (featureVector, pageTab, project, sho headerId: 'entities', headerLabel: 'Entities', value: featureVector.index_keys?.join(', ') ?? '', - class: 'table-cell-2' + className: 'table-cell-2' }, { id: `updated.${featureVector.ui.identifierUnique}`, headerId: 'updated', headerLabel: 'Updated', value: featureVector.updated ? formatDatetime(featureVector.updated, 'N/A') : 'N/A', - class: 'table-cell-2', + className: 'table-cell-2', showTag: true, showStatus: true }, @@ -349,7 +349,7 @@ export const createFeatureVectorsRowData = (featureVector, pageTab, project, sho id: `buttonCopy.${featureVector.ui.identifierUnique}`, headerId: 'copy', value: '', - class: 'table-cell-icon', + className: 'table-cell-icon', type: BUTTON_COPY_URI_CELL_TYPE, actionHandler: item => generateUri(item, pageTab) }, @@ -357,7 +357,7 @@ export const createFeatureVectorsRowData = (featureVector, pageTab, project, sho id: `uid.${featureVector.ui.identifierUnique}`, headerId: 'featurevectoruid', value: featureVector.uid, - class: 'table-cell-2', + className: 'table-cell-2', type: 'hidden' } ] diff --git a/src/utils/createFunctionsContent.js b/src/utils/createFunctionsContent.js index a837220d6..8f2918ff3 100644 --- a/src/utils/createFunctionsContent.js +++ b/src/utils/createFunctionsContent.js @@ -36,7 +36,7 @@ const createFunctionsContent = (functions, pageTab, projectName, showExpandButto headerId: 'name', headerLabel: 'Name', value: func.name, - class: 'table-cell-name', + className: 'table-cell-name', getLink: hash => { return `/projects/${projectName}/${MODELS_PAGE.toLowerCase()}/${REAL_TIME_PIPELINES_TAB}/pipeline/${hash}` }, @@ -44,7 +44,7 @@ const createFunctionsContent = (functions, pageTab, projectName, showExpandButto showStatus: true, expandedCellContent: { value: formatDatetime(func.updated, 'N/A'), - class: 'table-cell-name', + className: 'table-cell-name', type: 'date', showTag: true, showStatus: true @@ -56,7 +56,7 @@ const createFunctionsContent = (functions, pageTab, projectName, showExpandButto headerId: 'type', headerLabel: 'Type', value: func.graph?.kind === 'router' ? 'Router' : 'Flow', - class: 'table-cell-small', + className: 'table-cell-small', type: 'type' }, { @@ -64,7 +64,7 @@ const createFunctionsContent = (functions, pageTab, projectName, showExpandButto headerId: 'function', headerLabel: 'Function', value: func.name, - class: 'table-cell-2', + className: 'table-cell-2', getLink: tab => validateArguments(func.hash, tab) ? generateLinkToDetailsPanel( @@ -81,7 +81,7 @@ const createFunctionsContent = (functions, pageTab, projectName, showExpandButto id: `updated.${func.ui.identifierUnique}`, headerId: 'updated', value: formatDatetime(func.updated, 'N/A'), - class: 'table-cell-2', + className: 'table-cell-2', type: 'date', showTag: true, showStatus: true, @@ -99,13 +99,13 @@ const createFunctionsContent = (functions, pageTab, projectName, showExpandButto headerId: 'name', headerLabel: 'Name', value: func.name, - class: 'table-cell-name', + className: 'table-cell-name', getLink: (hash, tab) => { return `/projects/${projectName}/functions/${hash}${`/${tab}`}` }, expandedCellContent: { value: formatDatetime(func.updated, 'N/A'), - class: 'table-cell-name', + className: 'table-cell-name', type: 'date', showTag: true, showStatus: true @@ -119,7 +119,7 @@ const createFunctionsContent = (functions, pageTab, projectName, showExpandButto headerId: 'kind', headerLabel: 'Kind', value: func.type, - class: 'table-cell-small', + className: 'table-cell-small', type: 'type' }, { @@ -127,7 +127,7 @@ const createFunctionsContent = (functions, pageTab, projectName, showExpandButto headerId: 'hash', headerLabel: 'Hash', value: func.hash, - class: 'table-cell-1', + className: 'table-cell-1', type: 'hash' }, { @@ -135,7 +135,7 @@ const createFunctionsContent = (functions, pageTab, projectName, showExpandButto headerId: 'updated', headerLabel: 'Updated', value: formatDatetime(func.updated, 'N/A'), - class: 'table-cell-2', + className: 'table-cell-2', type: 'date', showTag: true, showStatus: true @@ -145,21 +145,21 @@ const createFunctionsContent = (functions, pageTab, projectName, showExpandButto headerId: 'command', headerLabel: 'Command', value: func.command, - class: 'table-cell-1' + className: 'table-cell-1' }, { id: `image.${func.ui.identifierUnique}`, headerId: 'image', headerLabel: 'Image', value: getFunctionImage(func), - class: 'table-cell-1' + className: 'table-cell-1' }, { id: `description.${func.ui.identifierUnique}`, headerId: 'description', headerLabel: 'Description', value: func.description, - class: 'table-cell-2' + className: 'table-cell-2' }, { id: `tag.${func.ui.identifierUnique}`, diff --git a/src/utils/createJobsContent.js b/src/utils/createJobsContent.js index fa29e5b9e..738187c94 100644 --- a/src/utils/createJobsContent.js +++ b/src/utils/createJobsContent.js @@ -72,7 +72,7 @@ export const createJobsMonitorTabContent = (jobs, jobName, isStagingMode) => { headerLabel: jobName ? 'UID' : 'Name', id: `name.${identifierUnique}`, value: jobName ? job.uid || job.id : job.name, - class: 'table-cell-name', + className: 'table-cell-name', type: type === JOB_KIND_WORKFLOW && !isStagingMode ? 'hidden' : 'link', getLink, showStatus: true @@ -82,14 +82,14 @@ export const createJobsMonitorTabContent = (jobs, jobName, isStagingMode) => { headerLabel: 'Type', id: `type.${identifierUnique}`, value: type, - class: 'table-cell-1', + className: 'table-cell-1', type: 'type' }, { headerId: 'job.uid', id: `uid.${identifierUnique}`, value: job.uid || job.id, - class: 'table-cell-1', + className: 'table-cell-1', type: 'hidden' }, { @@ -101,7 +101,7 @@ export const createJobsMonitorTabContent = (jobs, jobName, isStagingMode) => { (job.state?.value !== 'running' && job.updated) || (job.state?.value !== 'error' && new Date(job.finished_at)) ), - class: 'table-cell-1', + className: 'table-cell-1', type: 'duration' }, { @@ -109,14 +109,14 @@ export const createJobsMonitorTabContent = (jobs, jobName, isStagingMode) => { headerLabel: 'Owner', id: `owner.${identifierUnique}`, value: job.owner, - class: 'table-cell-1' + className: 'table-cell-1' }, { headerId: 'labels', headerLabel: 'Labels', id: `labels.${identifierUnique}`, value: job.labels, - class: 'table-cell-1', + className: 'table-cell-1', type: 'labels' }, { @@ -124,7 +124,7 @@ export const createJobsMonitorTabContent = (jobs, jobName, isStagingMode) => { headerLabel: 'Parameters', id: `parameters.${identifierUnique}`, value: job.parametersChips, - class: 'table-cell-1', + className: 'table-cell-1', type: 'parameters' }, { @@ -132,14 +132,14 @@ export const createJobsMonitorTabContent = (jobs, jobName, isStagingMode) => { headerLabel: 'Results', id: `resultsChips.${identifierUnique}`, value: job.resultsChips, - class: 'table-cell-3', + className: 'table-cell-3', type: 'results' }, { headerId: 'updated', id: `updated.${identifierUnique}`, value: job.updated || new Date(job.finished_at), - class: 'table-cell-1', + className: 'table-cell-1', type: 'hidden' } ] @@ -174,7 +174,7 @@ export const createJobsScheduleTabContent = jobs => { headerLabel: 'Name', id: `name.${identifierUnique}`, value: job.name, - class: 'table-cell-name', + className: 'table-cell-name', showStatus: true, getLink: tab => validateArguments(scheduleJobFunctionUid, tab) @@ -194,7 +194,7 @@ export const createJobsScheduleTabContent = jobs => { headerLabel: 'Type', id: `type.${identifierUnique}`, value: job.type, - class: 'table-cell-small', + className: 'table-cell-small', type: 'type' }, { @@ -202,7 +202,7 @@ export const createJobsScheduleTabContent = jobs => { headerLabel: 'Next run (Local TZ)', id: `nextRun.${identifierUnique}`, value: formatDatetime(job.nextRun), - class: 'table-cell-1', + className: 'table-cell-1', type: 'date' }, { @@ -210,7 +210,7 @@ export const createJobsScheduleTabContent = jobs => { headerLabel: 'Schedule (UTC)', id: `schedule.${identifierUnique}`, value: job.scheduled_object?.schedule || null, - class: 'table-cell-1', + className: 'table-cell-1', tip: 'The first day of the week (0) is Monday, and not Sunday.' }, { @@ -218,7 +218,7 @@ export const createJobsScheduleTabContent = jobs => { headerLabel: 'Labels', id: `labels.${identifierUnique}`, value: parseKeyValues(job.scheduled_object?.task.metadata.labels || {}), - class: 'table-cell-1', + className: 'table-cell-1', type: 'labels' }, { @@ -226,7 +226,7 @@ export const createJobsScheduleTabContent = jobs => { headerLabel: 'Last run (Local TZ)', id: `lastRun.${identifierUnique}`, value: formatDatetime(job.startTime), - class: 'table-cell-1', + className: 'table-cell-1', getLink: lastRunLink }, { @@ -234,14 +234,14 @@ export const createJobsScheduleTabContent = jobs => { headerLabel: 'Created time (Local TZ)', id: `createdTime.${identifierUnique}`, value: formatDatetime(job.createdTime, 'Not yet started'), - class: 'table-cell-1', + className: 'table-cell-1', type: 'date' }, { headerId: 'function', id: `func.${identifierUnique}`, value: job.func, - class: '', + className: '', type: 'hidden' } ] @@ -269,7 +269,7 @@ export const createJobsWorkflowsTabContent = (jobs, projectName, isStagingMode, headerLabel: 'Name', id: `name.${identifierUnique}`, value: jobName, - class: 'table-cell-name', + className: 'table-cell-name', type: 'link', getLink: () => { return getWorkflowDetailsLink(projectName, job.id, null, null, MONITOR_WORKFLOWS_TAB) @@ -280,7 +280,7 @@ export const createJobsWorkflowsTabContent = (jobs, projectName, isStagingMode, headerId: 'uid', id: `uid.${identifierUnique}`, value: job?.id, - class: 'table-cell-1', + className: 'table-cell-1', type: 'hidden', hidden: isSelectedItem }, @@ -289,7 +289,7 @@ export const createJobsWorkflowsTabContent = (jobs, projectName, isStagingMode, headerLabel: 'Created at', id: `createdAt.${identifierUnique}`, value: formatDatetime(job.created_at, 'N/A'), - class: 'table-cell-1', + className: 'table-cell-1', hidden: isSelectedItem }, { @@ -297,7 +297,7 @@ export const createJobsWorkflowsTabContent = (jobs, projectName, isStagingMode, headerLabel: 'Finished at', id: `finishedAt.${identifierUnique}`, value: formatDatetime(job.finished_at, 'N/A'), - class: 'table-cell-1', + className: 'table-cell-1', hidden: isSelectedItem }, { @@ -309,7 +309,7 @@ export const createJobsWorkflowsTabContent = (jobs, projectName, isStagingMode, (job.state?.value !== 'running' && job.updated) || (job.state?.value !== 'error' && new Date(job.finished_at)) ), - class: 'table-cell-1', + className: 'table-cell-1', type: 'duration', hidden: isSelectedItem }, @@ -317,7 +317,7 @@ export const createJobsWorkflowsTabContent = (jobs, projectName, isStagingMode, headerId: 'updated', id: `updated.${identifierUnique}`, value: job.updated || new Date(job.finished_at), - class: 'table-cell-1', + className: 'table-cell-1', type: 'hidden', hidden: isSelectedItem } @@ -352,7 +352,7 @@ export const createJobsWorkflowContent = ( headerLabel: 'Name', id: `name.${identifierUnique}`, value: jobName, - class: 'table-cell-name', + className: 'table-cell-name', type: 'link', getLink: tab => { return getWorkflowDetailsLink( @@ -370,7 +370,7 @@ export const createJobsWorkflowContent = ( headerLabel: 'Kind', id: `kind.${identifierUnique}`, value: job.run_type, - class: 'table-cell-1', + className: 'table-cell-1', type: 'type', hidden: isSelectedItem }, @@ -378,7 +378,7 @@ export const createJobsWorkflowContent = ( headerId: 'uid', id: `uid.${identifierUnique}`, value: job.uid || job.id, - class: 'table-cell-1', + className: 'table-cell-1', type: 'hidden', hidden: isSelectedItem }, @@ -387,7 +387,7 @@ export const createJobsWorkflowContent = ( headerLabel: 'Started at', id: `startedAt.${identifierUnique}`, value: formatDatetime(job.startedAt, 'N/A'), - class: 'table-cell-1', + className: 'table-cell-1', hidden: isSelectedItem }, { @@ -395,7 +395,7 @@ export const createJobsWorkflowContent = ( headerLabel: 'Finished at', id: `finishedAt.${identifierUnique}`, value: formatDatetime(job.finishedAt, 'N/A'), - class: 'table-cell-1', + className: 'table-cell-1', hidden: isSelectedItem }, { @@ -406,7 +406,7 @@ export const createJobsWorkflowContent = ( new Date(job.startedAt), job.state?.value !== 'error' && new Date(job.finishedAt) ), - class: 'table-cell-1', + className: 'table-cell-1', type: 'duration', hidden: isSelectedItem } From 34a418f779c911028472339464d3e5b9ebcb037f Mon Sep 17 00:00:00 2001 From: mariana-furyk <58301139+mariana-furyk@users.noreply.github.com> Date: Thu, 9 Nov 2023 10:32:23 +0200 Subject: [PATCH 07/10] Fix [Artifacts] UI crash when artifact doesn't have a producer (#2068) --- .../TableProducerCell/TableProducerCell.js | 54 ++++++++++--------- src/utils/createArtifactsContent.js | 6 +-- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/elements/TableProducerCell/TableProducerCell.js b/src/elements/TableProducerCell/TableProducerCell.js index 2c8a767c4..1c585f758 100644 --- a/src/elements/TableProducerCell/TableProducerCell.js +++ b/src/elements/TableProducerCell/TableProducerCell.js @@ -35,32 +35,34 @@ const TableProducerCell = ({ bodyCellClassName, className, producer }) => { const cellClassNames = classnames('table-body__cell', className, bodyCellClassName) return ( -
    - {producer.name && uid && ( - -
    - - } - > - {producer.name} - -
    - - )} - {producer.name && !uid && ( - }>{producer.name} - )} -
    + {producer?.name && uid && ( + +
    + + } + > + {producer.name} + +
    + + )} + {producer?.name && !uid && ( + }>{producer.name} + )} +