@@ -72,6 +121,7 @@ const ArtifactsFilters = ({ artifacts, isAllVersions }) => {
ArtifactsFilters.propTypes = {
artifacts: PropTypes.arrayOf(PropTypes.object).isRequired,
+ filtersConfig: PropTypes.object.isRequired,
isAllVersions: PropTypes.bool.isRequired
}
diff --git a/src/components/ArtifactsPreview/ArtifactsPreview.jsx b/src/components/ArtifactsPreview/ArtifactsPreview.jsx
index 2ad5abc0f4..28c9f1bf9a 100644
--- a/src/components/ArtifactsPreview/ArtifactsPreview.jsx
+++ b/src/components/ArtifactsPreview/ArtifactsPreview.jsx
@@ -22,8 +22,8 @@ import PropTypes from 'prop-types'
import classnames from 'classnames'
import ArtifactsPreviewView from './ArtifactsPreviewView'
-import Loader from '../../common/Loader/Loader'
import NoData from '../../common/NoData/NoData'
+import { Loader } from 'igz-controls/components'
const ArtifactsPreview = ({ className = '', noData, preview }) => {
const [showErrorBody, setShowErrorBody] = useState(false)
diff --git a/src/components/ArtifactsPreview/ArtifactsPreviewView.jsx b/src/components/ArtifactsPreview/ArtifactsPreviewView.jsx
index 3b9b2fcb4a..85a61ba9ec 100644
--- a/src/components/ArtifactsPreview/ArtifactsPreviewView.jsx
+++ b/src/components/ArtifactsPreview/ArtifactsPreviewView.jsx
@@ -26,7 +26,7 @@ import PreviewError from './PreviewError/PreviewError'
import { Tooltip, TextTooltipTemplate } from 'igz-controls/components'
import WarningMessage from '../../common/WarningMessage/WarningMessage'
-import { ARTIFACT_PREVIEW_TABLE_ROW_LIMIT, ERROR_STATE } from '../../constants'
+import { ARTIFACT_PREVIEW_TABLE_ROW_LIMIT, ERROR_STATE, UNKNOWN_STATE } from '../../constants'
import './artifactsPreview.scss'
@@ -145,7 +145,7 @@ const ArtifactsPreviewView = ({ className, preview, setShowErrorBody, showErrorB
alt="preview"
/>
)}
- {preview?.type === 'unknown' && (
+ {preview?.type === UNKNOWN_STATE && (
{preview?.data?.content ? preview?.data.content : 'No preview'}
diff --git a/src/components/ArtifactsPreview/artifactsPreview.scss b/src/components/ArtifactsPreview/artifactsPreview.scss
index 9c8feb71c2..bbcc290cba 100644
--- a/src/components/ArtifactsPreview/artifactsPreview.scss
+++ b/src/components/ArtifactsPreview/artifactsPreview.scss
@@ -20,7 +20,7 @@
&-title {
display: flex;
align-items: center;
- margin: 0 0 15px 0;
+ margin: 0 0 15px;
color: colors.$topaz;
font-weight: 500;
font-size: 20px;
diff --git a/src/components/ArtifactsPreview/artifactsPreviewController.scss b/src/components/ArtifactsPreview/artifactsPreviewController.scss
index 9a9f12a7f3..94573d135a 100644
--- a/src/components/ArtifactsPreview/artifactsPreviewController.scss
+++ b/src/components/ArtifactsPreview/artifactsPreviewController.scss
@@ -10,11 +10,11 @@
}
.icon-popout {
- text-align: end;
- cursor: pointer;
- min-height: 40px;
width: 100%;
height: auto;
- padding: 0;
+ min-height: 40px;
margin-bottom: 10px;
+ padding: 0;
+ text-align: end;
+ cursor: pointer;
}
diff --git a/src/components/ConsumerGroup/ConsumerGroup.jsx b/src/components/ConsumerGroup/ConsumerGroup.jsx
index 43156676f8..be29d755ba 100644
--- a/src/components/ConsumerGroup/ConsumerGroup.jsx
+++ b/src/components/ConsumerGroup/ConsumerGroup.jsx
@@ -22,13 +22,12 @@ import { useDispatch, useSelector } from 'react-redux'
import { isEmpty } from 'lodash'
import { useParams } from 'react-router-dom'
-import Loader from '../../common/Loader/Loader'
+import ConsumerGroupShardLagTableRow from '../../elements/ConsumerGroupShardLagTableRow/ConsumerGroupShardLagTableRow'
import NoData from '../../common/NoData/NoData'
import PageHeader from '../../elements/PageHeader/PageHeader'
-import Table from '../Table/Table'
-import { RoundedIcon } from 'igz-controls/components'
-import ConsumerGroupShardLagTableRow from '../../elements/ConsumerGroupShardLagTableRow/ConsumerGroupShardLagTableRow'
import Search from '../../common/Search/Search'
+import Table from '../Table/Table'
+import { RoundedIcon, Loader } from 'igz-controls/components'
import {
CONSUMER_GROUP_PAGE,
@@ -38,7 +37,7 @@ import createConsumerGroupContent from '../../utils/createConsumerGroupContent'
import { fetchNuclioV3ioStreamShardLags, resetV3ioStreamShardLagsError } from '../../reducers/nuclioReducer.js'
import { generatePageData } from './consumerGroup.util.js'
import { getNoDataMessage } from '../../utils/getNoDataMessage'
-import { showErrorNotification } from '../../utils/notifications.util'
+import { showErrorNotification } from 'igz-controls/utils/notification.util'
import RefreshIcon from 'igz-controls/images/refresh.svg?react'
diff --git a/src/components/ConsumerGroups/ConsumerGroups.jsx b/src/components/ConsumerGroups/ConsumerGroups.jsx
index 5f86492ffd..099c79b4a9 100644
--- a/src/components/ConsumerGroups/ConsumerGroups.jsx
+++ b/src/components/ConsumerGroups/ConsumerGroups.jsx
@@ -21,12 +21,12 @@ import React, { useEffect, useMemo, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useParams, useOutletContext } from 'react-router-dom'
-import Loader from '../../common/Loader/Loader'
import NoData from '../../common/NoData/NoData'
import PageHeader from '../../elements/PageHeader/PageHeader'
import Search from '../../common/Search/Search'
import Table from '../Table/Table'
import ConsumerGroupTableRow from '../../elements/ConsumerGroupTableRow/ConsumerGroupTableRow'
+import { Loader } from 'igz-controls/components'
import createConsumerGroupsContent from '../../utils/createConsumerGroupsContent'
import { CONSUMER_GROUPS_PAGE, GROUP_BY_NONE, NAME_FILTER } from '../../constants'
diff --git a/src/components/ConsumerGroupsWrapper/ConsumerGroupsWrapper.jsx b/src/components/ConsumerGroupsWrapper/ConsumerGroupsWrapper.jsx
index e43c882ced..42957a1e37 100644
--- a/src/components/ConsumerGroupsWrapper/ConsumerGroupsWrapper.jsx
+++ b/src/components/ConsumerGroupsWrapper/ConsumerGroupsWrapper.jsx
@@ -22,14 +22,14 @@ import { useNavigate, useParams, Outlet } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'
import { isEmpty } from 'lodash'
+import { Loader } from 'igz-controls/components'
import Breadcrumbs from '../../common/Breadcrumbs/Breadcrumbs'
-import Loader from '../../common/Loader/Loader'
import { GROUP_BY_NONE } from '../../constants'
import { areNuclioStreamsEnabled } from '../../utils/helper'
import { fetchNuclioV3ioStreams, resetV3ioStreamsError } from '../../reducers/nuclioReducer'
import { setFilters } from '../../reducers/filtersReducer'
-import { showErrorNotification } from '../../utils/notifications.util'
+import { showErrorNotification } from 'igz-controls/utils/notification.util'
const ConsumerGroupsWrapper = () => {
const [requestErrorMessage, setRequestErrorMessage] = useState('')
diff --git a/src/components/Datasets/datasets.util.jsx b/src/components/Datasets/datasets.util.jsx
index 7ce396d648..cc2319809b 100644
--- a/src/components/Datasets/datasets.util.jsx
+++ b/src/components/Datasets/datasets.util.jsx
@@ -22,21 +22,23 @@ import React from 'react'
import JobWizard from '../JobWizard/JobWizard'
import DeleteArtifactPopUp from '../../elements/DeleteArtifactPopUp/DeleteArtifactPopUp'
+import { ARTIFACT_MAX_DOWNLOAD_SIZE, DATASET_TYPE, DATASETS_PAGE } from '../../constants'
+import { PRIMARY_BUTTON, FULL_VIEW_MODE } from 'igz-controls/constants'
import {
- ARTIFACT_MAX_DOWNLOAD_SIZE,
- DATASET_TYPE,
- DATASETS_PAGE,
- FULL_VIEW_MODE
-} from '../../constants'
-import { PRIMARY_BUTTON } from 'igz-controls/constants'
-import { applyTagChanges, chooseOrFetchArtifact } from '../../utils/artifacts.util'
-import { copyToClipboard } from '../../utils/copyToClipboard'
+ applyTagChanges,
+ chooseOrFetchArtifact,
+ processActionAfterTagUniquesValidation
+} from '../../utils/artifacts.util'
import { getIsTargetPathValid } from '../../utils/createArtifactsContent'
import { showArtifactsPreview } from '../../reducers/artifactsReducer'
import { generateUri } from '../../utils/resources'
import { handleDeleteArtifact } from '../../utils/handleDeleteArtifact'
-import { openPopUp, openDeleteConfirmPopUp } from 'igz-controls/utils/common.util'
+import { openPopUp, openDeleteConfirmPopUp, copyToClipboard } from 'igz-controls/utils/common.util'
import { setDownloadItem, setShowDownloadsList } from '../../reducers/downloadReducer'
+import {
+ decreaseDetailsLoadingCounter,
+ increaseDetailsLoadingCounter
+} from '../../reducers/detailsReducer'
import TagIcon from 'igz-controls/images/tag-icon.svg?react'
import YamlIcon from 'igz-controls/images/yaml.svg?react'
@@ -65,7 +67,7 @@ export const infoHeaders = [
export const registerDatasetTitle = 'Register dataset'
-export const generateDataSetsDetailsMenu = (selectedItem, isDemoMode) => [
+export const generateDataSetsDetailsMenu = selectedItem => [
{
label: 'overview',
id: 'overview'
@@ -82,21 +84,15 @@ export const generateDataSetsDetailsMenu = (selectedItem, isDemoMode) => [
{
label: 'analysis',
id: 'analysis',
- hidden: !isDemoMode || !selectedItem?.extra_data
+ hidden: !selectedItem?.extra_data
}
]
-export const generatePageData = (
- viewMode,
- selectedItem,
- params,
- isDetailsPopUp = false,
- isDemoMode
-) => {
+export const generatePageData = (viewMode, isDetailsPopUp = false, selectedItem, params) => {
return {
page: DATASETS_PAGE,
details: {
- menu: generateDataSetsDetailsMenu(selectedItem, isDemoMode),
+ menu: generateDataSetsDetailsMenu(selectedItem),
infoHeaders,
type: DATASETS_PAGE,
hideBackBtn: viewMode === FULL_VIEW_MODE && !isDetailsPopUp,
@@ -129,7 +125,17 @@ export const handleApplyDetailsChanges = (
setNotification,
dispatch
) => {
- return applyTagChanges(changes, selectedItem, projectName, dispatch, setNotification)
+ return processActionAfterTagUniquesValidation({
+ tag: changes?.data?.tag?.currentFieldValue,
+ artifact: selectedItem,
+ projectName,
+ dispatch,
+ actionCallback: () =>
+ applyTagChanges(changes, selectedItem, projectName, dispatch, setNotification),
+ throwError: true,
+ showLoader: () => dispatch(increaseDetailsLoadingCounter()),
+ hideLoader: () => dispatch(decreaseDetailsLoadingCounter())
+ })
}
export const generateActionsMenu = (
diff --git a/src/components/Details/Details.jsx b/src/components/Details/Details.jsx
index b99e4bfb04..10617d5f5e 100644
--- a/src/components/Details/Details.jsx
+++ b/src/components/Details/Details.jsx
@@ -17,23 +17,12 @@ 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, useCallback, useRef, useMemo, useState } from 'react'
+import React, { useEffect, useCallback } from 'react'
import PropTypes from 'prop-types'
-import { useLocation, useParams } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'
-import { createForm } from 'final-form'
-import arrayMutators from 'final-form-arrays'
-import { Form } from 'react-final-form'
-import { cloneDeep, isEqual, pickBy } from 'lodash'
-import classnames from 'classnames'
-import { ConfirmDialog } from 'igz-controls/components'
-import ErrorMessage from '../../common/ErrorMessage/ErrorMessage'
-import DetailsTabsContent from './DetailsTabsContent/DetailsTabsContent'
import DetailsHeader from './DetailsHeader/DetailsHeader'
-import Loader from '../../common/Loader/Loader'
-import TabsSlider from '../../common/TabsSlider/TabsSlider'
-import BlockerSpy from '../../common/BlockerSpy/BlockerSpy'
+import DetailsTabsContent from './DetailsTabsContent/DetailsTabsContent'
import {
ALERTS_PAGE,
@@ -47,8 +36,7 @@ import {
JOBS_PAGE,
LLM_PROMPTS_PAGE,
MODEL_ENDPOINTS_TAB,
- MODELS_TAB,
- VIEW_SEARCH_PARAMETER
+ MODELS_TAB
} from '../../constants'
import {
generateAlertsContent,
@@ -57,24 +45,13 @@ import {
generateFunctionsContent,
generateJobsContent
} from './details.util'
-import {
- removeDetailsPopUpInfoContent,
- removeInfoContent,
- removeModelFeatureVector,
- resetChanges,
- setDetailsPopUpInfoContent,
- setEditMode,
- setFiltersWasHandled,
- setInfoContent,
- showWarning
-} from '../../reducers/detailsReducer'
-import { ACTIONS_MENU } from '../../types'
-import { TERTIARY_BUTTON, PRIMARY_BUTTON } from 'igz-controls/constants'
+import { DETAILS_MENU, ACTIONS_MENU } from 'igz-controls/types'
import { isEveryObjectValueEmpty } from '../../utils/isEveryObjectValueEmpty'
-import { setFieldState } from 'igz-controls/utils/form.util'
+import { removeModelFeatureVector } from '../../reducers/detailsReducer'
import { showArtifactsPreview } from '../../reducers/artifactsReducer'
+import { useDetails } from 'igz-controls/hooks/useDetails.hook'
-import './details.scss'
+import 'igz-controls/scss/details.scss'
const Details = ({
actionsMenu,
@@ -94,37 +71,36 @@ const Details = ({
tab = '',
withActionMenu = true
}) => {
- const [blocker, setBlocker] = useState({})
- const applyChangesRef = useRef()
- const dispatch = useDispatch()
- const detailsRef = useRef()
- const params = useParams()
+ const {
+ DetailsContainer,
+ applyChanges,
+ applyChangesRef,
+ blocker,
+ cancelChanges,
+ detailsPanelClassNames,
+ detailsRef,
+ commonDetailsStore,
+ doNotLeavePage,
+ form,
+ handleShowWarning,
+ leavePage,
+ location,
+ params,
+ removeDetailsInfo,
+ setBlocker,
+ setDetailsInfo,
+ shouldDetailsBlock
+ } = useDetails({
+ applyDetailsChanges,
+ applyDetailsChangesCallback,
+ formInitialValues,
+ isDetailsPopUp,
+ isDetailsScreen,
+ selectedItem
+ })
const detailsStore = useSelector(store => store.detailsStore)
const frontendSpec = useSelector(store => store.appStore.frontendSpec)
- const location = useLocation()
- const [setDetailsInfo, removeDetailsInfo] = useMemo(() => {
- return isDetailsPopUp
- ? [setDetailsPopUpInfoContent, removeDetailsPopUpInfoContent]
- : [setInfoContent, removeInfoContent]
- }, [isDetailsPopUp])
- const previousPathnameRef = useRef(
- location.pathname.substring(0, location.pathname.lastIndexOf(params.tab))
- )
-
- const detailsPanelClassNames = classnames(
- 'table__item',
- detailsStore.showWarning && 'pop-up-dialog-opened',
- isDetailsScreen && 'table__item_big',
- isDetailsPopUp && 'table__item-popup'
- )
-
- const formRef = useRef(
- createForm({
- initialValues: formInitialValues,
- mutators: { ...arrayMutators, setFieldState },
- onSubmit: () => {}
- })
- )
+ const dispatch = useDispatch()
const handlePreview = useCallback(() => {
dispatch(
@@ -135,14 +111,6 @@ const Details = ({
)
}, [dispatch, selectedItem])
- useEffect(() => {
- return () => {
- if (!isDetailsPopUp) {
- dispatch(resetChanges())
- }
- }
- }, [dispatch, isDetailsPopUp])
-
useEffect(() => {
if (!isEveryObjectValueEmpty(selectedItem)) {
if (pageData.details.type === JOBS_PAGE) {
@@ -200,189 +168,54 @@ const Details = ({
}
}, [dispatch, pageData.details.type, removeDetailsInfo, selectedItem])
- const handleShowWarning = useCallback(
- show => {
- dispatch(showWarning(show))
- },
- [dispatch]
- )
-
- const handleRefreshClick = useCallback(
- event => {
- if (
- detailsStore.changes.counter > 0 &&
- document.getElementById('refresh')?.contains(event.target)
- ) {
- handleShowWarning(true)
- dispatch(setFiltersWasHandled(true))
- }
- },
- [detailsStore.changes.counter, dispatch, handleShowWarning]
- )
-
- useEffect(() => {
- window.addEventListener('click', handleRefreshClick)
-
- return () => {
- window.removeEventListener('click', handleRefreshClick)
- }
- }, [handleRefreshClick])
-
- const shouldDetailsBlock = useCallback(
- ({ currentLocation, nextLocation }) => {
- const currentDetailsView = currentLocation.search.split(`${VIEW_SEARCH_PARAMETER}=`)?.[1]
- const nextDetailsView = nextLocation.search.split(`${VIEW_SEARCH_PARAMETER}=`)?.[1]
- const currentLocationPathname = currentLocation.pathname.split('/')
- const nextLocationPathname = nextLocation.pathname.split('/')
- currentLocationPathname.pop()
- nextLocationPathname.pop()
-
- return (
- detailsStore.changes.counter > 0 &&
- (currentLocationPathname.join('/') !== nextLocationPathname.join('/') ||
- currentDetailsView !== nextDetailsView)
- )
- },
- [detailsStore.changes.counter]
- )
-
- useEffect(() => {
- if (
- formRef.current &&
- detailsStore.changes.counter === 0 &&
- !isEqual(pickBy(formInitialValues), pickBy(formRef.current.getState()?.values)) &&
- !formRef.current.getState()?.active
- ) {
- formRef.current.restart(formInitialValues)
- }
- }, [formInitialValues, detailsStore.changes.counter])
-
- useEffect(() => {
- const currentPathname = location.pathname.substring(
- 0,
- location.pathname.lastIndexOf(params.tab)
- )
-
- if (previousPathnameRef.current !== currentPathname && !isDetailsPopUp) {
- formRef.current.restart(formInitialValues)
- dispatch(setEditMode(false))
- previousPathnameRef.current = currentPathname
- }
- }, [dispatch, formInitialValues, isDetailsPopUp, location.pathname, params.tab])
-
- const applyChanges = useCallback(() => {
- applyDetailsChanges(detailsStore.changes).then(() => {
- dispatch(resetChanges())
-
- const changes = cloneDeep(detailsStore.changes)
-
- // todo [redux-toolkit] rework it after redux-toolkit will be added to the details store. Need to remove setTimeout and use a Promise that resolves after the state is updated.
- setTimeout(() => {
- applyDetailsChangesCallback(changes, selectedItem)
- })
- })
- }, [
- applyDetailsChanges,
- applyDetailsChangesCallback,
- detailsStore.changes,
- dispatch,
- selectedItem
- ])
-
- const cancelChanges = useCallback(() => {
- if (detailsStore.changes.counter > 0) {
- dispatch(resetChanges())
- formRef.current.reset(formInitialValues)
- }
- }, [detailsStore.changes.counter, dispatch, formInitialValues])
-
- const leavePage = useCallback(() => {
- cancelChanges()
- handleShowWarning(false)
-
- if (detailsStore.filtersWasHandled) {
- dispatch(setFiltersWasHandled(false))
- } else {
- blocker.proceed?.()
- }
-
- window.dispatchEvent(new CustomEvent('discardChanges'))
- }, [blocker, cancelChanges, detailsStore.filtersWasHandled, dispatch, handleShowWarning])
-
- const doNotLeavePage = useCallback(() => {
- blocker.reset?.()
- dispatch(showWarning(false))
- window.dispatchEvent(new CustomEvent('cancelLeave'))
- }, [blocker, dispatch])
-
return (
-
+ setBlocker={setBlocker}
+ shouldDetailsBlock={shouldDetailsBlock}
+ withActionMenu={withActionMenu}
+ />
)
}
@@ -390,13 +223,7 @@ Details.propTypes = {
actionsMenu: ACTIONS_MENU.isRequired,
applyDetailsChanges: PropTypes.func,
applyDetailsChangesCallback: PropTypes.func,
- detailsMenu: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
- hidden: PropTypes.bool
- })
- ).isRequired,
+ detailsMenu: DETAILS_MENU.isRequired,
detailsPopUpSelectedTab: PropTypes.string,
formInitialValues: PropTypes.object,
getCloseDetailsLink: PropTypes.func,
diff --git a/src/components/Details/DetailsGenerationConfiguration/DetailsGenerationConfiguration.jsx b/src/components/Details/DetailsGenerationConfiguration/DetailsGenerationConfiguration.jsx
new file mode 100644
index 0000000000..a95293080a
--- /dev/null
+++ b/src/components/Details/DetailsGenerationConfiguration/DetailsGenerationConfiguration.jsx
@@ -0,0 +1,58 @@
+/*
+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 PropTypes from 'prop-types'
+import { isEmpty } from 'lodash'
+
+import './detailsGenerationConfiguration.scss'
+
+const DetailsGenerationConfiguration = ({ selectedItem }) => {
+ return (
+
+ {!isEmpty(selectedItem.invocation_config) && (
+ <>
+
+ {Object.entries(selectedItem.invocation_config || {}).length} modifications made to
+ the default configuration:
+
+
+ {Object.entries(selectedItem.invocation_config || {}).map(([key, value]) => {
+ return (
+
+ )
+ })}
+ >
+ )}
+ {isEmpty(selectedItem.invocation_config) &&
Default configuration is used. }
+
+ )
+}
+
+DetailsGenerationConfiguration.propTypes = {
+ selectedItem: PropTypes.object.isRequired
+}
+
+export default DetailsGenerationConfiguration
diff --git a/src/components/Details/DetailsGenerationConfiguration/detailsGenerationConfiguration.scss b/src/components/Details/DetailsGenerationConfiguration/detailsGenerationConfiguration.scss
new file mode 100644
index 0000000000..7794ba4e90
--- /dev/null
+++ b/src/components/Details/DetailsGenerationConfiguration/detailsGenerationConfiguration.scss
@@ -0,0 +1,27 @@
+@use 'igz-controls/scss/borders';
+@use 'igz-controls/scss/colors';
+
+.generation-configuration-tab {
+ margin-top: 15px;
+
+ &__counter {
+ margin-bottom: 20px;
+ }
+
+ &__table-header {
+ font-weight: 500;
+ }
+
+ &__row {
+ display: flex;
+ padding: 10px 0;
+ font-size: 15px;
+ line-height: 20px;
+ border-bottom: borders.$secondaryBorder;
+
+ &-key,
+ &-value {
+ width: 50%;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/components/Details/DetailsHeader/DetailsHeader.jsx b/src/components/Details/DetailsHeader/DetailsHeader.jsx
index 1b49000623..e65244c818 100644
--- a/src/components/Details/DetailsHeader/DetailsHeader.jsx
+++ b/src/components/Details/DetailsHeader/DetailsHeader.jsx
@@ -17,40 +17,29 @@ 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, useRef, useMemo } from 'react'
+import React, { useMemo, useCallback } from 'react'
import PropTypes from 'prop-types'
-import { Link, useLocation, useNavigate, useParams } from 'react-router-dom'
-import { isEmpty } from 'lodash'
import { useDispatch, useSelector } from 'react-redux'
+import { Link } from 'react-router-dom'
+import { isEmpty } from 'lodash'
-import { Button, Tooltip, TextTooltipTemplate, RoundedIcon } from 'igz-controls/components'
-import LoadButton from '../../../common/LoadButton/LoadButton'
import Select from '../../../common/Select/Select'
-import ActionsMenu from '../../../common/ActionsMenu/ActionsMenu'
+import { Tooltip, TextTooltipTemplate, RoundedIcon } from 'igz-controls/components'
+import { ACTIONS_MENU } from 'igz-controls/types'
import {
+ ABORTED_STATE,
DETAILS_ARTIFACTS_TAB,
- FULL_VIEW_MODE,
- JOBS_PAGE,
- VIEW_SEARCH_PARAMETER
+ DETAILS_LOGS_TAB,
+ JOBS_PAGE
} from '../../../constants'
-import { formatDatetime } from '../../../utils'
-import { TERTIARY_BUTTON } from 'igz-controls/constants'
-import { ACTIONS_MENU } from '../../../types'
-import { getViewMode } from '../../../utils/helper'
-import {
- generateUrlFromRouterPath,
- getDefaultCloseDetailsLink
-} from '../../../utils/link-helper.util'
-import { getFilteredSearchParams } from '../../../utils/filter.util'
-import { setIteration } from '../../../reducers/detailsReducer'
+import { formatDatetime } from 'igz-controls/utils/datetime.util'
+import { generateUrlFromRouterPath } from 'igz-controls/utils/common.util'
+import { getDefaultCloseDetailsLink } from '../../../utils/link-helper.util'
+import { setIteration, setRunAttempt } from '../../../reducers/detailsReducer'
+import { useDetailsHeader } from 'igz-controls/hooks/useDetailsHeader.hook'
-import Close from 'igz-controls/images/close.svg?react'
import Back from 'igz-controls/images/back-arrow.svg?react'
-import Refresh from 'igz-controls/images/refresh.svg?react'
-import EnlargeIcon from 'igz-controls/images/ml-enlarge.svg?react'
-import MinimizeIcon from 'igz-controls/images/ml-minimize.svg?react'
-import HistoryIcon from 'igz-controls/images/history.svg?react'
import InfoIcon from 'igz-controls/images/info-fill.svg?react'
const DetailsHeader = ({
@@ -60,22 +49,37 @@ const DetailsHeader = ({
cancelChanges,
getCloseDetailsLink = null,
handleCancel = null,
- handleRefresh,
+ handleRefresh = null,
handleShowWarning,
- isDetailsScreen,
isDetailsPopUp = false,
+ isDetailsScreen,
pageData,
selectedItem,
- tab,
+ tab = '',
withActionMenu = true
}) => {
+ const {
+ DetailsHeaderContainer,
+ actionButton,
+ commonDetailsStore,
+ handleActionClick,
+ handleBackClick,
+ handleCancelClick,
+ headerRef,
+ location,
+ navigate,
+ params,
+ showAllVersions,
+ viewMode,
+ withToggleViewBtn
+ } = useDetailsHeader({
+ handleCancel,
+ handleShowWarning,
+ isDetailsPopUp,
+ pageData
+ })
+
const detailsStore = useSelector(store => store.detailsStore)
- const params = useParams()
- const navigate = useNavigate()
- const viewMode = getViewMode(window.location.search)
- const { actionButton, withToggleViewBtn, showAllVersions } = pageData.details
- const headerRef = useRef()
- const location = useLocation()
const dispatch = useDispatch()
const errorMessage = useMemo(
@@ -92,253 +96,215 @@ const DetailsHeader = ({
return isDetailsPopUp ? detailsStore.detailsJobPods : detailsStore.pods
}, [detailsStore.detailsJobPods, detailsStore.pods, isDetailsPopUp])
- const {
- value: stateValue,
- label: stateLabel,
- className: stateClassName
- } = selectedItem.state || {}
+ const state = useMemo(() => {
+ const selectedItemState = selectedItem.state || {}
- const handleBackClick = useCallback(() => {
- if (detailsStore.changes.counter > 0) {
- handleShowWarning(true)
- } else {
- handleCancel()
+ return {
+ value: selectedItemState.value,
+ label: selectedItemState.label,
+ className: selectedItemState.className
}
- }, [detailsStore.changes.counter, handleCancel, handleShowWarning])
+ }, [selectedItem.state])
- const handleCancelClick = useCallback(() => {
- if (detailsStore.changes.counter === 0 || isDetailsPopUp) {
- handleCancel()
- }
- }, [detailsStore.changes.counter, handleCancel, isDetailsPopUp])
-
- return (
-
-
-
- {isDetailsScreen && !pageData.details.hideBackBtn && !isDetailsPopUp && (
-
-
-
-
-
- )}
- }
+ const renderTitle = useCallback(() => {
+ return (
+ <>
+ {isDetailsScreen && !pageData.details.hideBackBtn && !isDetailsPopUp && (
+
- {selectedItem.name || selectedItem.db_key}
-
-
-
- {/*In the Workflow page we display both Jobs and Functions items. The function contains `updated` property.
+
+
+
+
+ )}
+
}>
+ {selectedItem.name || selectedItem.db_key}
+
+ >
+ )
+ }, [
+ isDetailsScreen,
+ pageData.details.hideBackBtn,
+ isDetailsPopUp,
+ getCloseDetailsLink,
+ selectedItem.name,
+ handleBackClick,
+ selectedItem.db_key
+ ])
+
+ const renderStatus = useCallback(() => {
+ return (
+ <>
+ {/*In the Workflow page we display both Jobs and Functions items. The function contains `updated` property.
The job contains startTime property.*/}
-
-
- {Object.keys(selectedItem).length > 0 &&
- pageData.page === JOBS_PAGE &&
- !selectedItem?.updated
- ? formatDatetime(
- selectedItem?.startTime,
- stateValue === 'aborted' ? 'N/A' : 'Not yet started'
- )
- : selectedItem?.updated
- ? formatDatetime(selectedItem?.updated, 'N/A')
- : pageData.details.additionalHeaderInfo || ''}
-
- {stateValue && stateLabel && (
- }>
-
-
- )}
-
-
- {selectedItem.ui?.customError?.title && selectedItem.ui?.customError?.message && (
-
- }
- >
- {selectedItem.ui.customError.title} {selectedItem.ui.customError.message}
-
- )}
- {errorMessage && (
- }
- >
- {errorMessage}
-
- )}
- {!isEmpty(podsData?.podsPending) && (
-
- {`${podsData.podsPending.length} of ${podsData.podsList.length} pods are pending`}
-
- )}
- {detailsStore.pods.error && (
- Failed to load pods
- )}
-
- {selectedItem.ui?.infoMessage && (
-
-
-
- }
- >
- {selectedItem.ui.infoMessage}
-
-
-
+
+
+ {Object.keys(selectedItem).length > 0 &&
+ pageData.page === JOBS_PAGE &&
+ !selectedItem?.updated
+ ? formatDatetime(
+ selectedItem?.startTime,
+ state.value === ABORTED_STATE ? 'N/A' : 'Not yet started'
+ )
+ : selectedItem?.updated
+ ? formatDatetime(selectedItem?.updated, 'N/A')
+ : pageData.details.additionalHeaderInfo || ''}
+
+ {state.value && state.label && (
+ }>
+
+
)}
-
-
- {params.tab === DETAILS_ARTIFACTS_TAB && detailsStore.iteration && !isDetailsPopUp && (
- {
- dispatch(setIteration(option))
- }}
- options={detailsStore.iterationOptions}
- selectedId={detailsStore.iteration}
- />
- )}
-
-
- {detailsStore.changes.counter > 0 && !isDetailsPopUp && (
- <>
-
+
+ {selectedItem.ui?.customError?.title && selectedItem.ui?.customError?.message && (
}
>
-
+ {selectedItem.ui.customError.title} {selectedItem.ui.customError.message}
- >
- )}
- {actionButton && !actionButton.hidden && (
-
- )}
- {showAllVersions && (
-
showAllVersions()}
- tooltipText="Show all versions"
- >
-
-
- )}
- {isDetailsScreen && (
-
handleRefresh(selectedItem)}
- tooltipText="Refresh"
- >
-
-
- )}
- {withActionMenu &&
}
-
- {withToggleViewBtn && !isDetailsPopUp && (
- <>
- {viewMode !== FULL_VIEW_MODE && (
-
{
- navigate(
- `${location.pathname}${window.location.search}${window.location.search ? '&' : '?'}${VIEW_SEARCH_PARAMETER}=full`
- )
- }}
- id="full-view"
- tooltipText="Full view"
- >
-
-
- )}
- {viewMode === FULL_VIEW_MODE && (
-
{
- navigate(
- `${location.pathname}${getFilteredSearchParams(window.location.search, [VIEW_SEARCH_PARAMETER])}`
- )
- }}
- id="table-view"
- tooltipText="Table view"
- >
-
-
- )}
- >
)}
- {!pageData.details.hideBackBtn &&
- (isDetailsPopUp ? (
-
-
-
-
-
- ) : (
-
-
-
-
-
- ))}
+ {errorMessage && (
+
}
+ >
+ {errorMessage}
+
+ )}
+ {!isEmpty(podsData?.podsPending) && (
+
+ {`${podsData.podsPending.length} of ${podsData.podsList.length} pods are pending`}
+
+ )}
+ {detailsStore.pods.error && (
+
Failed to load pods
+ )}
-
-
+ {selectedItem.ui?.infoMessage && (
+
+
+
+ }
+ >
+ {selectedItem.ui.infoMessage}
+
+
+
+ )}
+ >
+ )
+ }, [
+ selectedItem,
+ pageData.page,
+ pageData.details.additionalHeaderInfo,
+ errorMessage,
+ podsData.podsPending,
+ podsData.podsList.length,
+ detailsStore.pods.error,
+ state.value,
+ state.label,
+ state.className
+ ])
+
+ const renderCustomElements = useCallback(() => {
+ switch (params.tab) {
+ case DETAILS_ARTIFACTS_TAB: {
+ return (
+ detailsStore.iteration &&
+ detailsStore.iterationOptions &&
+ !isDetailsPopUp && (
+
{
+ dispatch(setIteration(option))
+ }}
+ options={detailsStore.iterationOptions}
+ selectedId={detailsStore.iteration}
+ />
+ )
+ )
+ }
+ case DETAILS_LOGS_TAB: {
+ return (
+ detailsStore.runAttempt &&
+ !isEmpty(detailsStore.runAttemptOptions) &&
+ !isDetailsPopUp && (
+ {
+ dispatch(setRunAttempt(option))
+ }}
+ options={detailsStore.runAttemptOptions}
+ selectedId={detailsStore.runAttempt}
+ />
+ )
+ )
+ }
+ default:
+ return null
+ }
+ }, [
+ params.tab,
+ detailsStore.iteration,
+ detailsStore.iterationOptions,
+ detailsStore.runAttempt,
+ detailsStore.runAttemptOptions,
+ isDetailsPopUp,
+ dispatch
+ ])
+
+ return (
+
)
}
diff --git a/src/components/Details/DetailsPromptTemplate/ArgumentsTab.jsx b/src/components/Details/DetailsPromptTemplate/ArgumentsTab.jsx
new file mode 100644
index 0000000000..52667d356f
--- /dev/null
+++ b/src/components/Details/DetailsPromptTemplate/ArgumentsTab.jsx
@@ -0,0 +1,72 @@
+/*
+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 PropTypes from 'prop-types'
+import classNames from 'classnames'
+import { isEmpty } from 'lodash'
+
+import ContentMenu from '../../../elements/ContentMenu/ContentMenu'
+import NoData from '../../../common/NoData/NoData'
+import { Tooltip, TextTooltipTemplate } from 'igz-controls/components'
+
+const ArgumentsTab = ({
+ handleTabChange,
+ selectedArgument = {},
+ selectedItem,
+ selectedTab,
+ tabs
+}) => {
+ return (
+
+
+
+
+ {Object.values(selectedItem.prompt_legend ?? {}).map(legend => {
+ const rowClassNames = classNames(
+ 'arguments-tab__row',
+ selectedArgument.field + selectedArgument.description ===
+ legend.field + legend.description && 'arguments-tab__row_selected'
+ )
+
+ return (
+
+
+ }>
+ {legend.field}
+
+
+
{legend.description}
+
+ )
+ })}
+ {isEmpty(selectedItem.prompt_legend) &&
}
+
+ )
+}
+
+ArgumentsTab.propTypes = {
+ handleTabChange: PropTypes.func.isRequired,
+ selectedArgument: PropTypes.object,
+ selectedItem: PropTypes.object.isRequired,
+ selectedTab: PropTypes.string.isRequired,
+ tabs: PropTypes.array.isRequired
+}
+
+export default ArgumentsTab
diff --git a/src/components/Details/DetailsPromptTemplate/DetailsPromptTemplate.jsx b/src/components/Details/DetailsPromptTemplate/DetailsPromptTemplate.jsx
new file mode 100644
index 0000000000..795026e392
--- /dev/null
+++ b/src/components/Details/DetailsPromptTemplate/DetailsPromptTemplate.jsx
@@ -0,0 +1,81 @@
+/*
+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 { useCallback, useEffect, useState } from 'react'
+import PropTypes from 'prop-types'
+import { useDispatch } from 'react-redux'
+
+import PromptTab from './PromptTab'
+import ArgumentsTab from './ArgumentsTab'
+
+import { ARGUMENTS_TAB, PROMPT_TAB } from '../../../constants'
+import { removeLLMPromptTemplate } from '../../../reducers/artifactsReducer'
+
+import './detailsPromptTemplate.scss'
+
+const DetailsPromptTemplate = ({ selectedItem }) => {
+ const [selectedTab, setSelectedTab] = useState(PROMPT_TAB)
+ const [selectedArgument, setSelectedArgument] = useState({})
+ const dispatch = useDispatch()
+
+ const tabs = [
+ { id: PROMPT_TAB, label: 'Prompt' },
+ { id: ARGUMENTS_TAB, label: 'Arguments' }
+ ]
+
+ const handleTabChange = useCallback(tabName => {
+ setSelectedTab(tabName)
+ setSelectedArgument({})
+ }, [])
+
+ useEffect(() => {
+ return () => {
+ dispatch(removeLLMPromptTemplate())
+ }
+ }, [dispatch, selectedItem])
+
+ return (
+
+ {selectedTab === PROMPT_TAB ? (
+
+ ) : (
+
+ )}
+
+ )
+}
+
+DetailsPromptTemplate.propTypes = {
+ selectedItem: PropTypes.object.isRequired
+}
+
+export default DetailsPromptTemplate
diff --git a/src/components/Details/DetailsPromptTemplate/PromptTab.jsx b/src/components/Details/DetailsPromptTemplate/PromptTab.jsx
new file mode 100644
index 0000000000..c4759c2f8e
--- /dev/null
+++ b/src/components/Details/DetailsPromptTemplate/PromptTab.jsx
@@ -0,0 +1,227 @@
+/*
+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 { useEffect, useState, createContext, useCallback } from 'react'
+import PropTypes from 'prop-types'
+import { capitalize, has, isEmpty } from 'lodash'
+import { useDispatch, useSelector } from 'react-redux'
+
+import { Loader, Tooltip, TextTooltipTemplate } from 'igz-controls/components'
+import ContentMenu from '../../../elements/ContentMenu/ContentMenu'
+import SearchNavigator from '../../../common/SearchNavigator/SearchNavigator'
+import ExpandableText from '../../../common/ExpandableText/ExpandableText'
+import NoData from '../../../common/NoData/NoData'
+
+import { ARGUMENTS_TAB } from '../../../constants'
+import { fetchLLMPromptTemplate } from '../../../reducers/artifactsReducer'
+
+export const ExpandContext = createContext({})
+
+const PromptTab = ({
+ handleTabChange,
+ selectedItem,
+ selectedTab,
+ setSelectedArgument,
+ setSelectedTab,
+ tabs
+}) => {
+ const [promptTemplate, setPromptTemplate] = useState([])
+ const [searchResult, setSearchResult] = useState('')
+ const [forceExpandAll, setForceExpandAll] = useState(false)
+ const [loading, setLoading] = useState(false)
+ const [showError, setShowError] = useState(false)
+ const dispatch = useDispatch()
+ const artifactsStore = useSelector(store => store.artifactsStore)
+
+ const isPromptTemplateValid = useCallback(prompt_template => {
+ return prompt_template.every(
+ templateRow => has(templateRow, 'role') && has(templateRow, 'content')
+ )
+ }, [])
+
+ const generateJsxContent = useCallback(
+ (prompt_template, prompt_legend = {}) => {
+ const legendMap = { ...prompt_legend }
+
+ return prompt_template.map((item, idx) => {
+ const parts = item.content.split(/(\{[^}]+})/g).map((part, i) => {
+ const match = part.match(/^\{([^}]+)}$/)
+
+ if (match) {
+ const argName = match[1]
+ const currentArgument = legendMap[argName]
+
+ if (currentArgument) {
+ return (
+ }
+ textShow
+ >
+ {
+ setSelectedArgument(currentArgument)
+ setSelectedTab(ARGUMENTS_TAB)
+ }}
+ >
+ {`{${argName}}`}
+
+
+ )
+ }
+
+ return (
+
+ {`{${argName}}`}
+
+ )
+ }
+
+ return part
+ })
+
+ return (
+
+
{capitalize(item.role)}
+
+ {parts}
+
+
+ )
+ })
+ },
+ [setSelectedArgument, setSelectedTab]
+ )
+
+ useEffect(() => {
+ queueMicrotask(() => {
+ if (!isEmpty(selectedItem.prompt_template)) {
+ if (!isPromptTemplateValid(selectedItem.prompt_template)) {
+ setShowError(true)
+ } else {
+ setPromptTemplate(
+ generateJsxContent(selectedItem.prompt_template, selectedItem.prompt_legend)
+ )
+ }
+ } else if (isEmpty(artifactsStore.LLMPrompts.promptTemplate)) {
+ if (
+ !selectedItem.target_path.endsWith('.txt') &&
+ !selectedItem.target_path.endsWith('.json')
+ ) {
+ setShowError(true)
+ } else {
+ setLoading(true)
+ dispatch(
+ fetchLLMPromptTemplate({
+ project: selectedItem.project,
+ config: {
+ params: {
+ path: selectedItem.target_path
+ }
+ }
+ })
+ )
+ .unwrap()
+ .then(response => {
+ if (!isPromptTemplateValid(response.data)) {
+ setShowError(true)
+ } else {
+ setPromptTemplate(generateJsxContent(response.data, selectedItem.prompt_legend))
+ }
+ })
+ .catch(() => setShowError(true))
+ .finally(() => {
+ setLoading(false)
+ })
+ }
+ } else if (!isEmpty(artifactsStore.LLMPrompts.promptTemplate)) {
+ if (!isPromptTemplateValid(artifactsStore.LLMPrompts.promptTemplate)) {
+ return setShowError(true)
+ } else {
+ setPromptTemplate(
+ generateJsxContent(artifactsStore.LLMPrompts.promptTemplate, selectedItem.prompt_legend)
+ )
+ }
+ }
+ })
+ }, [
+ selectedItem.prompt_template,
+ selectedItem.prompt_legend,
+ setSelectedArgument,
+ setSelectedTab,
+ selectedItem.project,
+ selectedItem.target_path,
+ generateJsxContent,
+ dispatch,
+ artifactsStore.LLMPrompts.promptTemplate,
+ isPromptTemplateValid
+ ])
+
+ useEffect(() => {
+ return () => {
+ setPromptTemplate([])
+ setSearchResult('')
+ setForceExpandAll(false)
+ setShowError(false)
+ setLoading(false)
+ }
+ }, [])
+
+ return (
+
+
+
+ setForceExpandAll(Boolean(value))}
+ />
+
+
+ {showError ? (
+
+ ) : loading ? (
+
+ ) : (
+ <>
+
+
+ {searchResult || promptTemplate}
+
+ >
+ )}
+
+
+ )
+}
+
+PromptTab.propTypes = {
+ handleTabChange: PropTypes.func.isRequired,
+ selectedTab: PropTypes.string.isRequired,
+ selectedItem: PropTypes.object.isRequired,
+ setSelectedArgument: PropTypes.func.isRequired,
+ setSelectedTab: PropTypes.func.isRequired,
+ tabs: PropTypes.array.isRequired
+}
+
+export default PromptTab
diff --git a/src/components/Details/DetailsPromptTemplate/detailsPromptTemplate.scss b/src/components/Details/DetailsPromptTemplate/detailsPromptTemplate.scss
new file mode 100644
index 0000000000..0c36e7a8e7
--- /dev/null
+++ b/src/components/Details/DetailsPromptTemplate/detailsPromptTemplate.scss
@@ -0,0 +1,112 @@
+@use 'igz-controls/scss/borders';
+@use 'igz-controls/scss/colors';
+
+.prompt-template {
+ .prompt-tab {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+
+ &__header {
+ position: sticky;
+ top: 0;
+ left: 0;
+ z-index: 6;
+ display: flex;
+ align-items: center;
+ width: 100%;
+ padding-top: 20px;
+ padding-bottom: 10px;
+ background-color: white;
+ }
+
+ &__table {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ min-height: 300px;
+ margin-top: 8px;
+
+ &-header {
+ font-weight: bold;
+ }
+
+ .tooltip-wrapper {
+ display: inline;
+ }
+
+ .loader-wrapper {
+ min-height: 300px;
+ }
+ }
+
+ &__row {
+ display: flex;
+ padding: 8px 0;
+ border-bottom: borders.$dividerBorder;
+ }
+
+ &__role {
+ flex: 0 0 100px;
+ padding-right: 16px;
+ }
+
+ &__content {
+ display: flex;
+ flex: 1;
+ }
+
+ &__copy-btn {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 135px;
+ padding: 11px 18px;
+ font-size: 14px;
+ border: borders.$primaryBorder;
+ border-radius: 4px;
+ cursor: pointer;
+
+ &-wrapper {
+ margin-left: 10px;
+ }
+
+ svg {
+ width: 14px;
+ height: 14px;
+ }
+ }
+ }
+
+ .arguments-tab {
+ &__header {
+ display: flex;
+ align-items: center;
+ margin-top: 20px;
+ padding-bottom: 10px;
+ }
+
+ &__row {
+ display: flex;
+ padding: 10px 0;
+ font-size: 15px;
+ line-height: 20px;
+ border-bottom: borders.$secondaryBorder;
+
+ &_selected {
+ background-color: colors.$ghostWhite;
+ }
+
+ &-key {
+ width: 150px;
+ padding-right: 10px;
+ font-weight: bold;
+ }
+
+ &-value {
+ color: colors.$topaz;
+ }
+ }
+ }
+}
diff --git a/src/components/Details/DetailsTabsContent/DetailsTabsContent.jsx b/src/components/Details/DetailsTabsContent/DetailsTabsContent.jsx
index 84b3adad60..827c902c7f 100644
--- a/src/components/Details/DetailsTabsContent/DetailsTabsContent.jsx
+++ b/src/components/Details/DetailsTabsContent/DetailsTabsContent.jsx
@@ -22,32 +22,32 @@ import PropTypes from 'prop-types'
import { useSelector } from 'react-redux'
import { useParams } from 'react-router-dom'
-import DetailsDrillDownAlert from '../../DetailsDrillDownAlert/DetailsDrillDownAlert'
import DetailsAlerts from '../../DetailsAlerts/DetailsAlerts'
import DetailsAnalysis from '../../DetailsAnalysis/DetailsAnalysis'
import DetailsArtifacts from '../../DetailsArtifacts/DetailsArtifacts'
import DetailsCode from '../../DetailsCode/DetailsCode'
+import DetailsCollections from '../../DetailsCollections/DetailsCollections'
import DetailsDriftAnalysis from '../../DetailsDriftAnalysis/DetailsDriftAnalysis'
+import DetailsDrillDownAlert from '../../DetailsDrillDownAlert/DetailsDrillDownAlert'
import DetailsFeatureAnalysis from '../../DetailsFeaturesAnalysis/DetailsFeaturesAnalysis'
+import DetailsGenerationConfiguration from '../DetailsGenerationConfiguration/DetailsGenerationConfiguration'
import DetailsInfo from '../../DetailsInfo/DetailsInfo'
import DetailsInputs from '../../DetailsInputs/DetailsInputs'
import DetailsLogs from '../../DetailsLogs/DetailsLogs'
import DetailsMetadata from '../../DetailsMetadata/DetailsMetadata'
import DetailsMetrics from '../../DetailsMetrics/DetailsMetrics'
import DetailsPods from '../../DetailsPods/DetailsPods'
+import DetailsPromptTemplate from '../DetailsPromptTemplate/DetailsPromptTemplate'
import DetailsPreview from '../../DetailsPreview/DetailsPreview'
import DetailsRequestedFeatures from '../../DetailsRequestedFeatures/DetailsRequestedFeatures'
import DetailsResults from '../../DetailsResults/DetailsResults'
import DetailsStatistics from '../../DetailsStatistics/DetailsStatistics'
import DetailsTransformations from '../../DetailsTransformations/DetailsTransformations'
import NoData from '../../../common/NoData/NoData'
-import DetailsCollections from '../../DetailsCollections/DetailsCollections'
-
-import { isJobKindDask, JOB_STEADY_STATES } from '../../Jobs/jobs.util'
import {
- DETAILS_ALERT_APPLICATION,
DETAILS_ALERTS_TAB,
+ DETAILS_ALERT_APPLICATION,
DETAILS_ANALYSIS_TAB,
DETAILS_ARTIFACTS_TAB,
DETAILS_BUILD_LOG_TAB,
@@ -56,6 +56,7 @@ import {
DETAILS_DRIFT_ANALYSIS_TAB,
DETAILS_FEATURES_ANALYSIS_TAB,
DETAILS_FEATURES_TAB,
+ DETAILS_INVOCATION_CONFIGURATION_TAB,
DETAILS_INPUTS_TAB,
DETAILS_LOGS_TAB,
DETAILS_METADATA_TAB,
@@ -63,12 +64,14 @@ import {
DETAILS_OVERVIEW_TAB,
DETAILS_PODS_TAB,
DETAILS_PREVIEW_TAB,
+ DETAILS_PROMPT_TEMPLATE_TAB,
DETAILS_REQUESTED_FEATURES_TAB,
DETAILS_RESULTS_TAB,
DETAILS_RETURNED_FEATURES_TAB,
DETAILS_STATISTICS_TAB,
DETAILS_TRANSFORMATIONS_TAB
} from '../../../constants'
+import { isJobKindDask, JOB_STEADY_STATES } from '../../Jobs/jobs.util'
const DetailsTabsContent = ({
applyChangesRef,
@@ -80,13 +83,14 @@ const DetailsTabsContent = ({
selectedItem
}) => {
const detailsStore = useSelector(store => store.detailsStore)
+ const commonDetailsStore = useSelector(store => store.commonDetailsStore)
const params = useParams()
switch (isDetailsPopUp ? detailsPopUpSelectedTab : params.tab) {
case DETAILS_OVERVIEW_TAB:
return (
case DETAILS_METRICS_TAB:
- return
+ return
case DETAILS_ALERTS_TAB:
return // todo [Alerts] in ML-9205 remove the key when alerts are refactored and the issue is fixed by refactoring
case DETAILS_PREVIEW_TAB:
@@ -217,7 +221,7 @@ const DetailsTabsContent = ({
case DETAILS_REQUESTED_FEATURES_TAB:
return (
+ case DETAILS_PROMPT_TEMPLATE_TAB:
+ return
+ case DETAILS_INVOCATION_CONFIGURATION_TAB:
+ return
default:
return null
}
diff --git a/src/components/Details/details.scss b/src/components/Details/details.scss
deleted file mode 100644
index 6bff24e4e6..0000000000
--- a/src/components/Details/details.scss
+++ /dev/null
@@ -1,376 +0,0 @@
-@use 'igz-controls/scss/colors';
-@use 'igz-controls/scss/mixins';
-@use 'igz-controls/scss/borders';
-@use 'igz-controls/scss/shadows';
-
-.table {
- &__item {
- position: absolute;
- top: 0;
- right: 0;
- z-index: 2;
- display: flex;
- flex-direction: column;
- width: calc(100% - 250px);
- height: 100%;
- padding: 20px 30px;
- overflow-y: auto;
- color: colors.$mulledWine;
- background-color: colors.$white;
- border: 1px solid colors.$magnoliaWhite;
- border-radius: 0 4px 4px 0;
-
- &_big {
- width: 100%;
- }
-
- &-popup {
- border-radius: 8px;
- }
-
- .error {
- margin-bottom: 10px;
- }
-
- .item {
- &-header {
- display: flex;
- flex-wrap: wrap;
- align-items: flex-start;
- justify-content: flex-end;
- margin: 0 0 1em 0;
-
- &-wrapper {
- position: sticky;
- top: 0;
- left: 0;
- z-index: 6;
- background-color: inherit;
- }
-
- &__title {
- display: flex;
- align-items: center;
- }
-
- &__status {
- color: colors.$topaz;
- line-height: 25px;
-
- &-row {
- display: flex;
- flex: 1 0 100%;
- flex-flow: row wrap;
- align-items: center;
- margin-bottom: 5px;
-
- .info-banner {
- display: flex;
- gap: 8px;
- align-items: center;
- max-width: 100%;
- height: 36px;
- padding: 4px 8px;
- background-color: colors.$magnoliaWhite;
- border-radius: 4px;
- }
- }
- }
-
- &__back-btn {
- margin-right: 5px;
-
- & > * {
- margin: 0;
- }
- }
-
- &__navigation-buttons {
- display: flex;
- }
-
- &__pods {
- &-error {
- color: colors.$spunPearl;
- }
- }
-
- &__data {
- flex: 1 0 0;
- min-width: 250px;
- margin-right: auto;
-
- h3 {
- margin: 0 0 5px 0;
- font-size: 24px;
- line-height: 28px;
- }
-
- .state {
- display: inline-block;
- min-width: 18px;
-
- i {
- margin: 0 10px 0 5px;
- }
- }
-
- .error-container {
- flex: 1;
- margin-left: 0;
-
- &:not(:last-child) {
- margin-right: 1em;
- padding-right: 1em;
- border-right: borders.$secondaryBorder;
- }
- }
- }
-
- &__custom-elements {
- z-index: 7;
- display: flex;
- align-items: center;
- margin-right: 10px;
- }
-
- &__buttons {
- z-index: 7;
- display: flex;
- align-items: center;
-
- a {
- display: inline-block;
- margin: 21px 21px 21px 10px;
-
- &.details-close-btn {
- margin: 0;
- }
- }
-
- .btn {
- margin: 0 0.2rem;
- }
-
- .table-actions-container {
- display: block;
- }
-
- .actions-menu {
- &__container {
- display: block;
-
- .btn {
- padding: 0;
- border: none;
-
- &:hover {
- background-color: transparent;
- }
-
- :first-child {
- margin-right: 0;
- }
- }
- }
- }
-
- .details-date-picker {
- margin-right: 12px;
- }
- }
- }
-
- &__content {
- border-top: borders.$secondaryBorder;
- }
- }
-
- &_inputs {
- width: 100%;
-
- @include mixins.resetSpaces;
-
- &_item {
- display: flex;
- flex-direction: row;
- align-items: center;
- padding: 27px 24px;
- border-bottom: borders.$secondaryBorder;
-
- div {
- min-width: 237px;
- padding: 0 5px;
- }
- }
- }
-
- &-logs {
- position: relative;
- display: flex;
- flex: 1;
- width: 100%;
- min-height: 200px;
- padding: 10px 0;
- color: colors.$white;
- font-family: 'Source Code Pro', 'Courier New', monospace;
- white-space: pre-wrap;
- background-color: colors.$black;
-
- &:not(:last-child) {
- margin-bottom: 10px;
- }
-
- &-container {
- display: flex;
- flex-direction: column;
- height: 100%;
- }
-
- &-content {
- width: 100%;
- height: 100%;
- padding: 0 10px;
- overflow-y: scroll;
-
- /* Define the thumb style */
- &::-webkit-scrollbar-thumb {
- background: colors.$doveGray;
- border-radius: 5px;
- }
- }
-
- &-panel {
- padding: 0 10px;
-
- .logs-refresh {
- .btn {
- min-width: 40px;
- padding: 0;
- border-radius: 50%;
-
- & > * {
- margin: 0;
- }
- }
- }
-
- .logs-loader {
- width: 40px;
- height: 40px;
- }
- }
- }
-
- &_artifacts {
- display: flex;
- flex: 1;
- flex-direction: column;
-
- &_wrapper {
- width: 100%;
- }
- }
-
- .details-item {
- &__buttons-block {
- display: flex;
- }
-
- &__apply-btn-wrapper {
- width: 30px;
- }
-
- &__apply-btn {
- display: block;
- min-width: 30px;
- height: 30px;
- margin: 0;
- padding: 0;
- }
- }
-
- .preview_container {
- display: flex;
- flex-flow: column;
- width: 100%;
- height: 100%;
-
- &__header {
- display: flex;
- justify-content: space-between;
-
- &-text {
- align-self: center;
- }
- }
-
- .inputs_container {
- width: 100%;
- }
-
- .accordion {
- &__container {
- &.open {
- .accordion__body {
- overflow: initial;
- }
- }
- }
- }
-
- .preview {
- &_popout {
- margin: 0;
- padding: 0;
- background: colors.$white;
- border: none;
- outline: none;
- cursor: pointer;
- }
- }
-
- .json {
- padding: 0 15px;
- }
-
- .preview-table {
- display: flex;
- flex: 1;
- flex-direction: column;
- overflow: auto;
-
- .table-header {
- top: 0;
- }
- }
- }
-
- &_code {
- flex: 1;
- overflow: auto;
-
- h3 {
- text-align: center;
- vertical-align: middle;
- }
-
- pre {
- padding: 8px 16px;
- white-space: pre-wrap;
- word-break: break-word;
- }
- }
-
- &.pop-up-dialog-opened {
- overflow-y: hidden;
- }
-
- .chips {
- &-wrapper {
- flex-wrap: wrap;
- width: 100%;
- }
- }
- }
-}
diff --git a/src/components/Details/details.util.js b/src/components/Details/details.util.js
index 762e6c05ed..df74df2893 100644
--- a/src/components/Details/details.util.js
+++ b/src/components/Details/details.util.js
@@ -30,10 +30,10 @@ import {
} from 'igz-controls/utils/validation.util'
import {
+ ABORTED_STATE,
DATASETS_PAGE,
DOCUMENTS_PAGE,
FEATURE_SETS_TAB,
- FEATURE_STORE_PAGE,
FEATURE_VECTORS_TAB,
FILES_PAGE,
FUNCTION_TYPE_APPLICATION,
@@ -42,16 +42,16 @@ import {
MODELS_TAB,
TAG_LATEST
} from '../../constants'
-import { formatDatetime, generateLinkPath, parseUri } from '../../utils'
-import { isArtifactTagUnique } from '../../utils/artifacts.util'
+import { generateLinkPath, parseUri } from '../../utils'
import { getFunctionImage } from '../FunctionsPage/functions.util'
import { openPopUp } from 'igz-controls/utils/common.util'
+import { formatDatetime } from 'igz-controls/utils/datetime.util'
import {
setChangesCounter,
setChangesData,
setFiltersWasHandled,
showWarning
-} from '../../reducers/detailsReducer'
+} from 'igz-controls/reducers/commonDetailsReducer'
export const generateArtifactsContent = (
detailsType,
@@ -123,6 +123,14 @@ export const generateArtifactsContent = (
db_key: {
value: selectedItem.db_key
},
+ model_artifact: {
+ value: !isEmpty(selectedItem?.parent_uri) ? parseUri(selectedItem.parent_uri).key : '',
+ shouldPopUp: !isEmpty(selectedItem?.parent_uri),
+ handleClick: () =>
+ openPopUp(ArtifactPopUp, {
+ artifactData: parseUri(selectedItem?.parent_uri)
+ })
+ },
tag: {
value: selectedItem.tag ?? '',
editModeEnabled: true,
@@ -133,12 +141,6 @@ export const generateArtifactsContent = (
validationRules: {
name: 'common.tag',
additionalRules: [
- {
- name: 'tagUniqueness',
- label: 'Tag name must be unique',
- pattern: isArtifactTagUnique(projectName, detailsType, selectedItem),
- async: true
- },
{
name: 'latest',
label: 'Tag name "latest" is reserved',
@@ -147,10 +149,10 @@ export const generateArtifactsContent = (
]
}
},
- handleDiscardChanges: (formState, detailsStore) => {
+ handleDiscardChanges: (formState, commonDetailsStore) => {
formState.form.change(
'tag',
- detailsStore.changes.data.tag?.currentFieldValue ?? formState.initialValues.tag
+ commonDetailsStore.changes.data.tag?.currentFieldValue ?? formState.initialValues.tag
)
}
},
@@ -159,9 +161,7 @@ export const generateArtifactsContent = (
},
kind: {
value:
- detailsType !== FEATURE_STORE_PAGE &&
- detailsType !== FILES_PAGE &&
- detailsType !== DATASETS_PAGE
+ detailsType !== FILES_PAGE && detailsType !== DATASETS_PAGE
? selectedItem.kind || ' '
: null
},
@@ -225,6 +225,9 @@ export const generateArtifactsContent = (
),
value: getValidationRules('artifact.labels.value')
}
+ },
+ description: {
+ value: selectedItem.description
}
}
}
@@ -299,7 +302,7 @@ export const generateJobsContent = selectedItem => {
startTime: {
value: formatDatetime(
selectedItem.startTime,
- selectedItem.state?.value === 'aborted' ? 'N/A' : 'Not yet started'
+ selectedItem.state?.value === ABORTED_STATE ? 'N/A' : 'Not yet started'
)
},
updated: {
@@ -351,6 +354,12 @@ export const generateJobsContent = selectedItem => {
: selectedItem.iterationStats?.length
? selectedItem.iterationStats.length - 1
: 'N/A'
+ },
+ retryCountWithInitialAttempt: {
+ value: selectedItem.retryCountWithInitialAttempt
+ },
+ maxRetriesWithInitialAttempt: {
+ value: selectedItem.maxRetriesWithInitialAttempt
}
}
}
@@ -407,10 +416,10 @@ export const generateFeatureSetsOverviewContent = (selectedItem, isDetailsPopUp)
fieldData: {
name: 'description'
},
- handleDiscardChanges: (formState, detailsStore) => {
+ handleDiscardChanges: (formState, commonDetailsStore) => {
formState.form.change(
'description',
- detailsStore.changes.data.description?.currentFieldValue ??
+ commonDetailsStore.changes.data.description?.currentFieldValue ??
formState.initialValues.description
)
}
@@ -462,10 +471,10 @@ export const generateFeatureVectorsOverviewContent = (selectedItem, isDetailsPop
fieldData: {
name: 'description'
},
- handleDiscardChanges: (formState, detailsStore) => {
+ handleDiscardChanges: (formState, commonDetailsStore) => {
formState.form.change(
'description',
- detailsStore.changes.data.description?.currentFieldValue ??
+ commonDetailsStore.changes.data.description?.currentFieldValue ??
formState.initialValues.description
)
}
diff --git a/src/components/DetailsAlerts/DetailsAlerts.jsx b/src/components/DetailsAlerts/DetailsAlerts.jsx
index da56359a67..0d185b9924 100644
--- a/src/components/DetailsAlerts/DetailsAlerts.jsx
+++ b/src/components/DetailsAlerts/DetailsAlerts.jsx
@@ -50,7 +50,11 @@ const DetailsAlerts = () => {
parseAlertsQueryParamsCallback
)
- const { alerts, requestErrorMessage } = useRefreshAlerts(alertsFilters)
+ const filters = useMemo(() => {
+ return {...alertsFilters, 'entity-type': 'model-endpoint-result'}
+ }, [alertsFilters])
+
+ const { alerts, requestErrorMessage } = useRefreshAlerts(filters)
const tableContent = useMemo(() => {
if (alerts) {
diff --git a/src/components/DetailsAnalysis/DetailsAnalysis.jsx b/src/components/DetailsAnalysis/DetailsAnalysis.jsx
index 5b679f536a..3db8deaffd 100644
--- a/src/components/DetailsAnalysis/DetailsAnalysis.jsx
+++ b/src/components/DetailsAnalysis/DetailsAnalysis.jsx
@@ -20,12 +20,14 @@ such restriction.
import React, { useCallback, useEffect, useState, useRef } from 'react'
import PropTypes from 'prop-types'
import { useParams } from 'react-router-dom'
-import { useSelector } from 'react-redux'
+import { useDispatch, useSelector } from 'react-redux'
+import { isArray, isEmpty, isObject } from 'lodash'
import ArtifactsPreview from '../ArtifactsPreview/ArtifactsPreview'
import { REQUEST_CANCELED } from '../../constants'
import { fetchArtifactPreviewFromPath } from '../../utils/getArtifactPreview'
+import { showErrorNotification } from 'igz-controls/utils/notification.util'
const DetailsAnalysis = ({ artifact }) => {
const [preview, setPreview] = useState([])
@@ -34,6 +36,7 @@ const DetailsAnalysis = ({ artifact }) => {
const frontendSpec = useSelector(store => store.appStore.frontendSpec)
const previewIsFetchedRef = useRef(false)
const abortControllersListRef = useRef([])
+ const dispatch = useDispatch()
const fetchPreviewFromAnalysis = useCallback(() => {
Object.entries(artifact.analysis).forEach(([name, path]) => {
@@ -56,13 +59,22 @@ const DetailsAnalysis = ({ artifact }) => {
useEffect(() => {
if (artifact.analysis && preview.length === 0 && !previewIsFetchedRef.current && frontendSpec) {
- fetchPreviewFromAnalysis()
+ if (isObject(artifact.analysis) && !isArray(artifact.analysis)) {
+ fetchPreviewFromAnalysis()
+ } else {
+ showErrorNotification(dispatch, '', '', 'The analysis type is malformed. Expected dict')
+ queueMicrotask(() => {
+ setNoData(true)
+ })
+ }
previewIsFetchedRef.current = true
- } else if (!artifact.analysis) {
- setNoData(true)
+ } else if (!artifact.analysis || isEmpty(artifact.analysis)) {
+ queueMicrotask(() => {
+ setNoData(true)
+ })
}
- }, [artifact.analysis, fetchPreviewFromAnalysis, preview.length, frontendSpec])
+ }, [artifact.analysis, fetchPreviewFromAnalysis, preview.length, frontendSpec, dispatch])
useEffect(() => {
const abortControllersList = abortControllersListRef.current
diff --git a/src/components/DetailsArtifacts/DetailsArtifacts.jsx b/src/components/DetailsArtifacts/DetailsArtifacts.jsx
index f123c239fd..553b216964 100644
--- a/src/components/DetailsArtifacts/DetailsArtifacts.jsx
+++ b/src/components/DetailsArtifacts/DetailsArtifacts.jsx
@@ -24,9 +24,8 @@ import PropTypes from 'prop-types'
import classnames from 'classnames'
import ArtifactsPreviewController from '../ArtifactsPreview/ArtifactsPreviewController'
-import Loader from '../../common/Loader/Loader'
import NoData from '../../common/NoData/NoData'
-import { TextTooltipTemplate, Tooltip, Tip } from 'igz-controls/components'
+import { TextTooltipTemplate, Tooltip, Tip, Loader } from 'igz-controls/components'
import {
generateArtifactsPreviewContent,
@@ -37,7 +36,7 @@ import { ALLOW_SORT_BY, DEFAULT_SORT_BY, EXCLUDE_SORT_BY } from 'igz-controls/ty
import { fetchArtifacts } from '../../reducers/artifactsReducer'
import { fetchJob } from '../../reducers/jobReducer'
import { generateArtifactIdentifiers } from '../Details/details.util'
-import { getChipLabelAndValue } from '../../utils/getChipLabelAndValue'
+import { getChipLabelAndValue } from 'igz-controls/utils/chips.util'
import { setIteration, setIterationOption } from '../../reducers/detailsReducer'
import { useSortTable } from '../../hooks/useSortTable.hook'
@@ -280,7 +279,7 @@ DetailsArtifacts.propTypes = {
excludeSortBy: EXCLUDE_SORT_BY,
isDetailsPopUp: PropTypes.bool,
iteration: PropTypes.string.isRequired,
- selectedItem: PropTypes.object.isRequired,
+ selectedItem: PropTypes.object.isRequired
}
export default DetailsArtifacts
diff --git a/src/components/DetailsArtifacts/detailsArtifacts.util.jsx b/src/components/DetailsArtifacts/detailsArtifacts.util.jsx
index ace8668040..b90ab6a625 100644
--- a/src/components/DetailsArtifacts/detailsArtifacts.util.jsx
+++ b/src/components/DetailsArtifacts/detailsArtifacts.util.jsx
@@ -19,13 +19,13 @@ such restriction.
*/
import prettyBytes from 'pretty-bytes'
-import CopyToClipboard from '../../common/CopyToClipboard/CopyToClipboard'
+import { RoundedIcon, TextTooltipTemplate, Tooltip, CopyToClipboard } from 'igz-controls/components'
import Download from '../../common/Download/Download'
-import { RoundedIcon, TextTooltipTemplate, Tooltip } from 'igz-controls/components'
import ArtifactPopUp from '../../elements/DetailsPopUp/ArtifactPopUp/ArtifactPopUp'
import { openPopUp } from 'igz-controls/utils/common.util'
-import { formatDatetime, parseKeyValues } from '../../utils'
+import { formatDatetime } from 'igz-controls/utils/datetime.util'
+import { parseKeyValues } from '../../utils'
import { parseArtifacts } from '../../utils/parseArtifacts'
import DetailsIcon from 'igz-controls/images/view-details.svg?react'
diff --git a/src/components/DetailsCode/DetailsCode.jsx b/src/components/DetailsCode/DetailsCode.jsx
index b97ebbd6fd..7e873556fc 100644
--- a/src/components/DetailsCode/DetailsCode.jsx
+++ b/src/components/DetailsCode/DetailsCode.jsx
@@ -32,7 +32,9 @@ const DetailsCode = ({ code = '' }) => {
}, [code])
useEffect(() => {
- decodeCode()
+ queueMicrotask(() => {
+ decodeCode()
+ })
}, [decodeCode])
const html = Prism.highlight(decoded, Prism.languages.py, 'py')
@@ -53,7 +55,7 @@ const DetailsCode = ({ code = '' }) => {
}
DetailsCode.propTypes = {
- code: PropTypes.string,
+ code: PropTypes.string
}
export default DetailsCode
diff --git a/src/components/DetailsDriftAnalysis/detailsDriftAnalysis.util.js b/src/components/DetailsDriftAnalysis/detailsDriftAnalysis.util.js
index 58c52a9e2f..8f2f8a7b03 100644
--- a/src/components/DetailsDriftAnalysis/detailsDriftAnalysis.util.js
+++ b/src/components/DetailsDriftAnalysis/detailsDriftAnalysis.util.js
@@ -17,7 +17,7 @@ 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 { roundFloats } from '../../utils/roundFloats'
+import { roundFloats } from 'igz-controls/utils/common.util'
export const generateDriftAnalysis = measures => {
measures ??= {}
diff --git a/src/components/DetailsDrillDownAlert/DetailsAlertsMetrics.jsx b/src/components/DetailsDrillDownAlert/DetailsAlertsMetrics.jsx
index dfa9eb57aa..5f63dd0bc3 100644
--- a/src/components/DetailsDrillDownAlert/DetailsAlertsMetrics.jsx
+++ b/src/components/DetailsDrillDownAlert/DetailsAlertsMetrics.jsx
@@ -22,10 +22,10 @@ import PropTypes from 'prop-types'
import { useDispatch, useSelector } from 'react-redux'
import { isEmpty } from 'lodash'
-import ApplicationMetricCard from '../DetailsMetrics/MetricsCards/ApplicationMetricCard'
+import ApplicationMetricCard from '../DetailsMetrics/MetricsCards/ApplicationMetricCard/ApplicationMetricCard'
import DatePicker from '../../common/DatePicker/DatePicker'
import NoData from '../../common/NoData/NoData'
-import NoMetricData from '../DetailsMetrics/MetricsCards/NoMetricData'
+import NoMetricData from '../DetailsMetrics/MetricsCards/NoMetricData/NoMetricData'
import StatsCard from '../../common/StatsCard/StatsCard'
import { REQUEST_CANCELED } from '../../constants'
@@ -112,10 +112,10 @@ const DetailsAlertsMetrics = ({ selectedItem, filters, isAlertsPage = true }) =>
if (!isAlertsPage) {
if (filters?.dates?.initialSelectedOptionId === CUSTOM_RANGE_DATE_OPTION) {
- params.start = filters?.dates.value[0].getTime()
- params.end = filters?.dates.value[1].getTime()
+ params.start = filters?.dates?.value[0]?.getTime?.()
+ params.end = filters?.dates?.value[1]?.getTime?.()
} else {
- params.start = filters?.dates.value[0].getTime()
+ params.start = filters?.dates?.value[0]?.getTime?.()
params.end = Date.now()
}
}
@@ -139,22 +139,6 @@ const DetailsAlertsMetrics = ({ selectedItem, filters, isAlertsPage = true }) =>
return (
- {isAlertsPage && detailsStore.loadingCounter === 0 && (
-
-
-
- )}
-
{generatedMetrics.length === 0 ? (
!detailsStore.loadingCounter ? (
requestErrorMessage ? (
@@ -167,10 +151,25 @@ const DetailsAlertsMetrics = ({ selectedItem, filters, isAlertsPage = true }) =>
)
) : null
) : (
-
+
{generatedMetrics.map(([applicationName, applicationMetrics]) => (
- {isAlertsPage && {applicationName}
}
+ {isAlertsPage && detailsStore.loadingCounter === 0 && (
+
+ {applicationName}
+
+
+ )}
{applicationMetrics.map(metric =>
!metric.data || isEmpty(metric.points) ? (
diff --git a/src/components/DetailsDrillDownAlert/DetailsDrillDownAlert.jsx b/src/components/DetailsDrillDownAlert/DetailsDrillDownAlert.jsx
index f797a7c9f1..c7f3ac3a32 100644
--- a/src/components/DetailsDrillDownAlert/DetailsDrillDownAlert.jsx
+++ b/src/components/DetailsDrillDownAlert/DetailsDrillDownAlert.jsx
@@ -34,7 +34,7 @@ import EnlargeIcon from 'igz-controls/images/ml-enlarge.svg?react'
import '../DetailsInfo/detailsInfo.scss'
const DetailsDrillDownAlert = React.forwardRef(
- ({ detailsStore, formState, isDetailsPopUp, pageData, selectedItem }, applyChangesRef) => {
+ ({ commonDetailsStore, formState, isDetailsPopUp, pageData, selectedItem }, applyChangesRef) => {
const openAlertsLogsModal = useCallback(() => {
openPopUp(AlertLogsModal, { selectedItem, pageData })
}, [pageData, selectedItem])
@@ -42,7 +42,7 @@ const DetailsDrillDownAlert = React.forwardRef(
return (
<>
{
const table = generateFeaturesAnalysis(selectedItem)
const amethystColor = useMemo(() => getScssVariableValue('--amethystColor'), [])
- const chartConfig = useMemo(getHistogramChartConfig, [])
+ const chartConfig = useMemo(() => getHistogramChartConfig, [])
return (
diff --git a/src/components/DetailsFeaturesAnalysis/detailsFeaturesAnalysis.util.jsx b/src/components/DetailsFeaturesAnalysis/detailsFeaturesAnalysis.util.jsx
index 535be4ef25..de49bcbf2a 100644
--- a/src/components/DetailsFeaturesAnalysis/detailsFeaturesAnalysis.util.jsx
+++ b/src/components/DetailsFeaturesAnalysis/detailsFeaturesAnalysis.util.jsx
@@ -17,7 +17,7 @@ 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 { roundFloats } from '../../utils/roundFloats'
+import { roundFloats } from 'igz-controls/utils/common.util'
import { Tooltip, TextTooltipTemplate } from 'igz-controls/components'
import LabelColumn from 'igz-controls/images/ic_target-with-dart.svg?react'
diff --git a/src/components/DetailsInfo/DetailsInfo.jsx b/src/components/DetailsInfo/DetailsInfo.jsx
index 8a69b35f36..b02974e666 100644
--- a/src/components/DetailsInfo/DetailsInfo.jsx
+++ b/src/components/DetailsInfo/DetailsInfo.jsx
@@ -35,10 +35,10 @@ import {
import { detailsInfoActions, detailsInfoReducer, initialState } from './detailsInfoReducer'
import { handleFinishEdit } from '../Details/details.util'
import { isEveryObjectValueEmpty } from '../../utils/isEveryObjectValueEmpty'
-import { setEditMode } from '../../reducers/detailsReducer'
+import { setEditMode } from 'igz-controls/reducers/commonDetailsReducer'
const DetailsInfo = React.forwardRef(
- ({ detailsStore, formState, isDetailsPopUp, pageData, selectedItem }, applyChangesRef) => {
+ ({ commonDetailsStore, formState, isDetailsPopUp, pageData, selectedItem }, applyChangesRef) => {
const location = useLocation()
const [detailsInfoState, detailsInfoDispatch] = useReducer(detailsInfoReducer, initialState)
const params = useParams()
@@ -128,7 +128,7 @@ const DetailsInfo = React.forwardRef(
dispatch(setEditMode(false))
return handleFinishEdit(
- detailsStore.changes,
+ commonDetailsStore.changes,
detailsInfoActions,
detailsInfoDispatch,
currentField,
@@ -136,7 +136,7 @@ const DetailsInfo = React.forwardRef(
dispatch
)
},
- [detailsStore.changes, dispatch, formState]
+ [commonDetailsStore.changes, dispatch, formState]
)
return (
@@ -144,7 +144,7 @@ const DetailsInfo = React.forwardRef(
additionalInfo={{ alerts, configuration, document_loader, drift, producer, sources }}
detailsInfoDispatch={detailsInfoDispatch}
detailsInfoState={detailsInfoState}
- detailsStore={detailsStore}
+ commonDetailsStore={commonDetailsStore}
formState={formState}
handleDiscardChanges={handleDiscardChanges}
handleFinishEdit={finishEdit}
@@ -162,7 +162,7 @@ const DetailsInfo = React.forwardRef(
DetailsInfo.displayName = 'DetailsInfo'
DetailsInfo.propTypes = {
- detailsStore: PropTypes.object.isRequired,
+ commonDetailsStore: PropTypes.object.isRequired,
formState: PropTypes.object.isRequired,
isDetailsPopUp: PropTypes.bool.isRequired,
pageData: PropTypes.object.isRequired,
diff --git a/src/components/DetailsInfo/DetailsInfoView.jsx b/src/components/DetailsInfo/DetailsInfoView.jsx
index 50075014d9..205b775a84 100644
--- a/src/components/DetailsInfo/DetailsInfoView.jsx
+++ b/src/components/DetailsInfo/DetailsInfoView.jsx
@@ -37,10 +37,11 @@ import {
FILES_PAGE,
FUNCTIONS_PAGE,
JOBS_PAGE,
+ LLM_PROMPTS_PAGE,
MODELS_PAGE
} from '../../constants'
import { parseKeyValues } from '../../utils'
-import { getChipOptions } from '../../utils/getChipOptions'
+import { getChipOptions } from 'igz-controls/utils/chips.util'
import RightArrow from 'igz-controls/images/ic_arrow-right.svg?react'
@@ -57,7 +58,7 @@ const DetailsInfoView = React.forwardRef(
},
detailsInfoDispatch,
detailsInfoState,
- detailsStore,
+ commonDetailsStore,
formState,
handleDiscardChanges,
handleFinishEdit,
@@ -70,8 +71,8 @@ const DetailsInfoView = React.forwardRef(
ref
) => {
const infoContent = useMemo(
- () => (isDetailsPopUp ? detailsStore.detailsPopUpInfoContent : detailsStore.infoContent),
- [detailsStore.infoContent, detailsStore.detailsPopUpInfoContent, isDetailsPopUp]
+ () => (isDetailsPopUp ? commonDetailsStore.detailsPopUpInfoContent : commonDetailsStore.infoContent),
+ [commonDetailsStore.infoContent, commonDetailsStore.detailsPopUpInfoContent, isDetailsPopUp]
)
const wrapperClassNames = classnames(
!isEveryObjectValueEmpty(additionalInfo)
@@ -90,7 +91,8 @@ const DetailsInfoView = React.forwardRef(
pageData.page === FUNCTIONS_PAGE ||
pageData.page === MODELS_PAGE ||
pageData.page === FEATURE_STORE_PAGE ||
- pageData.page === DOCUMENTS_PAGE) &&
+ pageData.page === DOCUMENTS_PAGE ||
+ pageData.page === LLM_PROMPTS_PAGE) &&
params.pageTab !== FEATURE_SETS_TAB &&
General }
{pageData.details.infoHeaders?.map(header => {
@@ -139,7 +141,8 @@ const DetailsInfoView = React.forwardRef(
pageData.page === FILES_PAGE ||
pageData.page === MODELS_PAGE ||
pageData.page === FEATURE_STORE_PAGE ||
- pageData.page === DOCUMENTS_PAGE
+ pageData.page === DOCUMENTS_PAGE ||
+ pageData.page === LLM_PROMPTS_PAGE
) {
if (header.id === 'labels') {
chipsData.validationRules = infoContent[header.id]?.validationRules
@@ -157,8 +160,8 @@ const DetailsInfoView = React.forwardRef(
chipsData.delimiter =
}
- info = !isNil(detailsStore.changes.data[header.id])
- ? detailsStore.changes.data[header.id].currentFieldValue
+ info = !isNil(commonDetailsStore.changes.data[header.id])
+ ? commonDetailsStore.changes.data[header.id].currentFieldValue
: selectedItem && infoContent[header.id]?.value
} else if (pageData.page === FUNCTIONS_PAGE) {
info =
@@ -216,7 +219,7 @@ const DetailsInfoView = React.forwardRef(
)}
{!isEveryObjectValueEmpty(additionalInfo.drift) && (
<>
- Histogram Data Drift Application
+ Histogram data drift application
>
)}
@@ -266,7 +269,7 @@ DetailsInfoView.propTypes = {
}),
detailsInfoDispatch: PropTypes.func.isRequired,
detailsInfoState: PropTypes.object.isRequired,
- detailsStore: PropTypes.object.isRequired,
+ commonDetailsStore: PropTypes.object.isRequired,
formState: PropTypes.object,
handleDiscardChanges: PropTypes.func.isRequired,
handleFinishEdit: PropTypes.func.isRequired,
diff --git a/src/components/DetailsInfo/detailsInfo.scss b/src/components/DetailsInfo/detailsInfo.scss
index fc4741f8e8..cfc2613c68 100644
--- a/src/components/DetailsInfo/detailsInfo.scss
+++ b/src/components/DetailsInfo/detailsInfo.scss
@@ -26,13 +26,13 @@ $itemInfoWithoutPadding: item-info-without-padding;
&__header {
margin: 0;
- padding: 10px 0 7.5px 0;
+ padding: 10px 0 7.5px;
font-size: 18px;
}
&__details {
margin: 0 0 21px;
- padding: 0 0 0 0;
+ padding: 0;
list-style-type: none;
&-wrapper {
diff --git a/src/components/DetailsInfo/detailsInfo.util.jsx b/src/components/DetailsInfo/detailsInfo.util.jsx
index 3c1c749fab..042835dd41 100644
--- a/src/components/DetailsInfo/detailsInfo.util.jsx
+++ b/src/components/DetailsInfo/detailsInfo.util.jsx
@@ -34,14 +34,15 @@ import {
MODEL_ENDPOINTS_TAB,
MLRUN_STORAGE_INPUT_PATH_SCHEME
} from '../../constants'
-import { formatDatetime, parseKeyValues, parseUri } from '../../utils'
+import { parseKeyValues, parseUri } from '../../utils'
import { getTriggerCriticalTimePeriod } from '../../utils/createAlertsContent'
-import { getChipOptions } from '../../utils/getChipOptions'
+import { getChipOptions } from 'igz-controls/utils/chips.util'
import { getLimitsGpuType } from '../../elements/FormResourcesUnits/formResourcesUnits.util'
import { isEveryObjectValueEmpty } from '../../utils/isEveryObjectValueEmpty'
-import { roundFloats } from '../../utils/roundFloats'
+import { roundFloats } from 'igz-controls/utils/common.util'
import { generateFunctionPriorityLabel } from '../../utils/generateFunctionPriorityLabel'
import { openPopUp } from 'igz-controls/utils/common.util'
+import { formatDatetime } from 'igz-controls/utils/datetime.util'
export const generateTriggerInfoContent = criteria => {
if (criteria) {
@@ -90,7 +91,7 @@ const generateFunctionConfigurationContent = selectedFunction => {
return [
{
id: 'runOnSpotNodes',
- label: 'Run on Spot nodes',
+ label: 'Run on spot nodes',
value: capitalize(selectedFunction.preemption_mode)
},
{
@@ -135,7 +136,7 @@ const generateFunctionConfigurationContent = selectedFunction => {
},
{
id: 'nodeSelectors',
- label: 'Node Selectors',
+ label: 'Node selectors',
value: selectedFunction.node_selector,
chipVariant: 'results'
}
@@ -151,7 +152,7 @@ const generateModelEndpointDriftContent = modelEndpoint => {
},
{
id: 'hellinger_mean',
- label: 'Mean Hellinger',
+ label: 'Mean hellinger',
value: roundFloats(modelEndpoint.status?.drift_measures?.hellinger_mean, 2) ?? '-'
},
{
@@ -161,7 +162,7 @@ const generateModelEndpointDriftContent = modelEndpoint => {
},
{
id: 'drift_value',
- label: 'Drift Actual Value',
+ label: 'Drift actual value',
value:
isNumber(modelEndpoint.status?.drift_measures?.hellinger_mean) &&
isNumber(modelEndpoint.status?.drift_measures?.tvd_mean)
diff --git a/src/components/DetailsInputs/DetailsInputs.jsx b/src/components/DetailsInputs/DetailsInputs.jsx
index 6b5efbc228..178dc7004e 100644
--- a/src/components/DetailsInputs/DetailsInputs.jsx
+++ b/src/components/DetailsInputs/DetailsInputs.jsx
@@ -25,9 +25,8 @@ import PropTypes from 'prop-types'
import classnames from 'classnames'
import ArtifactsPreviewController from '../ArtifactsPreview/ArtifactsPreviewController'
-import Loader from '../../common/Loader/Loader'
import NoData from '../../common/NoData/NoData'
-import { Tooltip, TextTooltipTemplate } from 'igz-controls/components'
+import { Tooltip, TextTooltipTemplate, Loader } from 'igz-controls/components'
import {
ARTIFACT_OTHER_TYPE,
diff --git a/src/components/DetailsInputs/detailsInputs.util.jsx b/src/components/DetailsInputs/detailsInputs.util.jsx
index f83167b95d..cf29606217 100644
--- a/src/components/DetailsInputs/detailsInputs.util.jsx
+++ b/src/components/DetailsInputs/detailsInputs.util.jsx
@@ -20,8 +20,7 @@ such restriction.
import React from 'react'
import classnames from 'classnames'
-import CopyToClipboard from '../../common/CopyToClipboard/CopyToClipboard'
-import { RoundedIcon, TextTooltipTemplate, Tooltip } from 'igz-controls/components'
+import { RoundedIcon, TextTooltipTemplate, Tooltip, CopyToClipboard } from 'igz-controls/components'
import ArtifactPopUp from '../../elements/DetailsPopUp/ArtifactPopUp/ArtifactPopUp'
import FeatureVectorPopUp from '../../elements/DetailsPopUp/FeatureVectorPopUp/FeatureVectorPopUp'
diff --git a/src/components/DetailsLogs/DetailsLogs.jsx b/src/components/DetailsLogs/DetailsLogs.jsx
index d6d64d5bae..0bdc161063 100644
--- a/src/components/DetailsLogs/DetailsLogs.jsx
+++ b/src/components/DetailsLogs/DetailsLogs.jsx
@@ -17,11 +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, useMemo, useRef, useState } from 'react'
-import { useSelector } from 'react-redux'
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
+import { useDispatch, useSelector } from 'react-redux'
import PropTypes from 'prop-types'
import Logs from './Logs'
+import { setRunAttempt, setRunAttemptOptions } from '../../reducers/detailsReducer'
+import { REQUEST_CANCELED } from '../../constants'
const DetailsLogs = ({
additionalLogsTitle = '',
@@ -40,24 +42,45 @@ const DetailsLogs = ({
const streamAdditionalLogsRef = useRef()
const functionsStore = useSelector(store => store.functionsStore)
const jobsStore = useSelector(store => store.jobsStore)
+ const runAttempt = useSelector(store => store.detailsStore.runAttempt)
+ const dispatch = useDispatch()
+ const refreshLogsAbortControllerRef = useRef(new AbortController())
const mainLogsAreLoading = useMemo(() => {
- return functionsStore.logs.loading || jobsStore.logs.loading
- }, [functionsStore.logs.loading, jobsStore.logs.loading])
+ return functionsStore.logs.loading || jobsStore.logs.loadingCounter > 0
+ }, [functionsStore.logs.loading, jobsStore.logs.loadingCounter])
const additionalLogsAreLoading = useMemo(() => {
return functionsStore.nuclioLogs.loading
}, [functionsStore.nuclioLogs.loading])
+ const refreshLogsHandler = useCallback(
+ (item, project, setDetailsLogs, streamLogsRef, runAttempt) => {
+ refreshLogsAbortControllerRef.current?.abort?.(REQUEST_CANCELED)
+ streamLogsRef.current = null
+ refreshLogsAbortControllerRef.current = new AbortController()
+
+ return refreshLogs(
+ item,
+ project,
+ setDetailsLogs,
+ streamLogsRef,
+ runAttempt,
+ refreshLogsAbortControllerRef.current?.signal
+ )
+ },
+ [refreshLogs]
+ )
+
useEffect(() => {
if (refreshLogs) {
- refreshLogs(item, item.project, setDetailsLogs, streamLogsRef)
+ refreshLogsHandler(item, item.project, setDetailsLogs, streamLogsRef, runAttempt)
return () => {
setDetailsLogs('')
removeLogs()
}
}
- }, [item, refreshLogs, removeLogs, withLogsRefreshBtn])
+ }, [item, refreshLogs, removeLogs, withLogsRefreshBtn, runAttempt, refreshLogsHandler])
useEffect(() => {
refreshAdditionalLogs &&
@@ -69,12 +92,37 @@ const DetailsLogs = ({
}
}, [item, withLogsRefreshBtn, refreshAdditionalLogs, removeAdditionalLogs])
+ useEffect(() => {
+ if (item?.retryCount > 0) {
+ const attemptsList = Array.from({ length: item.retryCount + 1 }, (_, index) => ({
+ id: `${index + 1}`,
+ label: `${index + 1}`
+ }))
+
+ attemptsList[attemptsList.length - 1].id = '0'
+
+ dispatch(setRunAttemptOptions(attemptsList))
+
+ return () => {
+ dispatch(setRunAttemptOptions([]))
+ dispatch(setRunAttempt('0'))
+ refreshLogsAbortControllerRef.current?.abort?.(REQUEST_CANCELED)
+ }
+ } else {
+ return () => {
+ refreshLogsAbortControllerRef.current?.abort?.(REQUEST_CANCELED)
+ }
+ }
+ }, [dispatch, item])
+
return (
{logsTitle &&
{logsTitle} }
refreshLogs(item, item.project, setDetailsLogs, streamLogsRef)}
+ refreshLogs={() =>
+ refreshLogsHandler(item, item.project, setDetailsLogs, streamLogsRef, runAttempt)
+ }
ref={streamLogsRef}
withLogsRefreshBtn={withLogsRefreshBtn}
detailsLogs={detailsLogs}
diff --git a/src/components/DetailsLogs/Logs.jsx b/src/components/DetailsLogs/Logs.jsx
index b9ee49f02c..3d5110b9f1 100644
--- a/src/components/DetailsLogs/Logs.jsx
+++ b/src/components/DetailsLogs/Logs.jsx
@@ -20,8 +20,7 @@ such restriction.
import React from 'react'
import PropTypes from 'prop-types'
-import { Button } from 'igz-controls/components'
-import Loader from '../../common/Loader/Loader'
+import { Button, Loader } from 'igz-controls/components'
import NoData from '../../common/NoData/NoData'
import RefreshIcon from 'igz-controls/images/refresh.svg?react'
diff --git a/src/components/DetailsMetadata/DetailsMetadata.jsx b/src/components/DetailsMetadata/DetailsMetadata.jsx
index 124eaba663..3e25ce7878 100644
--- a/src/components/DetailsMetadata/DetailsMetadata.jsx
+++ b/src/components/DetailsMetadata/DetailsMetadata.jsx
@@ -21,9 +21,8 @@ import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
-import ChipCell from '../../common/ChipCell/ChipCell'
import NoData from '../../common/NoData/NoData'
-import { Tooltip, TextTooltipTemplate } from 'igz-controls/components'
+import { Tooltip, TextTooltipTemplate, ChipCell } from 'igz-controls/components'
import { generateMetadata } from './detailsMetadata.util'
diff --git a/src/components/DetailsMetrics/DetailsMetrics.jsx b/src/components/DetailsMetrics/DetailsMetrics.jsx
index 1d2ac8f42a..8f76078faa 100644
--- a/src/components/DetailsMetrics/DetailsMetrics.jsx
+++ b/src/components/DetailsMetrics/DetailsMetrics.jsx
@@ -22,12 +22,12 @@ import PropTypes from 'prop-types'
import { useDispatch, useSelector } from 'react-redux'
import { isEmpty } from 'lodash'
-import ApplicationMetricCard from './MetricsCards/ApplicationMetricCard'
+import ApplicationMetricCard from './MetricsCards/ApplicationMetricCard/ApplicationMetricCard'
import DatePicker from '../../common/DatePicker/DatePicker'
-import InvocationsMetricCard from './MetricsCards/InvocationsMetricCard'
+import InvocationsMetricCard from './MetricsCards/InvocationsMetricCard/InvocationsMetricCard'
import MetricsSelector from '../../elements/MetricsSelector/MetricsSelector'
import NoData from '../../common/NoData/NoData'
-import NoMetricData from './MetricsCards/NoMetricData'
+import NoMetricData from './MetricsCards/NoMetricData/NoMetricData'
import StatsCard from '../../common/StatsCard/StatsCard'
import {
@@ -42,6 +42,7 @@ import {
TIME_FRAME_LIMITS
} from '../../utils/datePicker.util'
import {
+ clearMetricsOptions,
fetchModelEndpointMetrics,
fetchModelEndpointMetricsValues,
setDetailsDates,
@@ -54,7 +55,12 @@ import MetricsIcon from 'igz-controls/images/metrics-icon.svg?react'
import './DetailsMetrics.scss'
-const DetailsMetrics = ({ selectedItem }) => {
+const DetailsMetrics = ({
+ applicationNameProp = '',
+ selectedItem,
+ renderTitle = null,
+ isDetailsPopUp = false
+}) => {
const [metrics, setMetrics] = useState([])
const [selectedDate, setSelectedDate] = useState('')
const [previousTotalInvocation, setPreviousTotalInvocation] = useState(0)
@@ -118,18 +124,21 @@ const DetailsMetrics = ({ selectedItem }) => {
dispatch(
fetchModelEndpointMetrics({
project: selectedItem.metadata.project,
- uid: selectedItem.metadata.uid
+ uid: selectedItem.metadata.uid,
+ applicationName: applicationNameProp
})
)
.unwrap()
.then(() => setMetricOptionsAreLoaded(true))
- }, [dispatch, selectedItem.metadata.project, selectedItem.metadata.uid])
+ }, [applicationNameProp, dispatch, selectedItem.metadata.project, selectedItem.metadata.uid])
useEffect(() => {
const selectedDate = detailsStore.dates.selectedOptionId
if (!selectedDate || !(selectedDate in timeRangeMapping)) return
- setSelectedDate(timeRangeMapping[selectedDate])
+ queueMicrotask(() => {
+ setSelectedDate(timeRangeMapping[selectedDate])
+ })
}, [detailsStore.dates.selectedOptionId])
const fetchData = useCallback(
@@ -213,7 +222,9 @@ const DetailsMetrics = ({ selectedItem }) => {
selectedItem.metadata.uid
)
} else {
- setMetrics([])
+ queueMicrotask(() => {
+ setMetrics([])
+ })
}
return () => {
@@ -232,34 +243,46 @@ const DetailsMetrics = ({ selectedItem }) => {
metricsValuesAbortController
])
+ useEffect(() => {
+ if (isDetailsPopUp) {
+ return () => {
+ dispatch(clearMetricsOptions())
+ }
+ }
+ }, [dispatch, isDetailsPopUp])
+
return (
-
-
- dispatch(
- setSelectedMetricsOptions({
- endpointUid: selectedItem.metadata.uid,
- metrics
- })
- )
- }
- preselectedMetrics={detailsStore.metricsOptions.preselected}
- />
-
+
+ {renderTitle &&
{renderTitle()} }
+
+
+ dispatch(
+ setSelectedMetricsOptions({
+ endpointUid: selectedItem.metadata.uid,
+ metrics
+ })
+ )
+ }
+ preselectedMetrics={detailsStore.metricsOptions.preselected}
+ />
+
+
{generatedMetrics.length === 0 ? (
!detailsStore.loadingCounter ? (
@@ -278,7 +301,7 @@ const DetailsMetrics = ({ selectedItem }) => {
return (
- {applicationName === ML_RUN_INFRA ? '' : applicationName}
+ {applicationName === ML_RUN_INFRA || applicationNameProp ? '' : applicationName}
{applicationMetrics.map(metric => {
if (applicationName === ML_RUN_INFRA) {
@@ -326,7 +349,10 @@ const DetailsMetrics = ({ selectedItem }) => {
}
DetailsMetrics.propTypes = {
- selectedItem: PropTypes.object,
+ applicationNameProp: PropTypes.string,
+ selectedItem: PropTypes.object.isRequired,
+ renderTitle: PropTypes.func,
+ isDetailsPopUp: PropTypes.bool
}
export default React.memo(DetailsMetrics)
diff --git a/src/components/DetailsMetrics/DetailsMetrics.scss b/src/components/DetailsMetrics/DetailsMetrics.scss
index c0fcd57790..f8d9158357 100644
--- a/src/components/DetailsMetrics/DetailsMetrics.scss
+++ b/src/components/DetailsMetrics/DetailsMetrics.scss
@@ -4,22 +4,38 @@
$stickyHeaderHeight: 55px;
.metrics-wrapper {
- &__custom-filters {
+ &__header {
position: sticky;
top: 0;
z-index: 2;
display: flex;
- flex-wrap: wrap;
- align-items: center;
- justify-content: flex-end;
+ justify-content: space-between;
background-color: colors.$white;
- > * {
- margin: 0 0 15px 0;
+ &__custom-filters {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: flex-end;
+ width: 100%;
+ background-color: colors.$white;
+
+ > * {
+ margin: 0 0 15px;
+ }
+
+ .details-date-picker {
+ margin-left: 15px;
+ }
}
- .details-date-picker {
- margin-left: 15px;
+ &__title {
+ max-width: 350px;
+ font-size: 24px;
+
+ a:hover {
+ text-decoration: underline;
+ }
}
}
@@ -37,363 +53,12 @@ $stickyHeaderHeight: 55px;
font-size: 20px;
}
- &__card {
- width: 100%;
- padding-bottom: 35px;
- border: borders.$primaryBorder;
- box-shadow: none;
-
- &.metrics__card-metric {
- .metrics__card-body {
- min-height: 130px;
- aspect-ratio: 7;
- }
- }
-
- &.metrics__card-invocation {
- .metrics__card-body {
- aspect-ratio: 5;
- }
- }
-
- .metrics__card-row {
- display: flex;
- flex: 1 0 100%;
- }
-
- &-invocation {
- position: relative;
- z-index: 1;
- width: 100%;
-
- &__toggle-icon {
- position: absolute;
- right: 5px;
- bottom: 5px;
- align-self: flex-start;
- visibility: hidden;
- opacity: 0;
- transition: all 0.2s ease-in-out;
- }
-
- &:hover {
- .metrics__card-invocation__toggle-icon {
- visibility: visible;
- opacity: 1;
-
- .round-icon-cp__circle {
- &::before {
- opacity: 0.4;
- }
-
- &:hover::before {
- opacity: 1;
- }
- }
- }
- }
-
- &-header {
- display: flex;
- flex: 1;
- align-items: baseline;
- justify-content: end;
- height: 40px;
- color: colors.$primary;
- font-weight: 500;
- font-size: 14px;
-
- &__drift-icon-container {
- align-self: stretch;
- margin-right: 2px;
-
- svg {
- box-sizing: content-box;
- width: 17px;
- height: 17px;
- padding-top: 9px;
- }
- }
-
- &__drift_up {
- color: colors.$brightTurquoise;
- }
-
- &__drift_down {
- color: colors.$ceriseRed;
- }
-
- &__selected-date {
- margin-left: 4px;
- color: colors.$topaz;
- }
-
- &__total-title {
- margin: 0 6px 0 12px;
- font-weight: 700;
- }
-
- &__total-score {
- font-weight: 700;
- font-size: 24px;
- }
- }
-
- &-content {
- flex: 0 0 0;
- flex-wrap: wrap;
- align-content: center;
- align-items: baseline;
- justify-content: center;
- width: 0;
- font-weight: 700;
- font-size: 18px;
- visibility: hidden;
- opacity: 0;
- transition: flex 0.2s;
-
- &-visible {
- display: flex;
- flex: 0 1 auto;
- width: fit-content;
- margin-right: 15px;
- visibility: visible;
- opacity: 1;
- transition:
- flex 0.2s,
- visibility 0s,
- opacity 1s;
- }
-
- &-title {
- margin-right: 24px;
- color: colors.$primary;
- font-size: 18px;
- text-align: center;
- }
-
- &-container {
- display: flex;
- gap: 2px;
- align-items: baseline;
- font-weight: 700;
- font-size: 14px;
-
- &__drift-icon {
- align-self: stretch;
-
- svg {
- box-sizing: content-box;
- width: 17px;
- height: 17px;
- margin-top: -1px;
- }
- }
-
- &__drift_up {
- margin-right: 4px;
- color: colors.$brightTurquoise;
- }
-
- &__drift_down {
- margin-right: 4px;
- color: colors.$ceriseRed;
- }
- }
-
- &-data {
- display: flex;
- align-items: baseline;
- margin-left: 15px;
-
- &__total-title {
- margin-right: 7px;
- font-weight: 700;
- font-size: 14px;
- }
-
- &__total-score {
- font-weight: 700;
- font-size: 24px;
- }
- }
- }
-
- .metrics__card {
- padding-top: 12px;
- padding-bottom: 0;
- }
-
- &_collapsed .stats-card__row {
- height: 0;
- min-height: 0 !important;
- margin-bottom: 0 !important;
- opacity: 0;
- transition: all 0.2s ease-in-out;
- }
-
- &_expanded .stats-card__row {
- height: 40px;
- opacity: 1;
- transition: all 0.2s ease-in-out;
- }
- }
-
- &-drift-status {
- display: inline-block;
- width: 10px;
- height: 10px;
- margin-left: 8px;
- border-radius: 50%;
- }
-
- &-drift-status-possible_drift {
- background-color: colors.$grandis;
- }
-
- &-drift-status-drift_detected {
- background-color: colors.$ceriseRed;
- }
-
- &-drift-status-no_drift {
- background-color: colors.$brightTurquoise;
- }
-
- &-drift-no_status {
- background-color: colors.$doveGray;
- }
-
- &-header {
- position: relative;
- top: -5px;
- display: flex;
- flex-direction: row;
- gap: 8px;
- align-items: center;
- justify-content: space-between;
- padding-left: 5px;
- font-weight: 500;
- transition: all 0.2s linear;
-
- &-result-kind {
- display: flex;
- gap: 4px;
- align-self: center;
- justify-content: center;
- width: max-content;
- overflow: hidden;
- color: colors.$topaz;
- font-weight: 500;
- font-size: 14px;
- font-family: Roboto, sans-serif;
- font-style: normal;
- line-height: normal;
- text-overflow: ellipsis;
- }
-
- &-data {
- color: colors.$topaz;
- font-weight: 700;
- font-size: 18px;
- text-align: center;
- leading-trim: both;
- text-edge: cap;
- }
-
- &-label {
- font-size: 14px;
- }
- }
-
- &-body {
- position: relative;
- display: flex;
- flex: 1;
- align-self: stretch;
- box-sizing: border-box;
- width: 100%;
-
- &-collapsed {
- height: 80px;
- }
-
- .chart-item {
- flex: 1;
-
- canvas {
- width: 100%;
- height: 100%;
- }
- }
-
- &-bar {
- flex: 0 1 25%;
- min-width: 210px;
- margin-right: 15px;
-
- canvas {
- width: 100%;
- height: 100%;
- }
-
- canvas.hidden-canvas {
- position: absolute;
- top: 0;
- left: 0;
- opacity: 0;
- }
- }
-
- &-line {
- flex: 1 1 0;
- margin-top: 3px;
-
- canvas {
- width: 100% !important;
- height: 100% !important;
- }
-
- canvas.hidden-canvas {
- position: absolute;
- top: 0;
- left: 0;
- opacity: 0;
- }
- }
-
- &-invocation {
- flex: 1;
- border: 1px solid transparent;
-
- canvas {
- width: 100% !important;
- height: 100% !important;
- }
-
- canvas.hidden-canvas {
- position: absolute;
- top: 0;
- left: 0;
- opacity: 0;
- }
- }
- }
- }
-
&-wrapper {
display: flex;
flex-flow: column nowrap;
height: 100%;
}
- &__empty-card {
- display: flex;
- gap: 25px;
- align-items: center;
- justify-content: center;
- width: 100%;
- }
-
&__empty-select {
display: flex;
flex-direction: column;
@@ -406,9 +71,3 @@ $stickyHeaderHeight: 55px;
}
}
}
-
-.empty-invocation-card {
- z-index: 1;
- padding-bottom: 1em;
- box-shadow: 0 -10px 0 0 colors.$white;
-}
diff --git a/src/components/DetailsMetrics/MetricsCards/ApplicationMetricCard.jsx b/src/components/DetailsMetrics/MetricsCards/ApplicationMetricCard/ApplicationMetricCard.jsx
similarity index 91%
rename from src/components/DetailsMetrics/MetricsCards/ApplicationMetricCard.jsx
rename to src/components/DetailsMetrics/MetricsCards/ApplicationMetricCard/ApplicationMetricCard.jsx
index d14447ea37..75eb7aa0ee 100644
--- a/src/components/DetailsMetrics/MetricsCards/ApplicationMetricCard.jsx
+++ b/src/components/DetailsMetrics/MetricsCards/ApplicationMetricCard/ApplicationMetricCard.jsx
@@ -19,17 +19,17 @@ such restriction.
*/
import React, { useMemo, memo } from 'react'
-import MetricChart from '../../../common/MlChart/MetricChart/MetricChart'
-import StatsCard from '../../../common/StatsCard/StatsCard'
+import MetricChart from '../../../../common/MlChart/MetricChart/MetricChart'
+import StatsCard from '../../../../common/StatsCard/StatsCard'
import { TextTooltipTemplate, Tooltip } from 'igz-controls/components'
-import { CHART_TYPE_LINE, CHART_TYPE_BAR } from '../../../constants'
-import { METRIC_DATA } from '../../../types'
-import { calculateHistogram, METRIC_COMPUTED_AVG_POINTS } from '../detailsMetrics.util'
-import { getMetricChartConfig } from '../../../utils/getChartConfig'
+import { CHART_TYPE_LINE, CHART_TYPE_BAR } from '../../../../constants'
+import { METRIC_DATA } from '../../../../types'
+import { calculateHistogram, METRIC_COMPUTED_AVG_POINTS } from '../../detailsMetrics.util'
+import { getMetricChartConfig } from '../../../../utils/getChartConfig'
import { getScssVariableValue } from 'igz-controls/utils/common.util'
-import '../DetailsMetrics.scss'
+import '../MetricsCards.scss'
const ApplicationMetricCard = ({ metric }) => {
const javaColor = useMemo(() => getScssVariableValue('--javaColor'), [])
diff --git a/src/components/DetailsMetrics/MetricsCards/InvocationsMetricCard.jsx b/src/components/DetailsMetrics/MetricsCards/InvocationsMetricCard/InvocationsMetricCard.jsx
similarity index 94%
rename from src/components/DetailsMetrics/MetricsCards/InvocationsMetricCard.jsx
rename to src/components/DetailsMetrics/MetricsCards/InvocationsMetricCard/InvocationsMetricCard.jsx
index d33f5e8386..4cac6a9065 100644
--- a/src/components/DetailsMetrics/MetricsCards/InvocationsMetricCard.jsx
+++ b/src/components/DetailsMetrics/MetricsCards/InvocationsMetricCard/InvocationsMetricCard.jsx
@@ -21,22 +21,25 @@ import { forwardRef, useMemo } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
-import MetricChart from '../../../common/MlChart/MetricChart/MetricChart'
-import StatsCard from '../../../common/StatsCard/StatsCard'
+import MetricChart from '../../../../common/MlChart/MetricChart/MetricChart'
+import StatsCard from '../../../../common/StatsCard/StatsCard'
import { RoundedIcon, Tip } from 'igz-controls/components'
import {
calculatePercentageDrift,
METRIC_COMPUTED_TOTAL_POINTS,
METRIC_RAW_TOTAL_POINTS
-} from '../detailsMetrics.util'
-import { CHART_TYPE_LINE, CHART_TYPE_GRADIENT_LINE } from '../../../constants'
-import { getMetricChartConfig } from '../../../utils/getChartConfig'
+} from '../../detailsMetrics.util'
+import { CHART_TYPE_LINE, CHART_TYPE_GRADIENT_LINE } from '../../../../constants'
+import { getMetricChartConfig } from '../../../../utils/getChartConfig'
import { getScssVariableValue } from 'igz-controls/utils/common.util'
import EnlargeIcon from 'igz-controls/images/expand.svg?react'
import MinimizeIcon from 'igz-controls/images/collapse.svg?react'
+import '../MetricsCards.scss'
+import './InvocationsMetricCard.scss'
+
const InvocationsMetricCard = forwardRef(
(
{
@@ -167,6 +170,7 @@ const InvocationsMetricCard = forwardRef(
diff --git a/src/components/DetailsMetrics/MetricsCards/InvocationsMetricCard/InvocationsMetricCard.scss b/src/components/DetailsMetrics/MetricsCards/InvocationsMetricCard/InvocationsMetricCard.scss
new file mode 100644
index 0000000000..c0cc0bc0cb
--- /dev/null
+++ b/src/components/DetailsMetrics/MetricsCards/InvocationsMetricCard/InvocationsMetricCard.scss
@@ -0,0 +1,201 @@
+@use 'igz-controls/scss/colors';
+@use 'igz-controls/scss/borders';
+
+.metrics__card-invocation {
+ position: relative;
+ z-index: 1;
+ width: 100%;
+
+ .metrics__card-body {
+ aspect-ratio: 5;
+
+ &-invocation {
+ flex: 1;
+ border: 1px solid transparent;
+
+ canvas {
+ width: 100% !important;
+ height: 100% !important;
+ }
+
+ canvas.hidden-canvas {
+ position: absolute;
+ top: 0;
+ left: 0;
+ opacity: 0;
+ }
+ }
+ }
+
+ &__toggle-icon {
+ position: absolute;
+ right: 5px;
+ bottom: 5px;
+ align-self: flex-start;
+ visibility: hidden;
+ opacity: 0;
+ transition: all 0.2s ease-in-out;
+ }
+
+ &:hover {
+ .metrics__card-invocation__toggle-icon {
+ visibility: visible;
+ opacity: 1;
+
+ .round-icon-cp__circle {
+ &::before {
+ opacity: 0.4;
+ }
+
+ &:hover::before {
+ opacity: 1;
+ }
+ }
+ }
+ }
+
+ &-header {
+ display: flex;
+ flex: 1;
+ align-items: baseline;
+ justify-content: end;
+ height: 40px;
+ color: colors.$primary;
+ font-weight: 500;
+ font-size: 14px;
+
+ &__drift-icon-container {
+ align-self: stretch;
+ margin-right: 2px;
+
+ svg {
+ box-sizing: content-box;
+ width: 17px;
+ height: 17px;
+ padding-top: 9px;
+ }
+ }
+
+ &__drift_up {
+ color: colors.$brightTurquoise;
+ }
+
+ &__drift_down {
+ color: colors.$ceriseRed;
+ }
+
+ &__selected-date {
+ margin-left: 4px;
+ color: colors.$topaz;
+ }
+
+ &__total-title {
+ margin: 0 6px 0 12px;
+ font-weight: 700;
+ }
+
+ &__total-score {
+ font-weight: 700;
+ font-size: 24px;
+ }
+ }
+
+ &-content {
+ flex: 0 0 0;
+ flex-wrap: wrap;
+ align-content: center;
+ align-items: baseline;
+ justify-content: center;
+ width: 0;
+ font-weight: 700;
+ font-size: 18px;
+ visibility: hidden;
+ opacity: 0;
+ transition: flex 0.2s;
+
+ &-visible {
+ display: flex;
+ flex: 0 1 auto;
+ width: fit-content;
+ margin-right: 15px;
+ visibility: visible;
+ opacity: 1;
+ transition:
+ flex 0.2s,
+ visibility 0s,
+ opacity 1s;
+ }
+
+ &-title {
+ margin-right: 24px;
+ color: colors.$primary;
+ font-size: 18px;
+ text-align: center;
+ }
+
+ &-container {
+ display: flex;
+ gap: 2px;
+ align-items: baseline;
+ font-weight: 700;
+ font-size: 14px;
+
+ &__drift-icon {
+ align-self: stretch;
+
+ svg {
+ box-sizing: content-box;
+ width: 17px;
+ height: 17px;
+ margin-top: -1px;
+ }
+ }
+
+ &__drift_up {
+ margin-right: 4px;
+ color: colors.$brightTurquoise;
+ }
+
+ &__drift_down {
+ margin-right: 4px;
+ color: colors.$ceriseRed;
+ }
+ }
+
+ &-data {
+ display: flex;
+ align-items: baseline;
+ margin-left: 15px;
+
+ &__total-title {
+ margin-right: 7px;
+ font-weight: 700;
+ font-size: 14px;
+ }
+
+ &__total-score {
+ font-weight: 700;
+ font-size: 24px;
+ }
+ }
+ }
+
+ .metrics__card {
+ padding-top: 12px;
+ padding-bottom: 0;
+ }
+
+ &_collapsed .stats-card__row {
+ height: 0;
+ min-height: 0 !important;
+ margin-bottom: 0 !important;
+ opacity: 0;
+ transition: all 0.2s ease-in-out;
+ }
+
+ &_expanded .stats-card__row {
+ height: 40px;
+ opacity: 1;
+ transition: all 0.2s ease-in-out;
+ }
+}
diff --git a/src/components/DetailsMetrics/MetricsCards/MetricsCards.scss b/src/components/DetailsMetrics/MetricsCards/MetricsCards.scss
new file mode 100644
index 0000000000..51a466c785
--- /dev/null
+++ b/src/components/DetailsMetrics/MetricsCards/MetricsCards.scss
@@ -0,0 +1,144 @@
+@use 'igz-controls/scss/colors';
+@use 'igz-controls/scss/borders';
+
+.metrics__card {
+ width: 100%;
+ padding-bottom: 35px;
+ border: borders.$primaryBorder;
+ box-shadow: none;
+
+ &.metrics__card-metric {
+ .metrics__card-body {
+ min-height: 130px;
+ aspect-ratio: 7;
+ }
+ }
+
+ .metrics__card-row {
+ display: flex;
+ flex: 1 0 100%;
+ }
+
+ &-drift-status {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ margin-left: 8px;
+ border-radius: 50%;
+ }
+
+ &-drift-status-possible_drift {
+ background-color: colors.$grandis;
+ }
+
+ &-drift-status-drift_detected {
+ background-color: colors.$ceriseRed;
+ }
+
+ &-drift-status-no_drift {
+ background-color: colors.$brightTurquoise;
+ }
+
+ &-drift-no_status {
+ background-color: colors.$doveGray;
+ }
+
+ &-header {
+ position: relative;
+ top: -5px;
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+ align-items: center;
+ justify-content: space-between;
+ padding-left: 5px;
+ font-weight: 500;
+ transition: all 0.2s linear;
+
+ &-result-kind {
+ display: flex;
+ gap: 4px;
+ align-self: center;
+ justify-content: center;
+ width: max-content;
+ overflow: hidden;
+ color: colors.$topaz;
+ font-weight: 500;
+ font-size: 14px;
+ font-family: Roboto, sans-serif;
+ font-style: normal;
+ line-height: normal;
+ text-overflow: ellipsis;
+ }
+
+ &-data {
+ color: colors.$topaz;
+ font-weight: 700;
+ font-size: 18px;
+ text-align: center;
+ leading-trim: both;
+ text-edge: cap;
+ }
+
+ &-label {
+ font-size: 14px;
+ }
+ }
+
+ &-body {
+ position: relative;
+ display: flex;
+ flex: 1;
+ align-self: stretch;
+ box-sizing: border-box;
+ width: 100%;
+
+ &-collapsed {
+ height: 80px;
+ }
+
+ .chart-item {
+ flex: 1;
+
+ canvas {
+ width: 100%;
+ height: 100%;
+ }
+ }
+
+ &-bar {
+ flex: 0 1 25%;
+ min-width: 210px;
+ margin-right: 15px;
+
+ canvas {
+ width: 100%;
+ height: 100%;
+ }
+
+ canvas.hidden-canvas {
+ position: absolute;
+ top: 0;
+ left: 0;
+ opacity: 0;
+ }
+ }
+
+ &-line {
+ flex: 1 1 0;
+ margin-top: 3px;
+
+ canvas {
+ width: 100% !important;
+ height: 100% !important;
+ }
+
+ canvas.hidden-canvas {
+ position: absolute;
+ top: 0;
+ left: 0;
+ opacity: 0;
+ }
+ }
+ }
+}
diff --git a/src/components/DetailsMetrics/MetricsCards/NoMetricData.jsx b/src/components/DetailsMetrics/MetricsCards/NoMetricData/NoMetricData.jsx
similarity index 85%
rename from src/components/DetailsMetrics/MetricsCards/NoMetricData.jsx
rename to src/components/DetailsMetrics/MetricsCards/NoMetricData/NoMetricData.jsx
index c1817e834d..9b7f0d77b2 100644
--- a/src/components/DetailsMetrics/MetricsCards/NoMetricData.jsx
+++ b/src/components/DetailsMetrics/MetricsCards/NoMetricData/NoMetricData.jsx
@@ -19,9 +19,12 @@ such restriction.
*/
import PropTypes from 'prop-types'
-import StatsCard from '../../../common/StatsCard/StatsCard'
+import StatsCard from '../../../../common/StatsCard/StatsCard'
-import NoDataIcon from 'igz-controls/images/no-data-metric-icon.svg?react'
+import NoDataIcon from 'igz-controls/images/no-data-metric-image.png'
+
+import '../MetricsCards.scss'
+import './NoMetricData.scss'
const NoMetricData = ({ title = '', message = 'No data to show', className = '', tip = '' }) => {
return (
@@ -29,7 +32,7 @@ const NoMetricData = ({ title = '', message = 'No data to show', className = '',
-
+
{message}
diff --git a/src/components/DetailsMetrics/MetricsCards/NoMetricData/NoMetricData.scss b/src/components/DetailsMetrics/MetricsCards/NoMetricData/NoMetricData.scss
new file mode 100644
index 0000000000..53d9dff89c
--- /dev/null
+++ b/src/components/DetailsMetrics/MetricsCards/NoMetricData/NoMetricData.scss
@@ -0,0 +1,19 @@
+@use 'igz-controls/scss/colors';
+
+.metrics__empty-card {
+ display: flex;
+ gap: 25px;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+
+ img {
+ width: 41px;
+ }
+}
+
+.empty-invocation-card {
+ z-index: 1;
+ padding-bottom: 1em;
+ box-shadow: 0 -10px 0 0 colors.$white;
+}
diff --git a/src/components/DetailsMetrics/detailsMetrics.util.jsx b/src/components/DetailsMetrics/detailsMetrics.util.jsx
index 6d45323988..aa3ed4ed5b 100644
--- a/src/components/DetailsMetrics/detailsMetrics.util.jsx
+++ b/src/components/DetailsMetrics/detailsMetrics.util.jsx
@@ -166,8 +166,9 @@ const generateResultMessage = (driftStatus, resultKind) => {
return `${capitalize(resultKindMessage)} ${text.toLowerCase().replace('drift', '').trim()}`
}
-export const generateMetricsItems = metrics => {
+export const generateMetricsItems = (metrics, applicationName) => {
return chain(metrics)
+ .filter(metric => !applicationName || metric.app === applicationName || metric.app === ML_RUN_INFRA) // todo remove filter when API will support app param
.sortBy(metric => metric.label)
.map(metric => {
return {
diff --git a/src/components/DetailsPods/DetailsPods.jsx b/src/components/DetailsPods/DetailsPods.jsx
index 003bcd448c..70c76c036c 100644
--- a/src/components/DetailsPods/DetailsPods.jsx
+++ b/src/components/DetailsPods/DetailsPods.jsx
@@ -23,16 +23,16 @@ import { useSelector } from 'react-redux'
import Prism from 'prismjs'
import classnames from 'classnames'
import { useParams } from 'react-router-dom'
-import Loader from '../../common/Loader/Loader'
import NoData from '../../common/NoData/NoData'
-import { Tooltip, TextTooltipTemplate } from 'igz-controls/components'
+import { Tooltip, TextTooltipTemplate, Loader } from 'igz-controls/components'
import { generatePods } from './detailsPods.util'
import './detailsPods.scss'
+import { PENDING_STATE } from '../../constants'
-const DetailsPods = ({ isDetailsPopUp = false, noDataMessage = ''}) => {
+const DetailsPods = ({ isDetailsPopUp = false, noDataMessage = '' }) => {
const [selectedPod, setSelectedPod] = useState(null)
const [table, setTable] = useState([])
const params = useParams()
@@ -43,7 +43,9 @@ const DetailsPods = ({ isDetailsPopUp = false, noDataMessage = ''}) => {
}, [detailsStore.detailsJobPods, detailsStore.pods, isDetailsPopUp])
useEffect(() => {
- setTable(generatePods(podsData))
+ queueMicrotask(() => {
+ setTable(generatePods(podsData))
+ })
return () => {
setSelectedPod(null)
@@ -52,7 +54,9 @@ const DetailsPods = ({ isDetailsPopUp = false, noDataMessage = ''}) => {
useEffect(() => {
if (!selectedPod) {
- setSelectedPod(table[0])
+ queueMicrotask(() => {
+ setSelectedPod(table[0])
+ })
}
}, [selectedPod, table])
@@ -72,7 +76,7 @@ const DetailsPods = ({ isDetailsPopUp = false, noDataMessage = ''}) => {
selectedPod?.value === row.value && 'row_active'
)
const podStatus =
- row.status?.phase?.toLowerCase() === 'pending'
+ row.status?.phase?.toLowerCase() === PENDING_STATE
? 'pending...'
: (row.status?.phase?.toLowerCase() ?? '')
diff --git a/src/components/DetailsRequestedFeatures/DetailsRequestedFeatures.jsx b/src/components/DetailsRequestedFeatures/DetailsRequestedFeatures.jsx
index 0c15405821..7c195c2563 100644
--- a/src/components/DetailsRequestedFeatures/DetailsRequestedFeatures.jsx
+++ b/src/components/DetailsRequestedFeatures/DetailsRequestedFeatures.jsx
@@ -24,8 +24,12 @@ import { useDispatch } from 'react-redux'
import DetailsRequestedFeaturesView from './DetailsRequestedFeaturesView'
+import {
+ setChanges,
+ setChangesCounter,
+ setChangesData
+} from 'igz-controls/reducers/commonDetailsReducer'
import { countChanges } from '../Details/details.util'
-import { setChanges, setChangesCounter, setChangesData } from '../../reducers/detailsReducer'
const DetailsRequestedFeatures = ({ changes, formState, isDetailsPopUp = false, selectedItem }) => {
const [confirmDialogData, setConfirmDialogData] = useState({
diff --git a/src/components/DetailsRequestedFeatures/detailsRequestedFeatures.scss b/src/components/DetailsRequestedFeatures/detailsRequestedFeatures.scss
index fc59156a0b..bcd718a217 100644
--- a/src/components/DetailsRequestedFeatures/detailsRequestedFeatures.scss
+++ b/src/components/DetailsRequestedFeatures/detailsRequestedFeatures.scss
@@ -10,6 +10,7 @@
&-header {
position: sticky;
top: 0;
+ z-index: 3;
display: flex;
flex-direction: row;
align-items: center;
@@ -20,7 +21,6 @@
line-height: 24px;
background-color: colors.$white;
border-bottom: borders.$secondaryBorder;
- z-index: 3;
}
&-cell {
diff --git a/src/components/DetailsResults/DetailsResults.jsx b/src/components/DetailsResults/DetailsResults.jsx
index 35b2f94956..2f404e92c7 100644
--- a/src/components/DetailsResults/DetailsResults.jsx
+++ b/src/components/DetailsResults/DetailsResults.jsx
@@ -25,7 +25,7 @@ import classNames from 'classnames'
import NoData from '../../common/NoData/NoData'
import { Tip, Tooltip, TextTooltipTemplate } from 'igz-controls/components'
-import { roundFloats } from '../../utils/roundFloats'
+import { roundFloats } from 'igz-controls/utils/common.util'
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'
diff --git a/src/components/DetailsResults/detailsResults.scss b/src/components/DetailsResults/detailsResults.scss
index 1b5d47ebf3..3f74e999a7 100644
--- a/src/components/DetailsResults/detailsResults.scss
+++ b/src/components/DetailsResults/detailsResults.scss
@@ -3,8 +3,8 @@
@use 'igz-controls/scss/mixins';
.table__item-results {
- display: flex;
position: relative;
+ display: flex;
.table {
i {
diff --git a/src/components/DetailsStatistics/DetailsStatistics.jsx b/src/components/DetailsStatistics/DetailsStatistics.jsx
index e764adc51d..aebfee8431 100644
--- a/src/components/DetailsStatistics/DetailsStatistics.jsx
+++ b/src/components/DetailsStatistics/DetailsStatistics.jsx
@@ -58,7 +58,7 @@ const DetailsStatistics = ({ selectedItem }) => {
}),
[detailsStatisticsHeaderRowHeight, detailsStatisticsRowHeight]
)
- const chartConfig = useMemo(getHistogramChartConfig, [])
+ const chartConfig = useMemo(() => getHistogramChartConfig, [])
const headers = useMemo(
() =>
Object.entries(statistics[0]).map(([label, value]) => ({
diff --git a/src/components/DetailsStatistics/DetailsStatisticsTableRow.jsx b/src/components/DetailsStatistics/DetailsStatisticsTableRow.jsx
index c7edce2eed..1d496a454d 100644
--- a/src/components/DetailsStatistics/DetailsStatisticsTableRow.jsx
+++ b/src/components/DetailsStatistics/DetailsStatisticsTableRow.jsx
@@ -17,7 +17,7 @@ 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, { memo, useMemo } from 'react'
+import React, { memo, useId, useMemo } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
@@ -30,6 +30,7 @@ import './detailsStatistics.scss'
const DetailsStatisticsTableRow = ({ statisticsItem, headers, chartConfig }) => {
const amethystColor = useMemo(() => getScssVariableValue('--amethystColor'), [])
+ const id = useId()
return (
@@ -79,7 +80,7 @@ const DetailsStatisticsTableRow = ({ statisticsItem, headers, chartConfig }) =>
}
return (
-
+
{statisticsValue.type.match(/icon/) && !statisticsValue.hidden && statisticsValue.value}
{statisticsValue.type === 'chart' && statisticsValue.value[1]?.length > 0 && (
diff --git a/src/components/DetailsStatistics/detailsStatistics.scss b/src/components/DetailsStatistics/detailsStatistics.scss
index 75601d6efb..d24b6f49f5 100644
--- a/src/components/DetailsStatistics/detailsStatistics.scss
+++ b/src/components/DetailsStatistics/detailsStatistics.scss
@@ -21,7 +21,7 @@ $detailsStatisticsHeaderRowHeight: 49px;
&-wrapper {
width: 100%;
- padding: 0 0 20px 0;
+ padding: 0 0 20px;
}
&-header {
diff --git a/src/components/DetailsStatistics/detailsStatistics.util.jsx b/src/components/DetailsStatistics/detailsStatistics.util.jsx
index 53ae6f0a14..5d137d742b 100644
--- a/src/components/DetailsStatistics/detailsStatistics.util.jsx
+++ b/src/components/DetailsStatistics/detailsStatistics.util.jsx
@@ -21,7 +21,7 @@ import React from 'react'
import { Tooltip, TextTooltipTemplate } from 'igz-controls/components'
-import { roundFloats } from '../../utils/roundFloats'
+import { roundFloats } from 'igz-controls/utils/common.util'
import Primary from 'igz-controls/images/ic-key.svg?react'
import LabelColumn from 'igz-controls/images/ic_target-with-dart.svg?react'
diff --git a/src/components/DetailsTransformations/ConfigSource/ConfigSource.jsx b/src/components/DetailsTransformations/ConfigSource/ConfigSource.jsx
index c65374b8f4..9f9041b826 100644
--- a/src/components/DetailsTransformations/ConfigSource/ConfigSource.jsx
+++ b/src/components/DetailsTransformations/ConfigSource/ConfigSource.jsx
@@ -23,8 +23,7 @@ import { map } from 'lodash'
import cronstrue from 'cronstrue'
import Accordion from '../../../common/Accordion/Accordion'
-import ChipCell from '../../../common/ChipCell/ChipCell'
-import { Tooltip, TextTooltipTemplate } from 'igz-controls/components'
+import { Tooltip, TextTooltipTemplate, ChipCell } from 'igz-controls/components'
import Arrow from 'igz-controls/images/arrow.svg?react'
diff --git a/src/components/DetailsTransformations/ConfigSteps/ConfigSteps.jsx b/src/components/DetailsTransformations/ConfigSteps/ConfigSteps.jsx
index 0272c65019..28092e5085 100644
--- a/src/components/DetailsTransformations/ConfigSteps/ConfigSteps.jsx
+++ b/src/components/DetailsTransformations/ConfigSteps/ConfigSteps.jsx
@@ -188,7 +188,7 @@ const ConfigSteps = ({
/>
-
Class Name
+
Class name
}
diff --git a/src/components/DetailsTransformations/DetailsTransformations.jsx b/src/components/DetailsTransformations/DetailsTransformations.jsx
index 3918c4febd..99feec393d 100644
--- a/src/components/DetailsTransformations/DetailsTransformations.jsx
+++ b/src/components/DetailsTransformations/DetailsTransformations.jsx
@@ -155,34 +155,40 @@ const DetailsTransformations = ({ selectedItem }) => {
}, [states, targets, selectedStep])
useEffect(() => {
- setStates(cloneDeep(selectedItem.graph?.steps))
- setTargets(cloneDeep(selectedItem.targets))
+ queueMicrotask(() => {
+ setStates(cloneDeep(selectedItem.graph?.steps))
+ setTargets(cloneDeep(selectedItem.targets))
+ })
}, [selectedItem.graph, selectedItem.targets])
useEffect(() => {
- let stepsList = reject(steps, ['id', selectedStep])
+ queueMicrotask(() => {
+ let stepsList = reject(steps, ['id', selectedStep])
- setErrorSteps(reject(stepsList, ['id', 'Source']))
- stepsList.unshift({
- id: 'Source',
- label: 'Source'
- })
+ setErrorSteps(reject(stepsList, ['id', 'Source']))
+ stepsList.unshift({
+ id: 'Source',
+ label: 'Source'
+ })
- setAfterSteps(stepsList)
+ setAfterSteps(stepsList)
+ })
}, [steps, selectedStep])
useEffect(() => {
- setNodes(nodes => {
- return map(nodes, node => {
- return {
- ...node,
- className:
- node.id === selectedStep
- ? node.className
- ? (node.className += ' selected')
- : 'selected'
- : node.className?.replace('selected', '')
- }
+ queueMicrotask(() => {
+ setNodes(nodes => {
+ return map(nodes, node => {
+ return {
+ ...node,
+ className:
+ node.id === selectedStep
+ ? node.className
+ ? (node.className += ' selected')
+ : 'selected'
+ : node.className?.replace('selected', '')
+ }
+ })
})
})
}, [selectedStep])
@@ -193,7 +199,9 @@ const DetailsTransformations = ({ selectedItem }) => {
useEffect(() => {
if (selectedItem.uid !== selectedItemUid) {
- setSelectedItemUid(selectedItem.uid)
+ queueMicrotask(() => {
+ setSelectedItemUid(selectedItem.uid)
+ })
}
}, [selectedItem, selectedItemUid])
@@ -203,23 +211,25 @@ const DetailsTransformations = ({ selectedItem }) => {
-
Configuration
-
-
-
-
+
+
Configuration
+
+
+
+
+
)
diff --git a/src/components/DetailsTransformations/detailsTransformations.scss b/src/components/DetailsTransformations/detailsTransformations.scss
index 3d93fd430d..03a7bd3b7b 100644
--- a/src/components/DetailsTransformations/detailsTransformations.scss
+++ b/src/components/DetailsTransformations/detailsTransformations.scss
@@ -4,6 +4,7 @@
.transformations-tab {
display: flex;
flex: 1 1;
+ height: 100%;
.graph-pane {
.config-item {
diff --git a/src/components/Documents/Documents.jsx b/src/components/Documents/Documents.jsx
index ee749e6d9b..524209128d 100644
--- a/src/components/Documents/Documents.jsx
+++ b/src/components/Documents/Documents.jsx
@@ -52,7 +52,7 @@ const Documents = ({ isAllVersions = false }) => {
isAllVersions={isAllVersions}
page={DOCUMENTS_PAGE}
removeArtifacts={removeDocuments}
- storeArtifactTypeLoading={artifactsStore.documents.modelLoading}
+ storeArtifactTypeLoading={artifactsStore.documents.documentLoading}
/>
)
}
diff --git a/src/components/Documents/documents.util.jsx b/src/components/Documents/documents.util.jsx
index 1fa571f1c9..4354cbc3bf 100644
--- a/src/components/Documents/documents.util.jsx
+++ b/src/components/Documents/documents.util.jsx
@@ -20,24 +20,23 @@ such restriction.
import React from 'react'
import { cloneDeep, isEmpty, omit } from 'lodash'
+import { ARTIFACT_MAX_DOWNLOAD_SIZE, DOCUMENT_TYPE, DOCUMENTS_PAGE } from '../../constants'
import {
- ARTIFACT_MAX_DOWNLOAD_SIZE,
- DOCUMENT_TYPE,
- DOCUMENTS_PAGE,
- FULL_VIEW_MODE
-} from '../../constants'
+ openDeleteConfirmPopUp,
+ openPopUp,
+ getErrorMsg,
+ copyToClipboard
+} from 'igz-controls/utils/common.util'
import { getIsTargetPathValid } from '../../utils/createArtifactsContent'
import { applyTagChanges, chooseOrFetchArtifact } from '../../utils/artifacts.util'
import { setDownloadItem, setShowDownloadsList } from '../../reducers/downloadReducer'
-import { copyToClipboard } from '../../utils/copyToClipboard'
import { generateUri } from '../../utils/resources'
import DeleteArtifactPopUp from '../../elements/DeleteArtifactPopUp/DeleteArtifactPopUp'
import { handleDeleteArtifact } from '../../utils/handleDeleteArtifact'
-import { openDeleteConfirmPopUp, openPopUp, getErrorMsg } from 'igz-controls/utils/common.util'
-import { FORBIDDEN_ERROR_STATUS_CODE } from 'igz-controls/constants'
+import { FORBIDDEN_ERROR_STATUS_CODE, FULL_VIEW_MODE } from 'igz-controls/constants'
import { convertChipsData } from '../../utils/convertChipsData'
import { updateArtifact } from '../../reducers/artifactsReducer'
-import { showErrorNotification } from '../../utils/notifications.util'
+import { showErrorNotification } from 'igz-controls/utils/notification.util'
import TagIcon from 'igz-controls/images/tag-icon.svg?react'
import YamlIcon from 'igz-controls/images/yaml.svg?react'
@@ -74,14 +73,14 @@ export const infoHeaders = [
{ label: 'Labels', id: 'labels' }
]
-export const generatePageData = viewMode => {
+export const generatePageData = (viewMode, isDetailsPopUp = false) => {
return {
page: DOCUMENTS_PAGE,
details: {
type: DOCUMENTS_PAGE,
menu: detailsMenu,
infoHeaders,
- hideBackBtn: viewMode === FULL_VIEW_MODE,
+ hideBackBtn: viewMode === FULL_VIEW_MODE && !isDetailsPopUp,
withToggleViewBtn: true
}
}
diff --git a/src/components/FeatureSetsPanel/FeatureSetsPanel.jsx b/src/components/FeatureSetsPanel/FeatureSetsPanel.jsx
index 63ad5ad080..dbbed3bcee 100644
--- a/src/components/FeatureSetsPanel/FeatureSetsPanel.jsx
+++ b/src/components/FeatureSetsPanel/FeatureSetsPanel.jsx
@@ -29,7 +29,7 @@ import arrayMutators from 'final-form-arrays'
import FeatureSetsPanelView from './FeatureSetsPanelView'
import { FEATURE_SETS_TAB, TAG_FILTER_LATEST } from '../../constants'
-import { setNotification } from '../../reducers/notificationReducer'
+import { setNotification } from 'igz-controls/reducers/notificationReducer'
import { checkValidation } from './featureSetPanel.util'
import { setFieldState } from 'igz-controls/utils/form.util'
import {
@@ -73,13 +73,13 @@ const FeatureSetsPanel = ({ closePanel, createFeatureSetSuccess, project }) => {
const [accessKeyRequired, setAccessKeyRequired] = useState(false)
const navigate = useNavigate()
const dispatch = useDispatch()
- const formRef = React.useRef(
- createForm({
+ const [form] = useState(() => {
+ return createForm({
initialValues: { labels: [] },
mutators: { ...arrayMutators, setFieldState },
onSubmit: () => {}
})
- )
+ })
const handleSave = () => {
let data = {
@@ -87,7 +87,7 @@ const FeatureSetsPanel = ({ closePanel, createFeatureSetSuccess, project }) => {
...featureStore.newFeatureSet,
metadata: {
...featureStore.newFeatureSet.metadata,
- labels: convertChipsData(formRef.current.getFieldState('labels')?.value),
+ labels: convertChipsData(form.getFieldState('labels')?.value),
tag: featureStore.newFeatureSet.metadata.tag || TAG_FILTER_LATEST
}
}
@@ -171,7 +171,7 @@ const FeatureSetsPanel = ({ closePanel, createFeatureSetSuccess, project }) => {
}
return createPortal(
-