Fixed issue: 4555 UI: Add breadcrumbs to Add Service page and Services page (#4806)

This commit is contained in:
Shailesh Parmar 2022-05-09 22:07:35 +05:30 committed by GitHub
parent 1b003f9e79
commit 4f57e20a15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 185 additions and 22 deletions

View File

@ -34,6 +34,7 @@ import {
} from '../../utils/ServiceUtils'; } from '../../utils/ServiceUtils';
import AddIngestion from '../AddIngestion/AddIngestion.component'; import AddIngestion from '../AddIngestion/AddIngestion.component';
import SuccessScreen from '../common/success-screen/SuccessScreen'; import SuccessScreen from '../common/success-screen/SuccessScreen';
import TitleBreadcrumb from '../common/title-breadcrumb/title-breadcrumb.component';
import PageLayout from '../containers/PageLayout'; import PageLayout from '../containers/PageLayout';
import IngestionStepper from '../IngestionStepper/IngestionStepper.component'; import IngestionStepper from '../IngestionStepper/IngestionStepper.component';
import ConnectionConfigForm from '../ServiceConfig/ConnectionConfigForm'; import ConnectionConfigForm from '../ServiceConfig/ConnectionConfigForm';
@ -52,9 +53,11 @@ const AddService = ({
ingestionAction, ingestionAction,
showDeployButton, showDeployButton,
onIngestionDeploy, onIngestionDeploy,
slashedBreadcrumb,
addIngestion,
handleAddIngestion,
}: AddServiceProps) => { }: AddServiceProps) => {
const history = useHistory(); const history = useHistory();
const [addIngestion, setAddIngestion] = useState(false);
const [showErrorMessage, setShowErrorMessage] = useState({ const [showErrorMessage, setShowErrorMessage] = useState({
serviceType: false, serviceType: false,
name: false, name: false,
@ -110,10 +113,6 @@ const AddService = ({
} }
}; };
const handleAddIngestion = (value: boolean) => {
setAddIngestion(value);
};
const handleConfigUpdate = ( const handleConfigUpdate = (
oData: ConfigData, oData: ConfigData,
serviceCat: ServiceCategory serviceCat: ServiceCategory
@ -263,6 +262,7 @@ const AddService = ({
return ( return (
<PageLayout <PageLayout
classes="tw-max-w-full-hd tw-h-full tw-pt-4" classes="tw-max-w-full-hd tw-h-full tw-pt-4"
header={<TitleBreadcrumb titleLinks={slashedBreadcrumb} />}
layout={PageLayoutType['2ColRTL']} layout={PageLayoutType['2ColRTL']}
rightPanel={fetchRightPanel()}> rightPanel={fetchRightPanel()}>
<div className="tw-form-container"> <div className="tw-form-container">

View File

@ -14,10 +14,13 @@
import { ServiceCategory } from '../../enums/service.enum'; import { ServiceCategory } from '../../enums/service.enum';
import { CreateIngestionPipeline } from '../../generated/api/services/ingestionPipelines/createIngestionPipeline'; import { CreateIngestionPipeline } from '../../generated/api/services/ingestionPipelines/createIngestionPipeline';
import { DataObj } from '../../interface/service.interface'; import { DataObj } from '../../interface/service.interface';
import { TitleBreadcrumbProps } from '../common/title-breadcrumb/title-breadcrumb.interface';
export interface AddServiceProps { export interface AddServiceProps {
serviceCategory: ServiceCategory; serviceCategory: ServiceCategory;
addIngestion: boolean;
onAddServiceSave: (service: DataObj) => Promise<void>; onAddServiceSave: (service: DataObj) => Promise<void>;
handleAddIngestion: (value: boolean) => void;
onAddIngestionSave: (ingestion: CreateIngestionPipeline) => Promise<void>; onAddIngestionSave: (ingestion: CreateIngestionPipeline) => Promise<void>;
newServiceData: DataObj | undefined; newServiceData: DataObj | undefined;
isIngestionDeployed: boolean; isIngestionDeployed: boolean;
@ -25,5 +28,6 @@ export interface AddServiceProps {
ingestionProgress: number; ingestionProgress: number;
ingestionAction: string; ingestionAction: string;
showDeployButton?: boolean; showDeployButton?: boolean;
slashedBreadcrumb: TitleBreadcrumbProps['titleLinks'];
onIngestionDeploy?: () => Promise<void>; onIngestionDeploy?: () => Promise<void>;
} }

View File

@ -42,6 +42,10 @@ jest.mock('../AddIngestion/AddIngestion.component', () => () => (
<>AddIngestion</> <>AddIngestion</>
)); ));
jest.mock('../common/title-breadcrumb/title-breadcrumb.component', () => () => (
<>TitleBreadcrumb.component</>
));
jest.mock('../ServiceConfig/ConnectionConfigForm', () => () => ( jest.mock('../ServiceConfig/ConnectionConfigForm', () => () => (
<>ConnectionConfigForm</> <>ConnectionConfigForm</>
)); ));
@ -50,12 +54,20 @@ describe('Test AddService component', () => {
it('AddService component should render', async () => { it('AddService component should render', async () => {
const { container } = render( const { container } = render(
<AddService <AddService
addIngestion={false}
handleAddIngestion={jest.fn()}
ingestionAction="Creating" ingestionAction="Creating"
ingestionProgress={0} ingestionProgress={0}
isIngestionCreated={false} isIngestionCreated={false}
isIngestionDeployed={false} isIngestionDeployed={false}
newServiceData={undefined} newServiceData={undefined}
serviceCategory={ServiceCategory.DASHBOARD_SERVICES} serviceCategory={ServiceCategory.DASHBOARD_SERVICES}
slashedBreadcrumb={[
{
name: 'breadcrumb',
url: '',
},
]}
onAddIngestionSave={jest.fn()} onAddIngestionSave={jest.fn()}
onAddServiceSave={jest.fn()} onAddServiceSave={jest.fn()}
/> />

View File

@ -116,7 +116,11 @@ const PageLayout: FC<PageLayoutProp> = ({
const get2ColRTLLayout = () => { const get2ColRTLLayout = () => {
return ( return (
<> <>
{header && <div className="tw-px-6">{header}</div>} {header && (
<div className="page-layout-container tw-gap-x-3 tw-px-6 centered-layout tw-max-w-full-hd tw-pt-4">
{header}
</div>
)}
<div <div
className={classNames( className={classNames(
'page-layout-container l2-rtl-col tw-gap-x-3 tw-px-6 tw-overflow-y-auto centered-layout', 'page-layout-container l2-rtl-col tw-gap-x-3 tw-px-6 tw-overflow-y-auto centered-layout',

View File

@ -169,6 +169,7 @@ export const ROUTES = {
SERVICE_WITH_TAB: `/service/${PLACEHOLDER_ROUTE_SERVICE_CAT}/${PLACEHOLDER_ROUTE_SERVICE_FQN}/${PLACEHOLDER_ROUTE_TAB}`, SERVICE_WITH_TAB: `/service/${PLACEHOLDER_ROUTE_SERVICE_CAT}/${PLACEHOLDER_ROUTE_SERVICE_FQN}/${PLACEHOLDER_ROUTE_TAB}`,
ADD_SERVICE: `/${PLACEHOLDER_ROUTE_SERVICE_CAT}/add-service`, ADD_SERVICE: `/${PLACEHOLDER_ROUTE_SERVICE_CAT}/add-service`,
SERVICES: '/services', SERVICES: '/services',
SERVICES_WITH_TAB: `/services/${PLACEHOLDER_ROUTE_SERVICE_CAT}`,
ADD_INGESTION: `/service/${PLACEHOLDER_ROUTE_SERVICE_CAT}/${PLACEHOLDER_ROUTE_SERVICE_FQN}/add-ingestion/${PLACEHOLDER_ROUTE_INGESTION_TYPE}`, ADD_INGESTION: `/service/${PLACEHOLDER_ROUTE_SERVICE_CAT}/${PLACEHOLDER_ROUTE_SERVICE_FQN}/add-ingestion/${PLACEHOLDER_ROUTE_INGESTION_TYPE}`,
EDIT_INGESTION: `/service/${PLACEHOLDER_ROUTE_SERVICE_CAT}/${PLACEHOLDER_ROUTE_SERVICE_FQN}/edit-ingestion/${PLACEHOLDER_ROUTE_INGESTION_FQN}/${PLACEHOLDER_ROUTE_INGESTION_TYPE}`, EDIT_INGESTION: `/service/${PLACEHOLDER_ROUTE_SERVICE_CAT}/${PLACEHOLDER_ROUTE_SERVICE_FQN}/edit-ingestion/${PLACEHOLDER_ROUTE_INGESTION_FQN}/${PLACEHOLDER_ROUTE_INGESTION_TYPE}`,
USERS: '/users', USERS: '/users',

View File

@ -12,7 +12,7 @@
*/ */
import { AxiosError, AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import { capitalize } from 'lodash'; import { capitalize, startCase } from 'lodash';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useHistory, useParams } from 'react-router-dom'; import { useHistory, useParams } from 'react-router-dom';
import { import {
@ -23,6 +23,8 @@ import {
import { getServiceByFQN } from '../../axiosAPIs/serviceAPI'; import { getServiceByFQN } from '../../axiosAPIs/serviceAPI';
import AddIngestion from '../../components/AddIngestion/AddIngestion.component'; import AddIngestion from '../../components/AddIngestion/AddIngestion.component';
import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder'; import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder';
import TitleBreadcrumb from '../../components/common/title-breadcrumb/title-breadcrumb.component';
import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/title-breadcrumb.interface';
import PageContainerV1 from '../../components/containers/PageContainerV1'; import PageContainerV1 from '../../components/containers/PageContainerV1';
import PageLayout from '../../components/containers/PageLayout'; import PageLayout from '../../components/containers/PageLayout';
import Loader from '../../components/Loader/Loader'; import Loader from '../../components/Loader/Loader';
@ -41,7 +43,11 @@ import { PipelineType } from '../../generated/entity/services/ingestionPipelines
import { DataObj } from '../../interface/service.interface'; import { DataObj } from '../../interface/service.interface';
import jsonData from '../../jsons/en'; import jsonData from '../../jsons/en';
import { getEntityMissingError } from '../../utils/CommonUtils'; import { getEntityMissingError } from '../../utils/CommonUtils';
import { getServiceIngestionStepGuide } from '../../utils/ServiceUtils'; import { getServicesWithTabPath } from '../../utils/RouterUtils';
import {
getServiceIngestionStepGuide,
serviceTypeLogo,
} from '../../utils/ServiceUtils';
import { showErrorToast } from '../../utils/ToastUtils'; import { showErrorToast } from '../../utils/ToastUtils';
const AddIngestionPage = () => { const AddIngestionPage = () => {
@ -60,6 +66,9 @@ const AddIngestionPage = () => {
); );
const [ingestionId, setIngestionId] = useState(''); const [ingestionId, setIngestionId] = useState('');
const [showIngestionButton, setShowIngestionButton] = useState(false); const [showIngestionButton, setShowIngestionButton] = useState(false);
const [slashedBreadcrumb, setSlashedBreadcrumb] = useState<
TitleBreadcrumbProps['titleLinks']
>([]);
const fetchServiceDetails = () => { const fetchServiceDetails = () => {
getServiceByFQN(serviceCategory, serviceFQN) getServiceByFQN(serviceCategory, serviceFQN)
@ -172,6 +181,26 @@ const AddIngestionPage = () => {
return ingestion && !showIngestionButton; return ingestion && !showIngestionButton;
}; };
useEffect(() => {
setSlashedBreadcrumb([
{
name: startCase(serviceCategory),
url: getServicesWithTabPath(serviceCategory),
},
{
name: serviceData?.name || '',
url: getServiceDetailsPath(serviceFQN, serviceCategory, 'ingestions'),
imgSrc: serviceTypeLogo(serviceData?.serviceType || ''),
activeTitle: true,
},
{
name: `Add ${capitalize(ingestionType)} Ingestion`,
url: '',
activeTitle: true,
},
]);
}, [serviceCategory, ingestionType, serviceData]);
const renderAddIngestionPage = () => { const renderAddIngestionPage = () => {
if (isLoading) { if (isLoading) {
return <Loader />; return <Loader />;
@ -185,6 +214,7 @@ const AddIngestionPage = () => {
return ( return (
<PageLayout <PageLayout
classes="tw-max-w-full-hd tw-h-full tw-pt-4" classes="tw-max-w-full-hd tw-h-full tw-pt-4"
header={<TitleBreadcrumb titleLinks={slashedBreadcrumb} />}
layout={PageLayoutType['2ColRTL']} layout={PageLayoutType['2ColRTL']}
rightPanel={getServiceIngestionStepGuide( rightPanel={getServiceIngestionStepGuide(
activeIngestionStep, activeIngestionStep,

View File

@ -12,7 +12,8 @@
*/ */
import { AxiosError, AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import React, { useState } from 'react'; import { startCase } from 'lodash';
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { import {
addIngestionPipeline, addIngestionPipeline,
@ -21,6 +22,7 @@ import {
} from '../../axiosAPIs/ingestionPipelineAPI'; } from '../../axiosAPIs/ingestionPipelineAPI';
import { postService } from '../../axiosAPIs/serviceAPI'; import { postService } from '../../axiosAPIs/serviceAPI';
import AddService from '../../components/AddService/AddService.component'; import AddService from '../../components/AddService/AddService.component';
import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/title-breadcrumb.interface';
import PageContainerV1 from '../../components/containers/PageContainerV1'; import PageContainerV1 from '../../components/containers/PageContainerV1';
import { import {
DEPLOYED_PROGRESS_VAL, DEPLOYED_PROGRESS_VAL,
@ -32,6 +34,7 @@ import { ServiceCategory } from '../../enums/service.enum';
import { CreateIngestionPipeline } from '../../generated/api/services/ingestionPipelines/createIngestionPipeline'; import { CreateIngestionPipeline } from '../../generated/api/services/ingestionPipelines/createIngestionPipeline';
import { DataObj } from '../../interface/service.interface'; import { DataObj } from '../../interface/service.interface';
import jsonData from '../../jsons/en'; import jsonData from '../../jsons/en';
import { getServicesWithTabPath } from '../../utils/RouterUtils';
import { showErrorToast } from '../../utils/ToastUtils'; import { showErrorToast } from '../../utils/ToastUtils';
const AddServicePage = () => { const AddServicePage = () => {
@ -45,6 +48,14 @@ const AddServicePage = () => {
); );
const [ingestionId, setIngestionId] = useState(''); const [ingestionId, setIngestionId] = useState('');
const [showIngestionButton, setShowIngestionButton] = useState(false); const [showIngestionButton, setShowIngestionButton] = useState(false);
const [slashedBreadcrumb, setSlashedBreadcrumb] = useState<
TitleBreadcrumbProps['titleLinks']
>([]);
const [addIngestion, setAddIngestion] = useState(false);
const handleAddIngestion = (value: boolean) => {
setAddIngestion(value);
};
const onAddServiceSave = (data: DataObj) => { const onAddServiceSave = (data: DataObj) => {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
@ -136,9 +147,25 @@ const AddServicePage = () => {
}); });
}; };
useEffect(() => {
setSlashedBreadcrumb([
{
name: startCase(serviceCategory),
url: getServicesWithTabPath(serviceCategory),
},
{
name: addIngestion ? 'Add New Ingestion' : 'Add New Service',
url: '',
activeTitle: true,
},
]);
}, [serviceCategory, addIngestion]);
return ( return (
<PageContainerV1> <PageContainerV1>
<AddService <AddService
addIngestion={addIngestion}
handleAddIngestion={handleAddIngestion}
ingestionAction={ingestionAction} ingestionAction={ingestionAction}
ingestionProgress={ingestionProgress} ingestionProgress={ingestionProgress}
isIngestionCreated={isIngestionCreated} isIngestionCreated={isIngestionCreated}
@ -146,6 +173,7 @@ const AddServicePage = () => {
newServiceData={newServiceData} newServiceData={newServiceData}
serviceCategory={serviceCategory as ServiceCategory} serviceCategory={serviceCategory as ServiceCategory}
showDeployButton={showIngestionButton} showDeployButton={showIngestionButton}
slashedBreadcrumb={slashedBreadcrumb}
onAddIngestionSave={onAddIngestionSave} onAddIngestionSave={onAddIngestionSave}
onAddServiceSave={onAddServiceSave} onAddServiceSave={onAddServiceSave}
onIngestionDeploy={onIngestionDeploy} onIngestionDeploy={onIngestionDeploy}

View File

@ -13,7 +13,7 @@
import { AxiosError, AxiosPromise, AxiosResponse } from 'axios'; import { AxiosError, AxiosPromise, AxiosResponse } from 'axios';
import { compare, Operation } from 'fast-json-patch'; import { compare, Operation } from 'fast-json-patch';
import { isEmpty } from 'lodash'; import { isEmpty, startCase } from 'lodash';
import { import {
EntityFieldThreadCount, EntityFieldThreadCount,
EntityTags, EntityTags,
@ -76,6 +76,7 @@ import {
} from '../../utils/DashboardDetailsUtils'; } from '../../utils/DashboardDetailsUtils';
import { getEntityFeedLink, getEntityLineage } from '../../utils/EntityUtils'; import { getEntityFeedLink, getEntityLineage } from '../../utils/EntityUtils';
import { deletePost, getUpdatedThread } from '../../utils/FeedUtils'; import { deletePost, getUpdatedThread } from '../../utils/FeedUtils';
import { getServicesWithTabPath } from '../../utils/RouterUtils';
import { serviceTypeLogo } from '../../utils/ServiceUtils'; import { serviceTypeLogo } from '../../utils/ServiceUtils';
import { getErrorText } from '../../utils/StringsUtils'; import { getErrorText } from '../../utils/StringsUtils';
import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils'; import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils';
@ -323,6 +324,10 @@ const DashboardDetailsPage = () => {
setServiceType(serviceType); setServiceType(serviceType);
setDeleted(deleted); setDeleted(deleted);
setSlashedDashboardName([ setSlashedDashboardName([
{
name: startCase(ServiceCategory.DASHBOARD_SERVICES),
url: getServicesWithTabPath(ServiceCategory.DASHBOARD_SERVICES),
},
{ {
name: service.name, name: service.name,
url: service.name url: service.name

View File

@ -14,6 +14,7 @@
import { AxiosError, AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import classNames from 'classnames'; import classNames from 'classnames';
import { compare } from 'fast-json-patch'; import { compare } from 'fast-json-patch';
import { startCase } from 'lodash';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { import {
EntityFieldThreadCount, EntityFieldThreadCount,
@ -88,6 +89,7 @@ import {
getEntityFieldThreadCounts, getEntityFieldThreadCounts,
getUpdatedThread, getUpdatedThread,
} from '../../utils/FeedUtils'; } from '../../utils/FeedUtils';
import { getServicesWithTabPath } from '../../utils/RouterUtils';
import { serviceTypeLogo } from '../../utils/ServiceUtils'; import { serviceTypeLogo } from '../../utils/ServiceUtils';
import { getErrorText } from '../../utils/StringsUtils'; import { getErrorText } from '../../utils/StringsUtils';
import { getEntityLink } from '../../utils/TableUtils'; import { getEntityLink } from '../../utils/TableUtils';
@ -249,6 +251,10 @@ const DatabaseSchemaPage: FunctionComponent = () => {
setTableData(tables); setTableData(tables);
setTableInstanceCount(tables?.length || 0); setTableInstanceCount(tables?.length || 0);
setSlashedTableName([ setSlashedTableName([
{
name: startCase(ServiceCategory.DATABASE_SERVICES),
url: getServicesWithTabPath(ServiceCategory.DATABASE_SERVICES),
},
{ {
name: service.name, name: service.name,
url: service.name url: service.name

View File

@ -13,7 +13,7 @@
import { AxiosError, AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import { compare } from 'fast-json-patch'; import { compare } from 'fast-json-patch';
import { isEmpty, isUndefined } from 'lodash'; import { isEmpty, isUndefined, startCase } from 'lodash';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { import {
EntityFieldThreadCount, EntityFieldThreadCount,
@ -99,6 +99,7 @@ import {
} from '../../utils/DatasetDetailsUtils'; } from '../../utils/DatasetDetailsUtils';
import { getEntityFeedLink, getEntityLineage } from '../../utils/EntityUtils'; import { getEntityFeedLink, getEntityLineage } from '../../utils/EntityUtils';
import { deletePost, getUpdatedThread } from '../../utils/FeedUtils'; import { deletePost, getUpdatedThread } from '../../utils/FeedUtils';
import { getServicesWithTabPath } from '../../utils/RouterUtils';
import { serviceTypeLogo } from '../../utils/ServiceUtils'; import { serviceTypeLogo } from '../../utils/ServiceUtils';
import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils'; import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils';
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils'; import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
@ -300,6 +301,10 @@ const DatasetDetailsPage: FunctionComponent = () => {
setFollowers(followers); setFollowers(followers);
setDeleted(deleted); setDeleted(deleted);
setSlashedTableName([ setSlashedTableName([
{
name: startCase(ServiceCategory.DATABASE_SERVICES),
url: getServicesWithTabPath(ServiceCategory.DATABASE_SERVICES),
},
{ {
name: service.name, name: service.name,
url: service.name url: service.name

View File

@ -12,7 +12,7 @@
*/ */
import { AxiosError, AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import { capitalize } from 'lodash'; import { capitalize, startCase } from 'lodash';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useHistory, useParams } from 'react-router-dom'; import { useHistory, useParams } from 'react-router-dom';
import { import {
@ -23,6 +23,8 @@ import {
import { getServiceByFQN } from '../../axiosAPIs/serviceAPI'; import { getServiceByFQN } from '../../axiosAPIs/serviceAPI';
import AddIngestion from '../../components/AddIngestion/AddIngestion.component'; import AddIngestion from '../../components/AddIngestion/AddIngestion.component';
import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder'; import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder';
import TitleBreadcrumb from '../../components/common/title-breadcrumb/title-breadcrumb.component';
import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/title-breadcrumb.interface';
import PageContainerV1 from '../../components/containers/PageContainerV1'; import PageContainerV1 from '../../components/containers/PageContainerV1';
import PageLayout from '../../components/containers/PageLayout'; import PageLayout from '../../components/containers/PageLayout';
import Loader from '../../components/Loader/Loader'; import Loader from '../../components/Loader/Loader';
@ -44,7 +46,11 @@ import {
import { DataObj } from '../../interface/service.interface'; import { DataObj } from '../../interface/service.interface';
import jsonData from '../../jsons/en'; import jsonData from '../../jsons/en';
import { getEntityMissingError } from '../../utils/CommonUtils'; import { getEntityMissingError } from '../../utils/CommonUtils';
import { getServiceIngestionStepGuide } from '../../utils/ServiceUtils'; import { getServicesWithTabPath } from '../../utils/RouterUtils';
import {
getServiceIngestionStepGuide,
serviceTypeLogo,
} from '../../utils/ServiceUtils';
import { showErrorToast } from '../../utils/ToastUtils'; import { showErrorToast } from '../../utils/ToastUtils';
const EditIngestionPage = () => { const EditIngestionPage = () => {
@ -65,6 +71,9 @@ const EditIngestionPage = () => {
IngestionActionMessage.UPDATING IngestionActionMessage.UPDATING
); );
const [showIngestionButton, setShowIngestionButton] = useState(false); const [showIngestionButton, setShowIngestionButton] = useState(false);
const [slashedBreadcrumb, setSlashedBreadcrumb] = useState<
TitleBreadcrumbProps['titleLinks']
>([]);
const fetchServiceDetails = () => { const fetchServiceDetails = () => {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
@ -209,6 +218,26 @@ const EditIngestionPage = () => {
return ingestion && !showIngestionButton && !ingestionData.deployed; return ingestion && !showIngestionButton && !ingestionData.deployed;
}; };
useEffect(() => {
setSlashedBreadcrumb([
{
name: startCase(serviceCategory),
url: getServicesWithTabPath(serviceCategory),
},
{
name: serviceData?.name || '',
url: getServiceDetailsPath(serviceFQN, serviceCategory, 'ingestions'),
imgSrc: serviceTypeLogo(serviceData?.serviceType || ''),
activeTitle: true,
},
{
name: `Edit ${capitalize(ingestionType)} Ingestion`,
url: '',
activeTitle: true,
},
]);
}, [serviceCategory, ingestionType, serviceData]);
const renderEditIngestionPage = () => { const renderEditIngestionPage = () => {
if (isLoading) { if (isLoading) {
return <Loader />; return <Loader />;
@ -218,6 +247,7 @@ const EditIngestionPage = () => {
return ( return (
<PageLayout <PageLayout
classes="tw-max-w-full-hd tw-h-full tw-pt-4" classes="tw-max-w-full-hd tw-h-full tw-pt-4"
header={<TitleBreadcrumb titleLinks={slashedBreadcrumb} />}
layout={PageLayoutType['2ColRTL']} layout={PageLayoutType['2ColRTL']}
rightPanel={getServiceIngestionStepGuide( rightPanel={getServiceIngestionStepGuide(
activeIngestionStep, activeIngestionStep,

View File

@ -13,7 +13,7 @@
import { AxiosError, AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import { compare, Operation } from 'fast-json-patch'; import { compare, Operation } from 'fast-json-patch';
import { isEmpty } from 'lodash'; import { isEmpty, startCase } from 'lodash';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { import {
EntityFieldThreadCount, EntityFieldThreadCount,
@ -75,6 +75,7 @@ import {
getCurrentPipelineTab, getCurrentPipelineTab,
pipelineDetailsTabs, pipelineDetailsTabs,
} from '../../utils/PipelineDetailsUtils'; } from '../../utils/PipelineDetailsUtils';
import { getServicesWithTabPath } from '../../utils/RouterUtils';
import { serviceTypeLogo } from '../../utils/ServiceUtils'; import { serviceTypeLogo } from '../../utils/ServiceUtils';
import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils'; import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils';
import { showErrorToast } from '../../utils/ToastUtils'; import { showErrorToast } from '../../utils/ToastUtils';
@ -276,6 +277,10 @@ const PipelineDetailsPage = () => {
setServiceType(serviceType); setServiceType(serviceType);
setDeleted(deleted); setDeleted(deleted);
setSlashedPipelineName([ setSlashedPipelineName([
{
name: startCase(ServiceCategory.PIPELINE_SERVICES),
url: getServicesWithTabPath(ServiceCategory.PIPELINE_SERVICES),
},
{ {
name: service.name, name: service.name,
url: service.name url: service.name

View File

@ -13,6 +13,7 @@
import { AxiosError, AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import { compare } from 'fast-json-patch'; import { compare } from 'fast-json-patch';
import { startCase } from 'lodash';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { EntityFieldThreadCount, EntityTags, EntityThread } from 'Models'; import { EntityFieldThreadCount, EntityTags, EntityThread } from 'Models';
import React, { FunctionComponent, useEffect, useState } from 'react'; import React, { FunctionComponent, useEffect, useState } from 'react';
@ -55,6 +56,7 @@ import {
} from '../../utils/CommonUtils'; } from '../../utils/CommonUtils';
import { getEntityFeedLink } from '../../utils/EntityUtils'; import { getEntityFeedLink } from '../../utils/EntityUtils';
import { deletePost, getUpdatedThread } from '../../utils/FeedUtils'; import { deletePost, getUpdatedThread } from '../../utils/FeedUtils';
import { getServicesWithTabPath } from '../../utils/RouterUtils';
import { serviceTypeLogo } from '../../utils/ServiceUtils'; import { serviceTypeLogo } from '../../utils/ServiceUtils';
import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils'; import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils';
import { showErrorToast } from '../../utils/ToastUtils'; import { showErrorToast } from '../../utils/ToastUtils';
@ -209,6 +211,10 @@ const TopicDetailsPage: FunctionComponent = () => {
setRetentionSize(retentionSize); setRetentionSize(retentionSize);
setDeleted(deleted); setDeleted(deleted);
setSlashedTopicName([ setSlashedTopicName([
{
name: startCase(ServiceCategory.MESSAGING_SERVICES),
url: getServicesWithTabPath(ServiceCategory.MESSAGING_SERVICES),
},
{ {
name: service.name, name: service.name,
url: service.name url: service.name

View File

@ -14,7 +14,7 @@
import { AxiosError, AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import classNames from 'classnames'; import classNames from 'classnames';
import { compare } from 'fast-json-patch'; import { compare } from 'fast-json-patch';
import { isNil } from 'lodash'; import { isNil, startCase } from 'lodash';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { EntityFieldThreadCount, EntityThread, ExtraInfo } from 'Models'; import { EntityFieldThreadCount, EntityThread, ExtraInfo } from 'Models';
import React, { import React, {
@ -89,7 +89,10 @@ import {
getEntityFieldThreadCounts, getEntityFieldThreadCounts,
getUpdatedThread, getUpdatedThread,
} from '../../utils/FeedUtils'; } from '../../utils/FeedUtils';
import { getExplorePathWithInitFilters } from '../../utils/RouterUtils'; import {
getExplorePathWithInitFilters,
getServicesWithTabPath,
} from '../../utils/RouterUtils';
import { serviceTypeLogo } from '../../utils/ServiceUtils'; import { serviceTypeLogo } from '../../utils/ServiceUtils';
import { getErrorText } from '../../utils/StringsUtils'; import { getErrorText } from '../../utils/StringsUtils';
import { getOwnerFromId, getUsagePercentile } from '../../utils/TableUtils'; import { getOwnerFromId, getUsagePercentile } from '../../utils/TableUtils';
@ -274,6 +277,10 @@ const DatabaseDetails: FunctionComponent = () => {
setServiceType(serviceType); setServiceType(serviceType);
setSlashedDatabaseName([ setSlashedDatabaseName([
{
name: startCase(ServiceCategory.DATABASE_SERVICES),
url: getServicesWithTabPath(ServiceCategory.DATABASE_SERVICES),
},
{ {
name: service.name, name: service.name,
url: service.name url: service.name

View File

@ -13,7 +13,7 @@
import { AxiosError, AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import classNames from 'classnames'; import classNames from 'classnames';
import { isNil, isUndefined } from 'lodash'; import { isNil, isUndefined, startCase } from 'lodash';
import { ExtraInfo, ServicesData } from 'Models'; import { ExtraInfo, ServicesData } from 'Models';
import React, { Fragment, FunctionComponent, useEffect, useState } from 'react'; import React, { Fragment, FunctionComponent, useEffect, useState } from 'react';
import { Link, useHistory, useParams } from 'react-router-dom'; import { Link, useHistory, useParams } from 'react-router-dom';
@ -71,6 +71,7 @@ import {
pluralize, pluralize,
} from '../../utils/CommonUtils'; } from '../../utils/CommonUtils';
import { getInfoElements } from '../../utils/EntityUtils'; import { getInfoElements } from '../../utils/EntityUtils';
import { getServicesWithTabPath } from '../../utils/RouterUtils';
import { import {
getCurrentServiceTab, getCurrentServiceTab,
getIsIngestionEnable, getIsIngestionEnable,
@ -684,6 +685,10 @@ const ServicePage: FunctionComponent = () => {
setServiceDetails(resService.data); setServiceDetails(resService.data);
setDescription(description); setDescription(description);
setSlashedTableName([ setSlashedTableName([
{
name: startCase(serviceName || ''),
url: getServicesWithTabPath(serviceName || ''),
},
{ {
name: getEntityName(resService.data), name: getEntityName(resService.data),
url: '', url: '',

View File

@ -62,6 +62,7 @@ jest.mock('../../authentication/auth-provider/AuthProvider', () => {
jest.mock('../../utils/RouterUtils', () => ({ jest.mock('../../utils/RouterUtils', () => ({
getAddServicePath: jest.fn(), getAddServicePath: jest.fn(),
getServicesWithTabPath: jest.fn(),
})); }));
jest.mock('../../axiosAPIs/serviceAPI', () => ({ jest.mock('../../axiosAPIs/serviceAPI', () => ({

View File

@ -16,7 +16,7 @@ import classNames from 'classnames';
import { isNil } from 'lodash'; import { isNil } from 'lodash';
import { ServiceCollection, ServiceData, ServiceTypes } from 'Models'; import { ServiceCollection, ServiceData, ServiceTypes } from 'Models';
import React, { Fragment, useEffect, useState } from 'react'; import React, { Fragment, useEffect, useState } from 'react';
import { Link, useHistory } from 'react-router-dom'; import { Link, useHistory, useParams } from 'react-router-dom';
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider'; import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
import { addAirflowPipeline } from '../../axiosAPIs/airflowPipelineAPI'; import { addAirflowPipeline } from '../../axiosAPIs/airflowPipelineAPI';
import { import {
@ -69,7 +69,10 @@ import {
} from '../../utils/CommonUtils'; } from '../../utils/CommonUtils';
import { getDashboardURL } from '../../utils/DashboardServiceUtils'; import { getDashboardURL } from '../../utils/DashboardServiceUtils';
import { getBrokers } from '../../utils/MessagingServiceUtils'; import { getBrokers } from '../../utils/MessagingServiceUtils';
import { getAddServicePath } from '../../utils/RouterUtils'; import {
getAddServicePath,
getServicesWithTabPath,
} from '../../utils/RouterUtils';
import { getErrorText } from '../../utils/StringsUtils'; import { getErrorText } from '../../utils/StringsUtils';
import { showErrorToast } from '../../utils/ToastUtils'; import { showErrorToast } from '../../utils/ToastUtils';
@ -98,13 +101,15 @@ export type ApiData = {
}; };
const ServicesPage = () => { const ServicesPage = () => {
const { serviceCategory } = useParams<{ [key: string]: string }>();
const history = useHistory(); const history = useHistory();
const { isAdminUser } = useAuth(); const { isAdminUser } = useAuth();
const { isAuthDisabled } = useAuthContext(); const { isAuthDisabled } = useAuthContext();
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const [serviceName, setServiceName] = const [serviceName, setServiceName] = useState<ServiceTypes>(
useState<ServiceTypes>('databaseServices'); (serviceCategory as ServiceTypes) ?? 'databaseServices'
);
const [paging, setPaging] = useState<ServicePagingRecord>({ const [paging, setPaging] = useState<ServicePagingRecord>({
databaseServices: pagingObject, databaseServices: pagingObject,
messagingServices: pagingObject, messagingServices: pagingObject,
@ -374,6 +379,7 @@ const ServicesPage = () => {
const handleTabChange = (tabName: ServiceTypes) => { const handleTabChange = (tabName: ServiceTypes) => {
setSearchText(''); setSearchText('');
setServiceName(tabName); setServiceName(tabName);
history.push(getServicesWithTabPath(tabName));
setServiceList(services[tabName] as unknown as Array<ServiceDataObj>); setServiceList(services[tabName] as unknown as Array<ServiceDataObj>);
}; };
@ -424,7 +430,7 @@ const ServicesPage = () => {
<span <span
className=" tw-ml-1 tw-font-normal tw-text-grey-body" className=" tw-ml-1 tw-font-normal tw-text-grey-body"
data-testid="brokers"> data-testid="brokers">
{getBrokers(messagingService.connection.config)} {getBrokers(messagingService.connection?.config)}
</span> </span>
</div> </div>
</> </>

View File

@ -64,6 +64,7 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
path={ROUTES.TEAMS_AND_USERS_DETAILS} path={ROUTES.TEAMS_AND_USERS_DETAILS}
/> />
<Route exact component={ServicesPage} path={ROUTES.SERVICES} /> <Route exact component={ServicesPage} path={ROUTES.SERVICES} />
<Route exact component={ServicesPage} path={ROUTES.SERVICES_WITH_TAB} />
<Route exact component={ServicePage} path={ROUTES.SERVICE} /> <Route exact component={ServicePage} path={ROUTES.SERVICE} />
<Route exact component={ServicePage} path={ROUTES.SERVICE_WITH_TAB} /> <Route exact component={ServicePage} path={ROUTES.SERVICE_WITH_TAB} />
<Route exact component={AddServicePage} path={ROUTES.ADD_SERVICE} /> <Route exact component={AddServicePage} path={ROUTES.ADD_SERVICE} />

View File

@ -104,3 +104,10 @@ export const getExplorePathWithInitFilters = (
? `${path}?${initialFilterQS}=${encodeURIComponent(filter)}` ? `${path}?${initialFilterQS}=${encodeURIComponent(filter)}`
: path; : path;
}; };
export const getServicesWithTabPath = (serviceCat: string) => {
let path = ROUTES.SERVICES_WITH_TAB;
path = path.replace(PLACEHOLDER_ROUTE_SERVICE_CAT, serviceCat);
return path;
};