diff --git a/web-console-v2/src/App.module.css b/web-console-v2/src/App.module.css index ed378209..226cd95a 100644 --- a/web-console-v2/src/App.module.css +++ b/web-console-v2/src/App.module.css @@ -11,6 +11,13 @@ max-height: calc(100vh - 3.7rem); } +.mainLoginContainer { + flex-grow: 1; + padding: 1rem 0 1rem 0; + transition: margin-left 0.3s ease; + max-height: calc(100vh - 3.7rem); +} + .expanded .mainContainer { margin-left: 16rem; width: 65rem; diff --git a/web-console-v2/src/App.tsx b/web-console-v2/src/App.tsx index a6cc303d..4352f93a 100644 --- a/web-console-v2/src/App.tsx +++ b/web-console-v2/src/App.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback, FC, lazy, useEffect } from 'react'; +import React, { useState, useCallback, FC, useEffect } from 'react'; import { QueryClientProvider } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import SideBar from 'components/Sidebar/Sidebar'; @@ -17,7 +17,7 @@ const useSidebarToggle = () => { const sidebarExpandValue = localStorage.getItem('sidebarExpand') const [isSidebarExpanded, setSidebarExpanded] = useState( - _.isEqual(localStorage.getItem('sidebarExpand'), "true") && window.location.pathname !== '/console/login' + _.isEqual(sidebarExpandValue, "true") ); const toggleSidebar = useCallback(() => { @@ -35,7 +35,7 @@ const useSidebarToggle = () => { const App: FC = () => { const { isSidebarExpanded, toggleSidebar } = useSidebarToggle(); - + const isLoginRoute = window.location.pathname.includes("/login") useEffect(() => { fetchSystemSettings(); }, []); @@ -45,13 +45,11 @@ const App: FC = () => { -
- +
+ {!isLoginRoute && ()} -
+
diff --git a/web-console-v2/src/components/@extended/CustomAlert.tsx b/web-console-v2/src/components/@extended/CustomAlert.tsx index a7e353bd..b1f852fe 100644 --- a/web-console-v2/src/components/@extended/CustomAlert.tsx +++ b/web-console-v2/src/components/@extended/CustomAlert.tsx @@ -43,7 +43,7 @@ const AlertComponent: React.FC = () => {
{context.show === true && ( - - diff --git a/web-console-v2/src/components/Cards/DatasetMetricsCard/DatasetMetricsCard.module.css b/web-console-v2/src/components/Cards/DatasetMetricsCard/DatasetMetricsCard.module.css new file mode 100644 index 00000000..a4dc0429 --- /dev/null +++ b/web-console-v2/src/components/Cards/DatasetMetricsCard/DatasetMetricsCard.module.css @@ -0,0 +1,57 @@ +.cardContainer { + width: 100%; + height: 100%; + padding-bottom: 0; + overflow: visible; +} + +.card { + box-shadow: var(--dashboard-card-box-shadow) !important; + border-radius: 1.125rem !important; + padding-bottom: 0; + z-index: -1; + height: 100%; +} + +.cardContent { + display: flex; + flex-grow: 1; + flex-direction: column; + gap: 1.5rem; + overflow: visible; +} + +.loadingText { + font-size: 1.7rem !important; +} + +.labelContainer { + display: flex !important; + justify-content: space-between; + align-items: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.label { + font-size: 1rem !important; + word-wrap: break-word; + white-space: normal; +} + +.symbol { + margin-left: auto; + text-align: center; + font-size: 1rem !important; +} + +.linearProgressContainer{ + display: flex; + align-items: center; +} + +.linearProgress{ + width: 100%; + margin-right: 0.5rem; +} \ No newline at end of file diff --git a/web-console-v2/src/components/Cards/DatasetMetricsCard/DatasetMetricsCard.tsx b/web-console-v2/src/components/Cards/DatasetMetricsCard/DatasetMetricsCard.tsx new file mode 100644 index 00000000..25cf78c5 --- /dev/null +++ b/web-console-v2/src/components/Cards/DatasetMetricsCard/DatasetMetricsCard.tsx @@ -0,0 +1,180 @@ +import * as React from 'react'; +import { useEffect, useMemo, useState } from 'react'; +import Card from '@mui/material/Card'; +import CardContent from '@mui/material/CardContent'; +import Typography from '@mui/material/Typography'; +import { ArrowUpward, ArrowDownward } from '@mui/icons-material'; +import { Box, Grid, Tooltip } from '@mui/material'; +import _ from 'lodash'; +import { fetchMetricData } from '../../../services/chartMetrics'; +import styles from "./DatasetMetricsCard.module.css" +import chartMeta from 'data/chartsV1'; +import dayjs from 'dayjs'; +import { druidQueries } from 'services/druid'; +import { useParams } from 'react-router-dom'; +import { useAlert } from 'contexts/AlertContextProvider'; +import { DatasetStatus, DatasetType } from 'types/datasets'; +import ApexWithFilters from 'sections/dashboard/analytics/ChartFilters'; +import ApexChart from 'sections/dashboard/analytics/apex'; +import filters from 'data/chartFilters'; + +const getQueryByType = (queryType: string, datasetId: any, isMasterDataset: boolean, interval: string) => { + const dateFormat = 'YYYY-MM-DDT00:00:00+05:30' + + switch (queryType) { + case 'status': { + return { ..._.get(chartMeta, 'druid_health_status.query') } + }; + case 'last_synced_time': { + const startDate = '2000-01-01'; + const endDate = dayjs().add(1, 'day').format(dateFormat); + const body = druidQueries.last_synced_time({ datasetId, intervals: `${startDate}/${endDate}`, master: isMasterDataset, }) + return { ..._.get(chartMeta, 'last_synced_time.query'), body } + }; + + case 'total_events_processed': { + const startDate = interval === 'today' ? dayjs().format(dateFormat) : dayjs().subtract(1, 'day').format(dateFormat); + const endDate = interval === 'today' ? dayjs().add(1, 'day').format(dateFormat) : dayjs().format(dateFormat); + const body = druidQueries.total_events_processed({ datasetId, intervals: `${startDate}/${endDate}`, master: isMasterDataset, }) + return { ..._.get(chartMeta, 'total_events_processed.query'), body } + }; + + case 'min_processing_time': { + const startDate = interval === 'today' ? dayjs().format(dateFormat) : dayjs().subtract(1, 'day').format(dateFormat); + const endDate = interval === 'today' ? dayjs().add(1, 'day').format(dateFormat) : dayjs().format(dateFormat); + const body = druidQueries.druid_min_processing_time({ datasetId, intervals: `${startDate}/${endDate}`, master: isMasterDataset, }); + return { ..._.get(chartMeta, 'minProcessingTime.query'), body } + }; + + case 'average_processing_time': { + const startDate = interval === 'today' ? dayjs().format(dateFormat) : dayjs().subtract(1, 'day').format(dateFormat); + const endDate = interval === 'today' ? dayjs().add(1, 'day').format(dateFormat) : dayjs().format(dateFormat); + const body = druidQueries.druid_avg_processing_time({ datasetId, intervals: `${startDate}/${endDate}`, master: isMasterDataset, }); + return { ..._.get(chartMeta, 'avgProcessingTime.query'), body } + }; + + case 'max_processing_time': { + const startDate = interval === 'today' ? dayjs().format(dateFormat) : dayjs().subtract(1, 'day').format(dateFormat); + const endDate = interval === 'today' ? dayjs().add(1, 'day').format(dateFormat) : dayjs().format(dateFormat); + const body = druidQueries.druid_max_processing_time({ datasetId, intervals: `${startDate}/${endDate}`, master: isMasterDataset, }); + return { ..._.get(chartMeta, 'maxProcessingTime.query'), body } + }; + + case 'total_duplicate_batches': { + const endDate = interval === 'today' ? dayjs().endOf('day').unix() : dayjs().endOf('day').subtract(1, 'day').unix(); + return { ..._.get(chartMeta, 'duplicate_batches_summary.query'), time: endDate, dataset: datasetId, } + }; + + case 'total_duplicate_events': { + const endDate = interval === 'today' ? dayjs().endOf('day').unix() : dayjs().format(dateFormat);; + return { ..._.get(chartMeta, 'duplicate_events_summary.query'), time: endDate, dataset: datasetId, } + }; + + case 'total_failed_events': { + const endDate = interval === 'today' ? dayjs().endOf('day').unix() : dayjs().endOf('day').subtract(1, 'day').unix(); + const metadata = isMasterDataset ? + _.cloneDeep({ ..._.get(chartMeta, 'failed_events_summary_master_datasets.query'), time: endDate, dataset: datasetId, }) : + _.cloneDeep({ ..._.get(chartMeta, 'failed_events_summary.query'), time: endDate, dataset: datasetId, }); + return metadata; + }; + + case 'total_events_processed_apex_charts': { + const metadata = isMasterDataset ? + _.cloneDeep(_.get(chartMeta, 'totalEventsProcessedTimeSeriesPerMasterDataset')) : + _.cloneDeep(_.get(chartMeta, 'totalEventsProcessedTimeSeriesPerDataset')); + _.set(metadata, 'query.body.query.filter.fields[1].value', datasetId); + return metadata; + }; + + case 'events_processing_time_apex_charts': { + const metadata = isMasterDataset ? + _.cloneDeep(_.get(chartMeta, 'minProcessingTimeSeriesPerMasterDataset')) : + _.cloneDeep(_.get(chartMeta, 'minProcessingTimeSeriesPerDataset')); + _.set(metadata, 'query.body.query.filter.fields[1].value', datasetId); + return metadata; + }; + + default: + throw new Error(`Unknown query type: ${queryType}`); + } +}; + +const DatasetMetricsCard: React.FC = (props: any) => { + const params = useParams(); + const { datasetId, datasetType } = params; + const [datasetDetails, setDatasetDetails] = useState({ data: null, status: "loading" }); + const { showAlert } = useAlert(); + const isMasterDataset = useMemo(() => _.get(datasetDetails, 'data.type') === DatasetType.MasterDataset, [datasetDetails]); + const hasBatchConfig = useMemo(() => _.get(datasetDetails, ['data', 'extraction_config', 'is_batch_event',]), [datasetDetails]); + const { label, icon, queryType, uuid, transformer, description, refresh, interval, isApexChart } = props; + const query = getQueryByType(queryType, datasetId, isMasterDataset, interval); + const [value, setValue] = useState(''); + const [loading, setLoading] = useState(false); + const change = '0%'; + const isPositive = change.startsWith('+'); + const symbol = isPositive ? ( + + ) : ( + + ); + + const fetchMetric = async (query: any) => { + try { + setLoading(true); + const response = await fetchMetricData(query, { uuid }); + const transformedLabel = + (await (transformer && transformer(response))) || response; + setValue(response); + setLoading(false); + } catch (error) { + console.log('error occured', error); + } + }; + + useEffect(() => { + fetchMetric(query); + }, [refresh?.api]); + + return ( + !isApexChart ? + + + + + {icon} + + {loading ? 'Loading...' : _.isArray(value) ? value[0] : value} + + + + + {label} + + + {/* + {symbol} {_.trimStart(change, '+-')} + */} + + + + + + : + + + + ); +}; + +export default DatasetMetricsCard; diff --git a/web-console-v2/src/components/Sidebar/Sidebar.tsx b/web-console-v2/src/components/Sidebar/Sidebar.tsx index 5676c5f0..04edbe97 100644 --- a/web-console-v2/src/components/Sidebar/Sidebar.tsx +++ b/web-console-v2/src/components/Sidebar/Sidebar.tsx @@ -134,7 +134,8 @@ const Sidebar: React.FC = ({ onExpandToggle, expand }) => { const handleLogout = () => { http.get(apiEndpoints.logout).then(() => { localStorage.clear(); - navigate(`/login`); + //navigate(`/login`); + window.location.href = '/console/login' }).catch(() => { showAlert('Failed to logout', 'error'); }) diff --git a/web-console-v2/src/constants/Queries.ts b/web-console-v2/src/constants/Queries.ts index b1ac7c77..e23cae23 100644 --- a/web-console-v2/src/constants/Queries.ts +++ b/web-console-v2/src/constants/Queries.ts @@ -17,7 +17,7 @@ export default { }, api_failure_percentage: { query: - '((sum_over_time(sum by (job) (node_failed_api_calls)[$interval:30s]) / sum_over_time(sum by (job) (node_total_api_calls{entity="data-out"})[$interval:30s]))*100)', + '((sum_over_time(sum by (job) (node_failed_api_calls{entity=~"data-in|data-out"})[$interval:30s]) / sum_over_time(sum by (job) (node_total_api_calls{entity=~"data-in|data-out"})[$interval:30s]))*100)' }, node_query_response_time_max: { query: 'max_over_time(max by (job) (node_query_response_time)[1134m:5m])', diff --git a/web-console-v2/src/data/chartsComponents/druid.ts b/web-console-v2/src/data/chartsComponents/druid.ts index fccd71cc..0773ddb5 100644 --- a/web-console-v2/src/data/chartsComponents/druid.ts +++ b/web-console-v2/src/data/chartsComponents/druid.ts @@ -78,10 +78,8 @@ export default { parse: (response: any) => { const ms = _.get(response, 'result[0].event.last_synced_time') || 0; if (!ms) return ["N/A", "primary"]; - return { - "value": dayjs(ms).fromNow(), - "hoverValue": dayjs(ms).format('YYYY-MM-DD HH:mm:ss') - } + const timeAgo = dayjs(ms).fromNow(); + return timeAgo }, error() { return ["N/A", "error"] @@ -100,10 +98,8 @@ export default { parse: (response: any) => { const ms = _.get(response, 'result[0].event.last_synced_time') || 0; if (!ms) return ["N/A", "primary"]; - return { - "value": dayjs(ms).fromNow(), - "hoverValue": dayjs(ms).format('YYYY-MM-DD HH:mm:ss') - } + const timeAgo = dayjs(ms).fromNow(); + return timeAgo }, error() { return ["N/A", "error"] diff --git a/web-console-v2/src/data/chartsV1/api.ts b/web-console-v2/src/data/chartsV1/api.ts index 2d38839d..abece790 100644 --- a/web-console-v2/src/data/chartsV1/api.ts +++ b/web-console-v2/src/data/chartsV1/api.ts @@ -186,7 +186,7 @@ export default { const endpoint = _.get(payload, 'metric.exported_endpoint'); const name = (endpoint && _.last(_.split(endpoint, "/"))); return { - name: name || 'Total Api Calls', + name: name || 'Total API Calls', data: _.get(payload, 'values') } }) @@ -278,7 +278,7 @@ export default { const endpoint = _.get(payload, 'metric.exported_endpoint'); const name = (endpoint && _.last(_.split(endpoint, "/"))); return { - name: name || 'Total Failed Api Calls', + name: name || 'Total Failed API Calls', data: _.get(payload, 'values') } }) @@ -370,7 +370,7 @@ export default { const endpoint = _.get(payload, 'metric.exported_endpoint'); const name = (endpoint && _.last(_.split(endpoint, "/"))); return { - name: name || 'Total Api Calls', + name: name || 'Total API Calls', data: _.get(payload, 'values') } }) @@ -462,7 +462,7 @@ export default { const endpoint = _.get(payload, 'metric.exported_endpoint'); const name = (endpoint && _.last(_.split(endpoint, "/"))); return { - name: name || 'Total Failed Api Calls', + name: name || 'Total Failed API Calls', data: _.get(payload, 'values') } }) diff --git a/web-console-v2/src/data/chartsV1/druid.ts b/web-console-v2/src/data/chartsV1/druid.ts index af6d924f..1373727d 100644 --- a/web-console-v2/src/data/chartsV1/druid.ts +++ b/web-console-v2/src/data/chartsV1/druid.ts @@ -78,10 +78,8 @@ export default { parse: (response: any) => { const ms = _.get(response, 'result[0].event.last_synced_time') || 0; if (!ms) return ["N/A", "primary"]; - return { - "value": dayjs(ms).fromNow(), - "hoverValue": dayjs(ms).format('YYYY-MM-DD HH:mm:ss') - } + const timeAgo = dayjs(ms).fromNow(); + return timeAgo }, error() { return ["N/A", "error"] @@ -100,10 +98,8 @@ export default { parse: (response: any) => { const ms = _.get(response, 'result[0].event.last_synced_time') || 0; if (!ms) return ["N/A", "primary"]; - return { - "value": dayjs(ms).fromNow(), - "hoverValue": dayjs(ms).format('YYYY-MM-DD HH:mm:ss') - } + const timeAgo = dayjs(ms).fromNow(); + return timeAgo }, error() { return ["N/A", "error"] diff --git a/web-console-v2/src/data/chartsV1/storage.ts b/web-console-v2/src/data/chartsV1/storage.ts index d646005d..40f3687e 100644 --- a/web-console-v2/src/data/chartsV1/storage.ts +++ b/web-console-v2/src/data/chartsV1/storage.ts @@ -204,10 +204,9 @@ export default { parse: (response: any) => { const result = _.get(response, 'data.result[0].value[1]'); if (!result) throw new Error(); - return { - "value": dayjs(result * 1000).fromNow(), - "hoverValue": dayjs(result * 1000).format('YYYY-MM-DD HH:mm:ss') - } + const date = dayjs().subtract(result * 1000, 'milliseconds'); + const timeAgo = date.fromNow(); + return timeAgo }, error() { return prettyMilliseconds(0) @@ -228,10 +227,9 @@ export default { parse: (response: any) => { const result = _.get(response, 'data.result[0].value[1]'); if (!result) throw new Error(); - return { - "value": dayjs(result * 1000).fromNow(), - "hoverValue": dayjs(result * 1000).format('YYYY-MM-DD HH:mm:ss') - } + const date = dayjs().subtract(result * 1000, 'milliseconds'); + const timeAgo = date.fromNow(); + return timeAgo }, error() { return prettyMilliseconds(0) diff --git a/web-console-v2/src/pages/ConnectorConfiguration/ConnectorConfiguration.tsx b/web-console-v2/src/pages/ConnectorConfiguration/ConnectorConfiguration.tsx index d591708b..a00c21fc 100644 --- a/web-console-v2/src/pages/ConnectorConfiguration/ConnectorConfiguration.tsx +++ b/web-console-v2/src/pages/ConnectorConfiguration/ConnectorConfiguration.tsx @@ -10,7 +10,6 @@ import React, { useEffect, useState } from 'react'; import { useLocation, useNavigate, useParams } from 'react-router-dom'; import { endpoints, useFetchDatasetsById, useReadConnectors, useUpdateDataset } from 'services/dataset'; import styles from './ConnectorConfiguration.module.css'; -import sampleSchema from './Schema'; import { customizeValidator } from '@rjsf/validator-ajv8'; import { RJSFSchema, UiSchema } from '@rjsf/utils'; import { http } from 'services/http'; @@ -51,7 +50,7 @@ const ConnectorConfiguration: React.FC = () => { const [connectorType, setConnectorType] = useState("stream"); const [connectorHelpText, setConnectorHelpText] = useState(null); const [isHelpSectionOpen, setIsHelpSectionOpen] = useState(true); - const [schema, setSchema] = useState(sampleSchema); + const [schema, setSchema] = useState(null); const [highlightedSection, setHighlightedSection] = useState(null); const navigate = useNavigate(); @@ -78,7 +77,7 @@ const ConnectorConfiguration: React.FC = () => { const handleFormDataChange = (data: FormData) => { - const valid = ajv.validate(schema.schema, data); + const valid = ajv.validate(schema ? schema.schema : {}, data); if (valid) { setFormData(data) setFormErrors([]); @@ -96,18 +95,18 @@ const ConnectorConfiguration: React.FC = () => { properties: { [sectionKey]: sectionValue as RJSFSchema }, - required: schema.schema.required && schema.schema.required.includes(sectionKey) ? [sectionKey] : [] + required: schema && schema.schema.required && schema.schema.required.includes(sectionKey) ? [sectionKey] : [] } return fieldSchema; } const getUISchema = (sectionKey: string) => { - if (schema.uiSchema[sectionKey]) { + if (schema && schema.uiSchema[sectionKey]) { return { [sectionKey]: schema.uiSchema[sectionKey] } } else { - const sectionValue: any = schema.schema.properties?.[sectionKey]; + const sectionValue: any = schema?.schema.properties?.[sectionKey]; if (typeof sectionValue === 'object' && 'format' in sectionValue && sectionValue.format === 'password') { return { [sectionKey]: { @@ -337,11 +336,11 @@ const ConnectorConfiguration: React.FC = () => { <> - {schema.title} + {schema?.title} - {schema.schema.properties && _.sortBy(_.entries(schema.schema.properties), [([, value]) => (value as any).uiIndex]).map(([sectionKey, sectionValue]) => { + {schema && schema.schema.properties && _.sortBy(_.entries(schema.schema.properties), [([, value]) => (value as any).uiIndex]).map(([sectionKey, sectionValue]) => { return ( { const { id } = props; const navigate = useNavigate(); const [metadata, setmetadata] = useState>(); const metricId = id + const params = useParams(); + const { datasetId } = params; const navigateToHome = ({ errMsg }: any) => { navigate('/'); @@ -101,14 +104,18 @@ const IndividualMetricDashboards = (props: any) => { justifyContent="flex-start" alignItems="center" spacing={2}> -
- {`${metadata?.primaryLabel || ""} Metrics `} -
+ + {!datasetId ? `${metadata?.primaryLabel || ""} Metrics ` : `${datasetId}`} + {health && } - - - - + {!datasetId + ? + + + + : + <> + } @@ -116,13 +123,25 @@ const IndividualMetricDashboards = (props: any) => { } return ( - <> + + {datasetId && + + } {renderCharts(metadata)} - + ) }; diff --git a/web-console-v2/src/pages/Dashboard/metrics.tsx b/web-console-v2/src/pages/Dashboard/metrics.tsx index 5d56dc11..2e3646db 100644 --- a/web-console-v2/src/pages/Dashboard/metrics.tsx +++ b/web-console-v2/src/pages/Dashboard/metrics.tsx @@ -14,6 +14,7 @@ import MetricsCard from "components/Cards/MetricsCard/MetricsCard"; import IngestionCharts from "sections/dashboard/analytics/IngestionCharts"; import HoursSinceLastBackup from "sections/widgets/HoursSinceLastBackup"; import StorageMetricsCard from "components/Cards/StorageMetricCard"; +import DatasetMetricsCard from "components/Cards/DatasetMetricsCard/DatasetMetricsCard"; export const metricsMetadata = [ { @@ -221,22 +222,22 @@ export const metricsMetadata = [ { id: "apiHealth", description: "Shows the Http Requests Health. If the API failure percentage is above 1%, then it's Unhealthy", - chart: () + chart: () }, { id: "apiResponseTime", description: "Shows the API Response time for today", - chart: + chart: }, { id: "apiMaxResponseTime", description: "Shows the max API Response time for today", - chart: + chart: }, { id: "apiFailurePercentage", description: "Shows the api failure percentage for today", - chart: + chart: }, { id: "apiFiftyPercentile", @@ -305,22 +306,22 @@ export const metricsMetadata = [ { id: "apiHealth", description: "Shows the Http Requests Health. If the API failure percentage is above 1%, then it's Unhealthy", - chart: + chart: }, { id: "apiResponseTime", description: "Shows the API Response time for today", - chart: + chart: }, { id: "apiMaxResponseTime", description: "Shows the max API Response time for today", - chart: + chart: }, { id: "apiFailurePercentage", description: "Shows the api failure percentage for today", - chart: + chart: }, { id: "apiFiftyPercentile", @@ -714,5 +715,148 @@ export const metricsMetadata = [ ] } } + }, + { + id: "individualDataset", + primaryLabel: "Dataset", + secondaryLabel: "Metrics", + description: "This page shows the metrics of datasets processing. With this information you can monitor the processing time and throughput of the events.", + color: 'main', + charts: { + xs: { + size: { + xs: 12, + sm: 6, + md: 4, + lg: 4 + }, + metadata: [ + ] + }, + small: { + size: { + xs: 12, + sm: 6, + md: 4, + lg: 4 + }, + groups: [ + { + title: "Dataset Status", + metadata: [ + { + id: "Status", + description: "Status", + chart: + }, + { + id: "Last Synced Time", + description: "Last Synced Time", + chart: + } + ] + }, + { + title: "Today", + metadata: [ + { + id: "Total Events Processed", + description: "Total Events Processed", + chart: + }, + { + id: "Min Processing Time", + description: "Min Processing Time", + chart: + }, + { + id: "Average Processing Time", + description: "Average Processing Time", + chart: + }, + { + id: "Max Processing Time", + description: "Max Processing Time", + chart: + }, + { + id: "Total Duplicate Batches", + description: "Total Duplicate Batches", + chart: + }, + { + id: "Total Duplicate Events", + description: "Total Duplicate Events", + chart: + }, + { + id: "Total Failed Events", + description: "Total Failed Events", + chart: + }, + ] + }, + { + title: "Yesterday", + metadata: [ + { + id: "Total Events Processed", + description: "Total Events Processed", + chart: + }, + { + id: "Min Processing Time", + description: "Min Processing Time", + chart: + }, + { + id: "Average Processing Time", + description: "Average Processing Time", + chart: + }, + { + id: "Max Processing Time", + description: "Max Processing Time", + chart: + }, + { + id: "Total Duplicate Batches", + description: "Total Duplicate Batches", + chart: + }, + { + id: "Total Duplicate Events", + description: "Total Duplicate Events", + chart: + }, + { + id: "Total Failed Events", + description: "Total Failed Events", + chart: + }, + ] + } + ], + }, + medium: { + size: { + xs: 12, + sm: 6, + md: 6, + lg: 6 + }, + metadata: [ + { + id: "Total Events Processed", + description: "This is a graphical representation of the total events processed by the dataset", + chart: + }, + { + id: "Events Processing Time (ms)", + chart: + } + ] + }, + } } ] diff --git a/web-console-v2/src/pages/SelectConnector/SelectConnector.tsx b/web-console-v2/src/pages/SelectConnector/SelectConnector.tsx index 28f51902..169bd617 100644 --- a/web-console-v2/src/pages/SelectConnector/SelectConnector.tsx +++ b/web-console-v2/src/pages/SelectConnector/SelectConnector.tsx @@ -119,15 +119,12 @@ const SelectConnector = () => { return ( <> {isPending ? ( - + ) : (
{t('selectConnector.chooseConnectors')} - - {t('selectConnector.addDataSource')} - >; - onChange: (formData: FormData, errors?: unknown[] | null) => void; -} - const GenericCard = styled(Card)(({ theme }) => ({ outline: 'none', boxShadow: 'none', @@ -287,9 +281,11 @@ const Ingestion = () => { const { schema } = generateData; const filePaths = _.map(uploadData, 'filePath'); let mergedEvent = {}; - if (data) { - _.map(data, (item: any) => { - mergedEvent = _.merge(mergedEvent, item); + if (data.length > 0) { + _.forEach(data, (item: any) => { + if(!isJsonSchema(item)) { + mergedEvent = _.merge(mergedEvent, item); + } }); } if (datasetIdParam === '' && datasetId) { @@ -430,7 +426,6 @@ const Ingestion = () => { } else { setNameError('The field should exclude any special characters, permitting only alphabets, numbers, ".", "-", and "_".'); } - console.log("### datasetName", datasetName) }; return ( @@ -448,7 +443,7 @@ const Ingestion = () => { isGenerateLoading || isUpdateLoading } - descriptionText="Loading the page" + descriptionText="Please wait while we process your request." /> {!( diff --git a/web-console-v2/src/pages/StepsPages/Ingestion/SchemaDetails/SchemaDetails.tsx b/web-console-v2/src/pages/StepsPages/Ingestion/SchemaDetails/SchemaDetails.tsx index 51e94e8a..abec530c 100644 --- a/web-console-v2/src/pages/StepsPages/Ingestion/SchemaDetails/SchemaDetails.tsx +++ b/web-console-v2/src/pages/StepsPages/Ingestion/SchemaDetails/SchemaDetails.tsx @@ -633,7 +633,7 @@ const SchemaDetails = (props: { showTableOnly?: boolean }) => { > {!( diff --git a/web-console-v2/src/pages/StepsPages/PreviewAndSave/AllConfigurations.tsx b/web-console-v2/src/pages/StepsPages/PreviewAndSave/AllConfigurations.tsx index 06f34e52..d8832606 100644 --- a/web-console-v2/src/pages/StepsPages/PreviewAndSave/AllConfigurations.tsx +++ b/web-console-v2/src/pages/StepsPages/PreviewAndSave/AllConfigurations.tsx @@ -241,7 +241,7 @@ const AllConfigurations = () => { return ( - {(response.isPending) ? : + {(response.isPending) ? : diff --git a/web-console-v2/src/pages/StepsPages/PreviewAndSave/Preview.tsx b/web-console-v2/src/pages/StepsPages/PreviewAndSave/Preview.tsx index 574f78c6..a1db66a9 100644 --- a/web-console-v2/src/pages/StepsPages/PreviewAndSave/Preview.tsx +++ b/web-console-v2/src/pages/StepsPages/PreviewAndSave/Preview.tsx @@ -98,7 +98,7 @@ const Preview: FC = (): ReactElement => { > { publishDataset.isPending ? ( - + ) : ( publishDataset.isError ? ( { const { datasetId }:any = useParams(); - const { data } = useFetchDatasetDiff({ + const { data, isPending } = useFetchDatasetDiff({ datasetId }); @@ -32,75 +40,93 @@ const ReviewDataset = () => { ); }; - const sections = [ - ...(additions?.length - ? [ - { - id: 'additions', - componentType: 'box', - title: ( - - Additional Parameters - - ), - description: 'Lists down all the additions in the configurations', - component: - } - ] - : []), - ...(modifications?.length - ? [ - { - id: 'updates', - componentType: 'box', - title: ( - - Modified Parameters - - ), - description: - 'Lists down all the modifications in the configuration along with new and old value', - component: - } - ] - : []), - ...(deletions?.length - ? [ - { - id: 'deletion', - componentType: 'box', - title: ( - - {' '} - Deleted Parameters - - ), - description: 'Lists down all the deletions in the configurations', - component: - } - ] - : []) - ]; + const [expanded, setExpanded] = React.useState('added'); - const render = () => { - if (noModifications) { - return ( - - {en['no-summary-modifications']} - - ); - } - return ( - - {renderSections({ sections: sections })} - {en['dataset-summary-review-warning']} - - ); + const handleChange = (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => { + setExpanded(newExpanded ? panel : false); }; + const Accordion = styled((props: AccordionProps) => ( + + ))(({ theme }) => ({ + border: `1px solid ${theme.palette.divider}`, + '&:not(:last-child)': { + borderBottom: 0, + }, + '&::before': { + display: 'none', + }, + })); + + const AccordionSummary = styled((props: AccordionSummaryProps) => ( + } + {...props} + /> + ))(({ theme }) => ({ + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, .05)' + : 'rgba(0, 0, 0, .03)', + flexDirection: 'row-reverse', + '& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': { + transform: 'rotate(90deg)', + }, + '& .MuiAccordionSummary-content': { + marginLeft: theme.spacing(1), + }, + })); + + const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({ + padding: theme.spacing(2), + borderTop: '1px solid rgba(0, 0, 0, .125)', + })); + + return ( <> - {render()} + {(isPending) ? : + <> + {noModifications && ( + + {en['no-summary-modifications']} + + )} + {!noModifications && ( + <> + + + + Added + + {additions?.length && ( + + )} + + + + Modified + + {modifications?.length && ( + + )} + + + + Deleted + + {deletions?.length && ( + + )} + + + + {en['dataset-summary-review-warning']} + + + )} + + } ); }; diff --git a/web-console-v2/src/pages/StepsPages/Processing/Processing.tsx b/web-console-v2/src/pages/StepsPages/Processing/Processing.tsx index e654fd3b..176cddcc 100644 --- a/web-console-v2/src/pages/StepsPages/Processing/Processing.tsx +++ b/web-console-v2/src/pages/StepsPages/Processing/Processing.tsx @@ -255,11 +255,17 @@ const Processing: React.FC = () => { { label: 'Lenient', component: '', value: TransformationMode.Lenient } ]; - const piiColumns:any = _.map(_.get(processingData, 'pii'), 'column'); - // Filter out items from transformationOptions where the column matches any in piiColumns - const transformationOptionsWithoutPii = _.filter(transformationOptions, (ele: any) => { - return !piiColumns.includes(ele); - }); + const piiColumns: any = _.map(_.get(processingData, 'pii'), 'column'); + const derivedColumns: any = _.map(_.get(processingData, 'derived'), 'column'); + + const columnsToExcludeInTransformation = _.union(piiColumns, derivedColumns); + + function filterTransformationOptions( + transformationOptions: any[], + columnsToExclude: any[] + ): any[] { + return _.filter(transformationOptions, (ele: any) => !columnsToExclude.includes(ele)); + } const processingSections = [ { @@ -316,10 +322,7 @@ const Processing: React.FC = () => { transformation_mode={transformation_mode} label={'Add Sensitive Field'} dialog={} - transformationOptions={_.union( - transformationOptions, - _.map(piiSuggestions, 'column') - )} + transformationOptions={transformationOptions} addedSuggestions={piiSuggestions} data={_.map(_.get(processingData, 'pii'), (obj1) => { const matchingObj = _.find(piiSuggestions, { @@ -354,7 +357,7 @@ const Processing: React.FC = () => { label={'Add Transformation'} dialog={} jsonData={jsonData} - transformationOptions={transformationOptionsWithoutPii} + transformationOptions={filterTransformationOptions(transformationOptions, columnsToExcludeInTransformation)} addedSuggestions={[]} data={_.get(processingData, 'transform')} handleAddOrEdit={(data: any) => handleAddOrEdit(data, 'transformations')} @@ -378,7 +381,7 @@ const Processing: React.FC = () => { actions={[{ label: 'JSONata', component: '', value: 'custom' }]} transformation_mode={transformation_mode} label={'Add Derived Field'} - dialog={} + dialog={} jsonData={jsonData} transformationOptions={transformationOptions} addedSuggestions={[]} @@ -446,7 +449,7 @@ const Processing: React.FC = () => { { (datasetList.isPending) ? - + : { - const { data, handleAddOrEdit, onClose, edit = false, transformationOptions, jsonData } = props; + const { data, handleAddOrEdit, onClose, edit = false, transformationOptions, jsonData, } = props; const [anchorEl, setAnchorEl] = useState(null); const [evaluationData, setEvaluationData] = useState(''); const [stateId, setStateId] = useState(uuidv4()) const [transformErrors, setTransformErrors] = useState(false); - const [extraErrors, setExtraErrors] = useState({}); const open = Boolean(anchorEl); - const [formErrors, setFormErrors] = useState([]); - const [formData, setFormData] = useState<{ [key: string]: any }>({}); - - if (!_.isEmpty(transformationOptions)) - _.set( - schema, - ['schema', 'properties', 'section', 'properties', 'transformations', 'enum'], - transformationOptions - ); + const [formData, setFormData] = useState({ + section: { + transformations: '', + transformationType: '', + transformationMode: 'Strict' + } + }); useEffect(() => { if (!_.isEmpty(data)) { - const type = _.get(data, ['transformationType']); - + const type = _.get(data, ['transformation']); const existingData = { section: { transformations: _.get(data, ['column']), - transformationType: _.isEqual(type, 'custom') ? 'jsonata' : type, + transformationType: type, transformationMode: _.get(data, ['transformationMode']), expression: _.get(data, ['transformation']) } @@ -82,15 +86,16 @@ const AddTransformationExpression = (props: any) => { }; const handleClose = () => { - - const newData = { - ...formData - }; - const keyPath = ['section', 'expression']; - _.set(newData, keyPath, evaluationData); - setStateId(uuidv4()) - setFormData(newData); - setFormErrors([]) + if (!transformationTypeError) { + setFormData((prevState: any) => ({ + ...prevState, + section: { + ...prevState.section, + transformationType: evaluationData + } + })); + } + setAnchorEl(null); }; @@ -98,16 +103,9 @@ const AddTransformationExpression = (props: any) => { setAnchorEl(null); }; - const isJsonata = true; - - const jsonataData = _.get(formData, ['section', 'expression']); - const onHandleClick = async () => { const newData = _.get(formData, ['section']); - console.log("#### newData", newData) - const array = []; - if (!_.isEmpty(data)) { array.push({ value: { field_key: _.get(data, ['column']) }, @@ -120,7 +118,7 @@ const AddTransformationExpression = (props: any) => { field_key: _.get(newData, ['transformations'], ''), transformation_function: { type: 'jsonata', - expr: _.get(newData, ['expression'], ''), + expr: _.get(newData, ['transformationType'], ''), category: 'transform' }, mode: _.get(newData, ['transformationMode'], '') @@ -128,49 +126,65 @@ const AddTransformationExpression = (props: any) => { action: 'upsert' }; - const datatype = await evaluateDataType(_.get(newData, ['expression'], ''), jsonData); - _.set( - obj, - ['value', 'transformation_function', 'datatype'], - _.get(datatype, 'data_type') - ); + try { + const datatype = await evaluateDataType(_.get(newData, ['transformationType'], ''), jsonData); + _.set( + obj, + ['value', 'transformation_function', 'datatype'], + _.get(datatype, 'data_type') + ); - array.push(obj); - handleAddOrEdit(array); - onClose(); + array.push(obj); + handleAddOrEdit(array); + onClose(); + } + catch (error) { + const message = _.get(error, 'message', 'Invalid transformation type'); + setTransformationTypeError(message); + } }; - const handleChange: TransformationFormProps['onChange'] = async (formData, errors) => { - const expression = _.get(formData, ['section', 'expression']); - const newExtraErrors = { - section: { - expression: { - __errors: [] - } + const transformationType = _.get(formData, ['section', 'transformationType']); + const handleErrors = async () => { + if (transformationType) { + try { + await evaluateDataType(transformationType, jsonData); + setTransformationTypeError(null); + setStateId(uuidv4()); + } catch (error) { + const message = _.get(error, 'message', 'Invalid transformation type'); + setTransformationTypeError(message); } - }; + } + }; - if (errors) { - setFormErrors(errors); - } else { - setFormErrors([]); + useEffect(() => { + handleErrors() + if (_.isEmpty(transformationType)) { + setTransformationTypeError(null) } + }, [transformationType]) - if (expression) { - try { - await evaluateDataType(expression, jsonData); - setExtraErrors(newExtraErrors); - setStateId(uuidv4()) - } catch (error) { - console.log("#### error", error) - const message = _.get(error, 'message'); + const [transformationTypeError, setTransformationTypeError] = useState(null); + const handleInputChange: any = (event: React.ChangeEvent) => { + const { name, value } = event.target; + setFormData((prevState: any) => ({ + ...prevState, + section: { + ...prevState.section, + [name]: value + } + })); + }; - _.set(newExtraErrors, ['section', 'expression', '__errors', 0], message); - setFormErrors([message]); + const handleRadioChange = (event: React.ChangeEvent) => { + setFormData((prevState: any) => ({ + ...prevState, + section: { + ...prevState.section, + transformationMode: event.target.value } - } - setExtraErrors(newExtraErrors); - setFormData(formData); + })); }; return ( @@ -198,21 +212,55 @@ const AddTransformationExpression = (props: any) => { ) : null} - - + + + + Select Field + + + + + + + + + Skip On Transformation Failure? + + + } label="Yes" /> + } label="No" /> + + + + @@ -220,7 +268,6 @@ const AddTransformationExpression = (props: any) => { - } diff --git a/web-console-v2/src/pages/alertManager/views/AlertRules.tsx b/web-console-v2/src/pages/alertManager/views/AlertRules.tsx index 645cfa6c..528fa967 100644 --- a/web-console-v2/src/pages/alertManager/views/AlertRules.tsx +++ b/web-console-v2/src/pages/alertManager/views/AlertRules.tsx @@ -33,6 +33,7 @@ const AlertRules = () => { const { label, id, path } = fields; return { }, []) const publishDraftDataset = async (datasetId: string) => { + showAlert(en['dataset-publish-inprogress'], "info") await publishDataset({ data: { datasetId } }); showAlert(en['dataset-publish-success'], "success") getDatasets(); diff --git a/web-console-v2/src/pages/dashboardV1/datasets.tsx b/web-console-v2/src/pages/dashboardV1/datasets.tsx index dc2e0516..de012d79 100644 --- a/web-console-v2/src/pages/dashboardV1/datasets.tsx +++ b/web-console-v2/src/pages/dashboardV1/datasets.tsx @@ -124,7 +124,16 @@ const ClusterHealth = () => { return } diff --git a/web-console-v2/src/router/index.tsx b/web-console-v2/src/router/index.tsx index 6ae0a77f..842ad9e3 100644 --- a/web-console-v2/src/router/index.tsx +++ b/web-console-v2/src/router/index.tsx @@ -87,7 +87,7 @@ export const routeConfigurations: RouteConfig[] = [ { path: `/alertChannels/edit/:id`, label: "Edit", element: }, { path: `/alertChannels/view/:id`, label: "View", element: }, { path: `/datasets`, label: "Datasets", element: }, - { path: `/datasets/metrics/:datasetId`, label: "Metrics", element: }, + { path: `/datasets/metrics/:datasetId`, label: "Metrics", element: }, { path: `/datasets/addEvents/:datasetId`, label: "Add Events", element: }, { path: `/datasets/view/:datasetId`, label: "View", element: }, { path: `/datasets/rollups/:datasetId`, element: }, diff --git a/web-console-v2/src/services/dataset.ts b/web-console-v2/src/services/dataset.ts index 11452b21..bda73fce 100644 --- a/web-console-v2/src/services/dataset.ts +++ b/web-console-v2/src/services/dataset.ts @@ -7,22 +7,6 @@ import { generateRequestBody, setDatasetId, setVersionKey, transformResponse } f import { queryClient } from 'queryClient'; import { DatasetStatus } from 'types/datasets'; import { generateDatasetState } from './datasetState'; -import apiEndpoints from 'data/apiEndpoints'; - -// const ENDPOINTS = { -// DATASETS_READ: '/console/config/v2/datasets/read', -// CREATE_DATASET: '/console/config/v2/datasets/create', -// UPLOAD_FILES: '/console/config/v2/files/generate-url', -// GENERATE_JSON_SCHEMA: '/console/config/v2/datasets/dataschema', -// UPDATE_DATASCHEMA: '/console/config/v2/datasets/update', -// LIST_DATASET: '/console/config/v2/datasets/list', -// DATASETS_DIFF: '/console/api/dataset/diff', -// PUBLISH_DATASET: '/console/config/v2/datasets/status-transition', -// LIST_CONNECTORS: '/console/config/v2/connectors/list', -// READ_CONNECTORS: '/console/config/v2/connectors/read' -// }; - -//USE THESE ROUTES FOR LOCAL TESTING const ENDPOINTS = { DATASETS_READ: '/config/v2/datasets/read', @@ -324,4 +308,32 @@ export const generateJsonSchema = (payload: any) => { const transitionRequest = generateRequestBody({ request: payload?.data, apiId: "api.datasets.dataschema" }) return http.post(`${ENDPOINTS.GENERATE_JSON_SCHEMA}`, transitionRequest) .then(transform); +} +export const isJsonSchema = (jsonObject: any) => { + if (typeof jsonObject !== "object" || jsonObject === null) { + return false; + } + const schemaKeywords = [ + "$schema", + "type", + "properties", + "required", + "additionalProperties", + "definitions", + "items", + "allOf", + "oneOf", + "anyOf", + "not", + ]; + + const hasSchemaKeyword = schemaKeywords.some((keyword) => + Object.prototype.hasOwnProperty.call(jsonObject, keyword) + ); + + if (!hasSchemaKeyword) { + return false; + } else { + return true; + } } \ No newline at end of file diff --git a/web-console-v2/src/theme.ts b/web-console-v2/src/theme.ts index ac0d692a..cf672d9d 100644 --- a/web-console-v2/src/theme.ts +++ b/web-console-v2/src/theme.ts @@ -111,7 +111,6 @@ export const theme = createTheme({ }, primary: { main: '#056ece', - dark: '#9bc5eb', light: '#cde2f5', lighter: '#e6f0fa' }, diff --git a/web-console-v2/src/utils/dataMappings.ts b/web-console-v2/src/utils/dataMappings.ts index e17bed20..072d1605 100644 --- a/web-console-v2/src/utils/dataMappings.ts +++ b/web-console-v2/src/utils/dataMappings.ts @@ -1,162 +1,98 @@ export const dataMappings = { - text: { - arrival_format: ['string'], - store_format: { - string: { - jsonSchema: 'string', - datasource: 'string' - }, - 'date-time': { - jsonSchema: 'string', - datasource: 'string' - }, - date: { - jsonSchema: 'string', - datasource: 'string' - }, - boolean: { - jsonSchema: 'string', - datasource: 'boolean' - }, - epoch: { - jsonSchema: 'string', - datasource: 'integer' - }, - long: { - jsonSchema: 'string', - datasource: 'long' - }, - double: { - jsonSchema: 'string', - datasource: 'double' - }, - bigdecimal: { - jsonSchema: 'string', - datasource: 'double' - }, - integer: { - jsonSchema: 'string', - datasource: 'long' - } - } - }, - string: { - arrival_format: ['string'], - store_format: { - string: { - jsonSchema: 'string', - datasource: 'string' - }, - 'date-time': { - jsonSchema: 'string', - datasource: 'string' - }, - date: { - jsonSchema: 'string', - datasource: 'string' - }, - boolean: { - jsonSchema: 'string', - datasource: 'boolean' - }, - epoch: { - jsonSchema: 'string', - datasource: 'integer' - }, - long: { - jsonSchema: 'string', - datasource: 'long' - }, - double: { - jsonSchema: 'string', - datasource: 'double' - }, - bigdecimal: { - jsonSchema: 'string', - datasource: 'double' - }, - integer: { - jsonSchema: 'string', - datasource: 'long' - } - } - }, - number: { - arrival_format: ['number', 'integer'], - store_format: { - integer: { - jsonSchema: 'integer', - datasource: 'long' - }, - float: { - jsonSchema: 'number', - datasource: 'double' - }, - long: { - jsonSchema: 'integer', - datasource: 'long' - }, - double: { - jsonSchema: 'number', - datasource: 'double' - }, - bigdecimal: { - jsonSchema: 'number', - datasource: 'double' - }, - epoch: { - jsonSchema: 'integer', - datasource: 'long' - }, - number: { - jsonSchema: 'number', - datasource: 'double' - } - } - }, - integer: { - arrival_format: ['integer'], - store_format: { - number: { - jsonSchema: 'integer', - datasource: 'double' + "text": { + "arrival_format": ["string"], + "store_format": { + "string": { + "jsonSchema": "string", + "datasource": "string" + }, + "date-time": { + "jsonSchema": "string", + "datasource": "string" + }, + "date": { + "jsonSchema": "string", + "datasource": "string" + }, + "boolean": { + "jsonSchema": "string", + "datasource": "boolean" + }, + "epoch": { + "jsonSchema": "string", + "datasource": "integer" + }, + "long": { + "jsonSchema": "string", + "datasource": "long" + }, + "double": { + "jsonSchema": "string", + "datasource": "double" + }, + "bigdecimal": { + "jsonSchema": "string", + "datasource": "double" + }, + "integer": { + "jsonSchema": "string", + "datasource": "long" } } }, - object: { - arrival_format: ['object'], - store_format: { - object: { - jsonSchema: 'object', - datasource: 'json' + "number": { + "arrival_format": ["number", "integer"], + "store_format": { + "integer": { + "jsonSchema": "integer", + "datasource": "long" + }, + "float": { + "jsonSchema": "number", + "datasource": "double" + }, + "long": { + "jsonSchema": "integer", + "datasource": "long" + }, + "double": { + "jsonSchema": "number", + "datasource": "double" + }, + "bigdecimal": { + "jsonSchema": "number", + "datasource": "double" + }, + "epoch":{ + "jsonSchema": "integer", + "datasource": "long" } } }, - - array: { - arrival_format: ['array'], - store_format: { - array: { - jsonSchema: 'array', - datasource: 'array' + "object": { + "arrival_format": ["object"], + "store_format": { + "object": { + "jsonSchema": "object", + "datasource": "json" } } }, - boolean: { - arrival_format: ['boolean'], - store_format: { - boolean: { - jsonSchema: 'boolean', - datasource: 'boolean' + "array": { + "arrival_format": ["array"], + "store_format": { + "array": { + "jsonSchema": "array", + "datasource": "array" } } }, - 'date-time': { - arrival_format: ['date-time'], - store_format: { - 'date-time': { - jsonSchema: 'date-time', - datasource: 'date-time' + "boolean": { + "arrival_format": ["boolean"], + "store_format": { + "boolean": { + "jsonSchema": "boolean", + "datasource": "boolean" } } } diff --git a/web-console-v2/src/utils/locales/en.json b/web-console-v2/src/utils/locales/en.json index 92658ba5..2552e488 100644 --- a/web-console-v2/src/utils/locales/en.json +++ b/web-console-v2/src/utils/locales/en.json @@ -66,7 +66,8 @@ "dataset-configuration": "Dataset Configurations", "dataset-field": "Dataset Field", "dataset-saved": "Dataset Saved.", - "dataset-publish-success": "Dataset publishing is under progress.", + "dataset-publish-inprogress": "Dataset publishing is under progress.", + "dataset-publish-success": "Dataset published successfully.", "dataset-publish-failure": "Failed to publish dataset", "dataset-retire-success": "Dataset retired successfully", "dataset-retire-failure": "Failed to retire dataset", diff --git a/web-console-v2/src/utils/renderCells.tsx b/web-console-v2/src/utils/renderCells.tsx index 83f099f2..0a187789 100644 --- a/web-console-v2/src/utils/renderCells.tsx +++ b/web-console-v2/src/utils/renderCells.tsx @@ -94,9 +94,11 @@ const renderColumnCell = ({ cell, value }: any) => { : isSubRow && !isObjectType ? subdepthIndentation : '3px'; - + const hasHighSeverity = row?.suggestions?.some( + (suggestion: any) => suggestion.severity !== 'LOW' && suggestion.severity !== 'MEDIUM' + ); return ( - +