Minor: Day one trigger button on service details page (#20613)

* Worked on adding the day 1 trigger button on service details page

* Fix the service main tab styling

* Enhance DataAssetsHeader and ServiceInsightsTab components to support day one application triggering. Added new props for disabling the run agents button and handling post-trigger actions. Updated localization files with new messages for day one application status. Refactored ServiceDetailsPage to integrate workflow state management and trigger fetching.

* Fix the border on application config tab

* Change the logic to show the trigger day one button to only display in case of failure

Fix the add service page card spacing

* Fix the unit test
This commit is contained in:
Aniket Katkar 2025-04-05 11:00:52 +05:30 committed by GitHub
parent d747536f48
commit 188f575180
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 378 additions and 196 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -19,17 +19,19 @@ import { get, isEmpty } from 'lodash';
import QueryString from 'qs';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Link, useHistory } from 'react-router-dom';
import { Link, useHistory, useParams } from 'react-router-dom';
import { ReactComponent as IconExternalLink } from '../../../assets/svg/external-links.svg';
import { ReactComponent as RedAlertIcon } from '../../../assets/svg/ic-alert-red.svg';
import { ReactComponent as TaskOpenIcon } from '../../../assets/svg/ic-open-task.svg';
import { ReactComponent as VersionIcon } from '../../../assets/svg/ic-version.svg';
import { ReactComponent as LinkIcon } from '../../../assets/svg/link-icon-with-bg.svg';
import { ReactComponent as TriggerIcon } from '../../../assets/svg/trigger.svg';
import { ActivityFeedTabs } from '../../../components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.interface';
import { DomainLabel } from '../../../components/common/DomainLabel/DomainLabel.component';
import { OwnerLabel } from '../../../components/common/OwnerLabel/OwnerLabel.component';
import TierCard from '../../../components/common/TierCard/TierCard';
import EntityHeaderTitle from '../../../components/Entity/EntityHeaderTitle/EntityHeaderTitle.component';
import { DAY_ONE_EXPERIENCE_APP_NAME } from '../../../constants/Applications.constant';
import { DATA_ASSET_ICON_DIMENSION } from '../../../constants/constants';
import { SERVICE_TYPES } from '../../../constants/Services.constant';
import { TAG_START_WITH } from '../../../constants/Tag.constants';
@ -39,12 +41,14 @@ import {
EntityType,
TabSpecificField,
} from '../../../enums/entity.enum';
import { ServiceCategory } from '../../../enums/service.enum';
import { LineageLayer } from '../../../generated/configuration/lineageSettings';
import { Container } from '../../../generated/entity/data/container';
import { Table } from '../../../generated/entity/data/table';
import { Thread } from '../../../generated/entity/feed/thread';
import { useApplicationStore } from '../../../hooks/useApplicationStore';
import { SearchSourceAlias } from '../../../interface/search.interface';
import { triggerOnDemandApp } from '../../../rest/applicationAPI';
import { getActiveAnnouncement } from '../../../rest/feedsAPI';
import { getDataQualityLineage } from '../../../rest/lineageAPI';
import { getContainerByName } from '../../../rest/storageAPI';
@ -62,6 +66,7 @@ import {
} from '../../../utils/EntityUtils';
import { getEntityDetailsPath } from '../../../utils/RouterUtils';
import serviceUtilClassBase from '../../../utils/ServiceUtilClassBase';
import { getEntityTypeFromServiceCategory } from '../../../utils/ServiceUtils';
import tableClassBase from '../../../utils/TableClassBase';
import { getTierTags } from '../../../utils/TableUtils';
import { showErrorToast } from '../../../utils/ToastUtils';
@ -193,7 +198,11 @@ export const DataAssetsHeader = ({
badge,
isDqAlertSupported,
isCustomizedView = false,
disableRunAgentsButton = true,
afterTriggerAction,
isDayOneWorkflowStatusLoading = false,
}: DataAssetsHeaderProps) => {
const { serviceCategory } = useParams<{ serviceCategory: ServiceCategory }>();
const { currentUser } = useApplicationStore();
const { selectedUserSuggestions } = useSuggestionsContext();
const USER_ID = currentUser?.id ?? '';
@ -204,6 +213,7 @@ export const DataAssetsHeader = ({
const [dqFailureCount, setDqFailureCount] = useState(0);
const [isFollowingLoading, setIsFollowingLoading] = useState(false);
const history = useHistory();
const [isDayOneTriggering, setIsDayOneTriggering] = useState(false);
const icon = useMemo(() => {
const serviceType = get(dataAsset, 'serviceType', '');
@ -282,7 +292,7 @@ export const DataAssetsHeader = ({
(node) => node?.fullyQualifiedName !== dataAsset?.fullyQualifiedName
) ?? [];
setDqFailureCount(updatedNodes.length);
} catch (error) {
} catch {
setDqFailureCount(0);
}
};
@ -472,6 +482,56 @@ export const DataAssetsHeader = ({
selectedUserSuggestions,
]);
const triggerTheDayOneApplication = useCallback(async () => {
try {
setIsDayOneTriggering(true);
const entityType = getEntityTypeFromServiceCategory(serviceCategory);
const entityLink = getEntityFeedLink(
entityType,
dataAsset.fullyQualifiedName ?? ''
);
await triggerOnDemandApp(DAY_ONE_EXPERIENCE_APP_NAME, {
entityLink,
});
afterTriggerAction?.();
} catch (err) {
showErrorToast(err as AxiosError);
} finally {
setIsDayOneTriggering(false);
}
}, [serviceCategory, afterTriggerAction]);
const triggerDayOneApplicationButton = useMemo(() => {
if (!SERVICE_TYPES.includes(entityType)) {
return null;
}
const isDisabled = isDayOneWorkflowStatusLoading || disableRunAgentsButton;
const isLoading = isDayOneWorkflowStatusLoading || isDayOneTriggering;
return (
<Tooltip title={t('message.trigger-day-one-application')}>
<Button
className="font-semibold"
data-testid="trigger-day-one-application-button"
disabled={isDisabled}
icon={<Icon className="flex-center" component={TriggerIcon} />}
loading={isLoading}
type="primary"
onClick={triggerTheDayOneApplication}>
{t('label.run-agent-plural')}
</Button>
</Tooltip>
);
}, [
disableRunAgentsButton,
isDayOneWorkflowStatusLoading,
isDayOneTriggering,
triggerTheDayOneApplication,
]);
return (
<>
<Row
@ -509,6 +569,7 @@ export const DataAssetsHeader = ({
className="data-asset-button-group spaced"
data-testid="asset-header-btn-group"
size="small">
{triggerDayOneApplicationButton}
{onUpdateVote && (
<Voting
disabled={deleted}

View File

@ -136,6 +136,9 @@ export type DataAssetsHeaderProps = {
extraDropdownContent?: ManageButtonProps['extraDropdownContent'];
onMetricUpdate?: (updatedData: Metric, key: keyof Metric) => Promise<void>;
isCustomizedView?: boolean;
disableRunAgentsButton?: boolean;
afterTriggerAction?: VoidFunction;
isDayOneWorkflowStatusLoading?: boolean;
} & (
| DataAssetTable
| DataAssetTopic

View File

@ -10,13 +10,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { act, render, screen } from '@testing-library/react';
import { act, fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { EntityType } from '../../../enums/entity.enum';
import {
APIEndpoint,
APIRequestMethod,
} from '../../../generated/entity/data/apiEndpoint';
import {
Container,
StorageServiceType,
@ -28,8 +24,12 @@ import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils';
import { DataAssetsHeader, ExtraInfoLink } from './DataAssetsHeader.component';
import { DataAssetsHeaderProps } from './DataAssetsHeader.interface';
import { DAY_ONE_EXPERIENCE_APP_NAME } from '../../../constants/Applications.constant';
import { ServiceCategory } from '../../../enums/service.enum';
import { DatabaseServiceType } from '../../../generated/entity/services/databaseService';
import { LabelType, State, TagSource } from '../../../generated/tests/testCase';
import { AssetCertification } from '../../../generated/type/assetCertification';
import { triggerOnDemandApp } from '../../../rest/applicationAPI';
const mockProps: DataAssetsHeaderProps = {
dataAsset: {
id: 'assets-id',
@ -57,6 +57,42 @@ const mockProps: DataAssetsHeaderProps = {
onOwnerUpdate: jest.fn(),
};
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useParams: jest.fn().mockImplementation(() => ({
serviceCategory: ServiceCategory.DATABASE_SERVICES,
})),
}));
jest.mock('../../../rest/applicationAPI', () => ({
triggerOnDemandApp: jest.fn().mockImplementation(() => Promise.resolve()),
}));
jest.mock('../../../utils/ServiceUtils', () => ({
getEntityTypeFromServiceCategory: jest
.fn()
.mockImplementation(() => EntityType.DATABASE_SERVICE),
}));
jest.mock('../../../utils/EntityUtils', () => ({
getEntityName: jest.fn().mockImplementation(() => 'name'),
getEntityFeedLink: jest.fn().mockImplementation(() => 'entityFeedLink'),
getEntityVoteStatus: jest.fn().mockImplementation(() => 'unVoted'),
}));
jest.mock('../../../utils/DataAssetsHeader.utils', () => ({
getDataAssetsHeaderInfo: jest.fn().mockImplementation(() => ({
breadcrumbs: [],
extraInfo: [],
})),
getEntityExtraInfoLength: jest.fn().mockImplementation(() => 0),
isDataAssetsWithServiceField: jest.fn().mockImplementation(() => true),
}));
jest.mock('../../common/CertificationTag/CertificationTag', () => {
return jest.fn().mockImplementation(() => <div>CertificationTag</div>);
});
jest.mock(
'../../../components/common/TitleBreadcrumb/TitleBreadcrumb.component',
() => {
@ -196,27 +232,6 @@ describe('DataAssetsHeader component', () => {
expect(screen.getByTestId('Tier')).toContainHTML('label.no-entity');
});
it('should render the request method if entityType is apiEndpoint', () => {
render(
<DataAssetsHeader
{...mockProps}
dataAsset={
{
name: 'testAPIEndpoint',
id: 'testAPIEndpointId',
endpointURL: 'testAPIEndpointURL',
requestMethod: APIRequestMethod.Get,
} as APIEndpoint
}
entityType={EntityType.API_ENDPOINT}
/>
);
expect(
screen.getByTestId('api-endpoint-request-method')
).toBeInTheDocument();
});
it('should call getDataQualityLineage, if isDqAlertSupported and alert supported is true', () => {
mockIsAlertSupported = true;
act(() => {
@ -291,11 +306,35 @@ describe('DataAssetsHeader component', () => {
expect(screen.getByText('label.certification')).toBeInTheDocument();
const certificatComponent = screen.getByTestId(
`certification-${mockCertification.tagLabel.tagFQN}`
);
const certificatComponent = screen.getByText(`CertificationTag`);
expect(certificatComponent).toBeInTheDocument();
expect(certificatComponent).toHaveTextContent('Bronze_Medal');
});
it('should trigger the Day One application when the button is clicked', () => {
render(
<DataAssetsHeader
{...mockProps}
dataAsset={{
...mockProps.dataAsset,
serviceType: DatabaseServiceType.BigQuery,
}}
disableRunAgentsButton={false}
entityType={EntityType.DATABASE_SERVICE}
/>
);
const button = screen.getByTestId('trigger-day-one-application-button');
expect(button).toBeInTheDocument();
fireEvent.click(button);
expect(triggerOnDemandApp).toHaveBeenCalledWith(
DAY_ONE_EXPERIENCE_APP_NAME,
{
entityLink: 'entityFeedLink',
}
);
});
});

View File

@ -85,7 +85,7 @@
max-width: 148px;
}
.ant-btn.source-url-button {
.ant-btn.ant-btn-default.source-url-button {
background-color: @blue-11;
.ant-typography {

View File

@ -17,6 +17,8 @@ import { ServicesType } from '../../interface/service.interface';
export interface ServiceInsightsTabProps {
serviceDetails: ServicesType;
workflowStatesData?: WorkflowStatesData;
isWorkflowStatusLoading: boolean;
}
export interface WorkflowStatesData {
mainInstanceState: WorkflowInstance;

View File

@ -19,41 +19,32 @@ import { isUndefined, toLower } from 'lodash';
import { ServiceTypes } from 'Models';
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import {
PLATFORM_INSIGHTS_CHART,
SERVICE_INSIGHTS_WORKFLOW_DEFINITION_NAME,
} from '../../constants/ServiceInsightsTab.constants';
import { PLATFORM_INSIGHTS_CHART } from '../../constants/ServiceInsightsTab.constants';
import { SystemChartType } from '../../enums/DataInsight.enum';
import { WorkflowStatus } from '../../generated/governance/workflows/workflowInstance';
import { getMultiChartsPreviewByName } from '../../rest/DataInsightAPI';
import {
getWorkflowInstancesForApplication,
getWorkflowInstanceStateById,
} from '../../rest/workflowAPI';
import {
getCurrentDayStartGMTinMillis,
getCurrentMillis,
getDayAgoStartGMTinMillis,
} from '../../utils/date-time/DateTimeUtils';
import { getEntityFeedLink } from '../../utils/EntityUtils';
import {
getPlatformInsightsChartDataFormattingMethod,
getStatusIconFromStatusType,
} from '../../utils/ServiceInsightsTabUtils';
import serviceUtilClassBase from '../../utils/ServiceUtilClassBase';
import { getEntityTypeFromServiceCategory } from '../../utils/ServiceUtils';
import { showErrorToast } from '../../utils/ToastUtils';
import {
ChartData,
ChartSeriesData,
} from './PlatformInsightsWidget/PlatformInsightsWidget.interface';
import './service-insights-tab.less';
import {
ServiceInsightsTabProps,
WorkflowStatesData,
} from './ServiceInsightsTab.interface';
import { ServiceInsightsTabProps } from './ServiceInsightsTab.interface';
const ServiceInsightsTab = ({ serviceDetails }: ServiceInsightsTabProps) => {
const ServiceInsightsTab = ({
serviceDetails,
workflowStatesData,
isWorkflowStatusLoading,
}: ServiceInsightsTabProps) => {
const { serviceCategory } = useParams<{
serviceCategory: ServiceTypes;
tab: string;
@ -64,53 +55,11 @@ const ServiceInsightsTab = ({ serviceDetails }: ServiceInsightsTabProps) => {
tierDistributionChart: ChartData[];
}>();
const [isLoading, setIsLoading] = useState(false);
const [workflowStatesData, setWorkflowStatesData] =
useState<WorkflowStatesData>();
const [isWorkflowStatusLoading, setIsWorkflowStatusLoading] = useState(false);
const serviceName = serviceDetails.name;
const widgets = serviceUtilClassBase.getInsightsTabWidgets(serviceCategory);
const fetchWorkflowInstanceStates = async () => {
try {
setIsWorkflowStatusLoading(true);
const startTs = getDayAgoStartGMTinMillis(6);
const endTs = getCurrentMillis();
const entityType = getEntityTypeFromServiceCategory(serviceCategory);
const workflowInstances = await getWorkflowInstancesForApplication({
startTs,
endTs,
workflowDefinitionName: SERVICE_INSIGHTS_WORKFLOW_DEFINITION_NAME,
entityLink: getEntityFeedLink(
entityType,
serviceDetails.fullyQualifiedName
),
});
const workflowInstanceId = workflowInstances.data[0]?.id;
if (workflowInstanceId) {
const workflowInstanceStates = await getWorkflowInstanceStateById(
SERVICE_INSIGHTS_WORKFLOW_DEFINITION_NAME,
workflowInstanceId,
{
startTs,
endTs,
}
);
setWorkflowStatesData({
mainInstanceState: workflowInstances.data[0],
subInstanceStates: workflowInstanceStates.data,
});
}
} catch (error) {
showErrorToast(error as AxiosError);
} finally {
setIsWorkflowStatusLoading(false);
}
};
const fetchChartsData = async () => {
try {
setIsLoading(true);
@ -157,7 +106,6 @@ const ServiceInsightsTab = ({ serviceDetails }: ServiceInsightsTabProps) => {
};
useEffect(() => {
fetchWorkflowInstanceStates();
fetchChartsData();
}, []);

View File

@ -54,23 +54,21 @@ const ApplicationConfiguration = ({
};
const formPanel = (
<div className="m-auto max-width-md w-full p-lg">
<FormBuilder
useSelectWidget
cancelText={t('label.back')}
formData={appData?.appConfiguration ?? {}}
hideCancelButton={!onCancel}
isLoading={isLoading}
okText={t('label.submit')}
schema={jsonSchema}
serviceCategory={ServiceCategory.DASHBOARD_SERVICES}
uiSchema={UiSchema}
validator={validator}
onCancel={onCancel}
onFocus={handleFieldFocus}
onSubmit={onConfigSave}
/>
</div>
<FormBuilder
useSelectWidget
cancelText={t('label.back')}
formData={appData?.appConfiguration ?? {}}
hideCancelButton={!onCancel}
isLoading={isLoading}
okText={t('label.submit')}
schema={jsonSchema}
serviceCategory={ServiceCategory.DASHBOARD_SERVICES}
uiSchema={UiSchema}
validator={validator}
onCancel={onCancel}
onFocus={handleFieldFocus}
onSubmit={onConfigSave}
/>
);
const docPanel = (
@ -83,7 +81,7 @@ const ApplicationConfiguration = ({
return (
<ResizablePanels
className="content-height-with-resizable-panel border-default border-radius-sm"
className="content-height-with-resizable-panel"
firstPanel={{
children: formPanel,
minWidth: 700,

View File

@ -120,8 +120,8 @@ const SelectServiceType = ({
<Row className="service-list-container" data-testid="select-service">
{filteredConnectors.map((type) => (
<Button
className={classNames('service-box p-xs d-block border', {
'border-primary': type === selectServiceType,
className={classNames('service-box', {
'selected-service': type === selectServiceType,
})}
data-testid={type}
key={type}

View File

@ -13,15 +13,20 @@
@import (reference) url('../../../../../styles/variables.less');
.service-list-container {
display: flex;
display: grid;
grid-template-columns: repeat(5, 1fr);
margin-top: @margin-md;
gap: @margin-md;
flex-wrap: wrap;
.ant-btn.ant-btn-default.selected-service {
border-color: @primary-color;
}
.service-box {
position: relative;
height: auto;
flex: 0 0 114px;
padding: 20px 8px;
p {
white-space: pre-wrap;

View File

@ -1155,6 +1155,7 @@
"rule-name": "Regelname",
"rule-plural": "Regeln",
"run": "Ausführen",
"run-agent-plural": "Agenten ausführen",
"run-at": "Ausführen um",
"run-now": "Jetzt ausführen",
"run-type": "Ausführart",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "Suchen Sie nach übereinstimmenden Datenvermögenswerten nach \"Name\", \"Beschreibung\", \"Spaltenname\" usw. aus dem <0>{{text}}</0>.",
"tour-step-trace-path-across-tables": "Mit <0>{{text}}</0> verfolgen Sie den Pfad der Daten über Tabellen, Pipelines und Dashboards.",
"tour-step-type-search-term": "Geben Sie im Suchfeld <0>\"{{text}}\"</0> ein. Drücken Sie <0>{{enterText}}.</0>",
"trigger-day-one-application": "Day One-Anwendung auslösen",
"try-adjusting-filter": "Versuchen Sie, Ihre Suche oder Ihren Filter anzupassen, um zu finden, was Sie suchen.",
"try-different-time-period-filtering": "Keine Ergebnisse verfügbar. Versuchen Sie, nach einem anderen Zeitraum zu filtern.",
"try-extending-time-frame": "Versuchen Sie, den Zeitraum zu verlängern, um die Ergebnisse anzuzeigen.",

View File

@ -1155,6 +1155,7 @@
"rule-name": "Rule Name",
"rule-plural": "Rules",
"run": "Run",
"run-agent-plural": "Run Agents",
"run-at": "Run at",
"run-now": "Run now",
"run-type": "Run Type",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "Search for matching data assets by \"name\", \"description\", \"column name\", and so on from the <0>{{text}}</0> box.",
"tour-step-trace-path-across-tables": " With <0>{{text}}</0>, trace the path of data across tables, pipelines, & dashboards.",
"tour-step-type-search-term": "In the search box, type <0>\"{{text}}\"</0>. Hit <0>{{enterText}}.</0>",
"trigger-day-one-application": "Trigger the Day One application",
"try-adjusting-filter": "Try adjusting your search or filter to find what you are looking for.",
"try-different-time-period-filtering": "No Results Available. Try filtering by a different time period.",
"try-extending-time-frame": "Try extending the time frame to view the results.",

View File

@ -1155,6 +1155,7 @@
"rule-name": "Nombre de la Regla",
"rule-plural": "Reglas",
"run": "Ejecutar",
"run-agent-plural": "Ejecutar Agentes",
"run-at": "Ejecutar a las",
"run-now": "Ejecutar ahora",
"run-type": "Tipo de Ejecución",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "Busca activos de datos coincidentes por \"nombre\", \"descripción\", \"nombre de la columna\", y así sucesivamente desde el cuadro <0>{{text}}</0>.",
"tour-step-trace-path-across-tables": " Con <0>{{text}}</0>, sigue el camino de los datos a través de tablas, pipelines y paneles de control.",
"tour-step-type-search-term": "En el cuadro de búsqueda, escribe <0>\"{{text}}\"</0>. Presiona <0>{{enterText}}.</0>",
"trigger-day-one-application": "Ejecutar la aplicación de Día Uno",
"try-adjusting-filter": "Intenta ajustar tu búsqueda o filtro para encontrar lo que estás buscando.",
"try-different-time-period-filtering": "No hay resultados disponibles. Intente filtrar por un período de tiempo diferente.",
"try-extending-time-frame": "Intenta extender el marco de tiempo para ver los resultados.",

View File

@ -1155,6 +1155,7 @@
"rule-name": "Nom de la Règle",
"rule-plural": "Règles",
"run": "Exécuter",
"run-agent-plural": "Exécuter les Agents",
"run-at": "Run at",
"run-now": "Exécuter maintenant",
"run-type": "Run Type",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "Rechercher les actifs de données correspondant par \"nom\", \"description\", \"nom de colonne\", etc. depuis <0>{{text}}</0> box.",
"tour-step-trace-path-across-tables": " Avec <0>{{text}}</0>, suivez le chemin des données à travers les tables, les pipelines et les tableaux de bord.",
"tour-step-type-search-term": "Dans la zone de recherche, tapez <0>« {{text}} »</0>. Appuyez sur <0>{{enterText}}</0>",
"trigger-day-one-application": "Déclencher l'application Day One",
"try-adjusting-filter": "Essayez de trouver ce que vous cherchez en ajustant votre recherche ou vos filtres.",
"try-different-time-period-filtering": "Aucun résultat disponible. Essayez de filtrer par une période de temps différente.",
"try-extending-time-frame": "Essayez d'étendre la période de temps pour voir les résultats.",

View File

@ -1155,6 +1155,7 @@
"rule-name": "Nome da regra",
"rule-plural": "Regras",
"run": "Executar",
"run-agent-plural": "Executar Agentes",
"run-at": "Executar ás",
"run-now": "Executar agora",
"run-type": "Tipo de execución",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "Busca activos de datos coincidentes por \"nome\", \"descrición\", \"nome da columna\", e moito máis desde o cadro <0>{{text}}</0>.",
"tour-step-trace-path-across-tables": "Con <0>{{text}}</0>, traza o camiño dos datos entre táboas, pipelines e paneis.",
"tour-step-type-search-term": "No cadro de busca, escribe <0>\"{{text}}\"</0>. Preme <0>{{enterText}}.</0>",
"trigger-day-one-application": "Ejecutar la aplicación Day One",
"try-adjusting-filter": "Tenta axustar a túa busca ou filtro para atopar o que estás buscando.",
"try-different-time-period-filtering": "Non hai resultados dispoñibles. Proba a filtrar por un período de tempo diferente.",
"try-extending-time-frame": "Tenta ampliar o intervalo de tempo para ver os resultados.",

View File

@ -1155,6 +1155,7 @@
"rule-name": "שם הכלל",
"rule-plural": "כללים",
"run": "הרץ",
"run-agent-plural": "הפעל סוכנים",
"run-at": "הרץ ב",
"run-now": "הרץ עכשיו",
"run-type": "סוג הרצה",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "חפש נכסי נתונים תואמים לפי \"שם\", \"תיאור\", \"שם עמודה\" וכדומה מתוך <0>{{text}}</0>.",
"tour-step-trace-path-across-tables": "עם <0>{{text}}</0>, תרסה את הנתיב של הנתונים בטבלאות, תהליכי טעינה ולוחות בקרה.",
"tour-step-type-search-term": "בתיבת החיפוש, הקלד <0>\"{{text}}\"</0>. לחץ <0>{{enterText}}.</0>",
"trigger-day-one-application": "הפעל את האפליקציה Day One",
"try-adjusting-filter": "נסה להתאים את החיפוש או הסינון שלך כדי למצוא את מה שאתה מחפש.",
"try-different-time-period-filtering": "אין תוצאות זמינות. נסה לסנן לפי תקופת זמן שונה.",
"try-extending-time-frame": "Try extending the time frame to view the results.",

View File

@ -1155,6 +1155,7 @@
"rule-name": "ルール名",
"rule-plural": "Rules",
"run": "実行",
"run-agent-plural": "エージェントを実行",
"run-at": "Run at",
"run-now": "Run now",
"run-type": "Run Type",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "Search for matching data assets by \"name\", \"description\", \"column name\", and so on from the <0>{{text}}</0> box.",
"tour-step-trace-path-across-tables": "<0>{{text}}</0>で、 テーブル、パイプライン、ダッシュボードにまたがるデータのパスを追跡します。",
"tour-step-type-search-term": "In the search box, type <0>\"{{text}}\"</0>. Hit <0>{{enterText}}.</0>",
"trigger-day-one-application": "Day Oneアプリケーションをトリガーする",
"try-adjusting-filter": "Try adjusting your search or filter to find what you are looking for.",
"try-different-time-period-filtering": "結果がありません。異なる期間でのフィルタリングを試して下さい。",
"try-extending-time-frame": "Try extending the time frame to view the results.",

View File

@ -1155,6 +1155,7 @@
"rule-name": "규칙 이름",
"rule-plural": "규칙들",
"run": "실행",
"run-agent-plural": "에이전트 실행",
"run-at": "실행 시간",
"run-now": "지금 실행",
"run-type": "실행 유형",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "검색 상자에 '이름', '설명', '열 이름' 등을 입력하여 일치하는 데이터 자산을 찾으세요: <0>{{text}}</0>",
"tour-step-trace-path-across-tables": "<0>{{text}}</0>를 사용하여 테이블, 파이프라인, 대시보드 간의 데이터 경로를 추적하세요.",
"tour-step-type-search-term": "검색 상자에 <0>\"{{text}}\"</0>를 입력하고 <0>{{enterText}}</0>를 누르세요.",
"trigger-day-one-application": "Day One 애플리케이션 트리거",
"try-adjusting-filter": "원하는 항목을 찾기 위해 검색어나 필터를 조정해 보세요.",
"try-different-time-period-filtering": "결과가 없습니다. 다른 기간으로 필터링해 보세요.",
"try-extending-time-frame": "결과를 확인하려면 시간 범위를 확장해 보세요.",

View File

@ -1155,6 +1155,7 @@
"rule-name": "नियम नाव",
"rule-plural": "नियम",
"run": "चालवा",
"run-agent-plural": "एजेंट्स चलवा",
"run-at": "येथे चालवा",
"run-now": "आता चालवा",
"run-type": "चालवा प्रकार",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "<0>{{text}}</0> बॉक्समधून \"नाव\", \"वर्णन\", \"स्तंभ नाव\" आणि अशा प्रकारे जुळणारे डेटा ॲसेट्स शोधा.",
"tour-step-trace-path-across-tables": "<0>{{text}}</0> सह, टेबल्स, पाइपलाइन आणि डॅशबोर्ड्समध्ये डेटाचा मार्ग शोधा.",
"tour-step-type-search-term": "शोध बॉक्समध्ये, <0>\"{{text}}\"</0> टाइप करा. <0>{{enterText}}</0> दाबा.",
"trigger-day-one-application": "दिवस एक अॅप्लिकेशन ट्रिगर करा",
"try-adjusting-filter": "तुम्ही शोधत असलेले शोधण्यासाठी तुमचा शोध किंवा फिल्टर समायोजित करण्याचा प्रयत्न करा.",
"try-different-time-period-filtering": "कोणतेही परिणाम उपलब्ध नाहीत. वेगळ्या कालावधीने फिल्टर करण्याचा प्रयत्न करा.",
"try-extending-time-frame": "परिणाम पाहण्यासाठी वेळेची चौकट वाढवण्याचा प्रयत्न करा.",

View File

@ -1155,6 +1155,7 @@
"rule-name": "Regelnaam",
"rule-plural": "Regels",
"run": "Uitvoeren",
"run-agent-plural": "Agents Uitvoeren",
"run-at": "Uitvoeren op",
"run-now": "Nu uitvoeren",
"run-type": "Uitvoertype",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "Zoek naar overeenkomende data-assets op \"naam\", \"beschrijving\", \"kolomnaam\", enzovoort vanuit het <0>{{text}}</0> vak.",
"tour-step-trace-path-across-tables": "Met <0>{{text}}</0> kun je het pad van data over tabellen, pipelines en dashboards volgen.",
"tour-step-type-search-term": "Typ in het zoekvak <0>\"{{text}}\"</0>. Druk op <0>{{enterText}}.</0>",
"trigger-day-one-application": "De Day One-toepassing activeren",
"try-adjusting-filter": "Probeer je zoekopdracht of filter aan te passen om te vinden wat je zoekt.",
"try-different-time-period-filtering": "Geen resultaten beschikbaar. Probeer te filteren op een ander tijdsinterval.",
"try-extending-time-frame": "Try extending the time frame to view the results.",

View File

@ -1,6 +1,6 @@
{
"label": {
"aborted": "Interrumpido",
"aborted": "لغو شده",
"accept": "پذیرفتن",
"accept-all": "پذیرفتن همه",
"accept-suggestion": "پذیرفتن پیشنهاد",
@ -1155,6 +1155,7 @@
"rule-name": "نام قانون",
"rule-plural": "قوانین",
"run": "اجرا",
"run-agent-plural": "اجرای عامل‌ها",
"run-at": "اجرا در",
"run-now": "الان اجرا کنید",
"run-type": "نوع اجرا",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "برای جستجوی دارایی‌های داده مشابه بر اساس \"نام\"، \"توضیحات\"، \"نام ستون\" و غیره از جعبه جستجو <0>{{text}}</0> استفاده کنید.",
"tour-step-trace-path-across-tables": "با <0>{{text}}</0>، مسیر داده را در سراسر جداول، خطوط لوله و داشبوردها دنبال کنید.",
"tour-step-type-search-term": "در جعبه جستجو، <0>\"{{text}}\"</0> را تایپ کنید. سپس <0>{{enterText}}.</0> را فشار دهید.",
"trigger-day-one-application": "تریگر یکی از اپلیکیشن‌های Day One",
"try-adjusting-filter": "سعی کنید جستجو یا فیلتر خود را تنظیم کنید تا آنچه را که به دنبال آن هستید پیدا کنید.",
"try-different-time-period-filtering": "نتایج موجود نیست. سعی کنید با یک دوره زمانی متفاوت فیلتر کنید.",
"try-extending-time-frame": "سعی کنید بازه زمانی را گسترش دهید تا نتایج را مشاهده کنید.",

View File

@ -1155,6 +1155,7 @@
"rule-name": "Nome da Regra",
"rule-plural": "Regras",
"run": "Executar",
"run-agent-plural": "Executar Agentes",
"run-at": "Executar em",
"run-now": "Executar agora",
"run-type": "Tipo de Execução",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "Pesquise ativos de dados correspondentes por \"nome\", \"descrição\", \"nome da coluna\", e assim por diante a partir da caixa de <0>{{text}}</0>.",
"tour-step-trace-path-across-tables": "Com <0>{{text}}</0>, trace o caminho dos dados através de tabelas, pipelines e painéis.",
"tour-step-type-search-term": "Na caixa de pesquisa, digite <0>\"{{text}}\"</0>. Pressione <0>{{enterText}}.</0>",
"trigger-day-one-application": "Ativar a aplicação Day One",
"try-adjusting-filter": "Tente ajustar sua pesquisa ou filtro para encontrar o que está procurando.",
"try-different-time-period-filtering": "Nenhum resultado disponível. Tente filtrar por um período de tempo diferente.",
"try-extending-time-frame": "Tente aumentar o período de tempo para visualizar os resultados.",

View File

@ -1155,6 +1155,7 @@
"rule-name": "Nome da Regra",
"rule-plural": "Regras",
"run": "Executar",
"run-agent-plural": "Executar Agentes",
"run-at": "Executar em",
"run-now": "Executar agora",
"run-type": "Tipo de Execução",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "Pesquise ativos de dados correspondentes por \"nome\", \"descrição\", \"nome da coluna\", e assim por diante a partir da caixa de <0>{{text}}</0>.",
"tour-step-trace-path-across-tables": "Com <0>{{text}}</0>, trace o caminho dos dados através de tabelas, pipelines e painéis.",
"tour-step-type-search-term": "Na caixa de pesquisa, digite <0>\"{{text}}\"</0>. Pressione <0>{{enterText}}.</0>",
"trigger-day-one-application": "Ativar a aplicação Day One",
"try-adjusting-filter": "Tente ajustar sua pesquisa ou filtro para encontrar o que está procurando.",
"try-different-time-period-filtering": "Nenhum resultado disponível. Tente filtrar por um período de tempo diferente.",
"try-extending-time-frame": "Tente aumentar o período de tempo para visualizar os resultados.",

View File

@ -1155,6 +1155,7 @@
"rule-name": "Наименование правила",
"rule-plural": "Правила",
"run": "Запустить",
"run-agent-plural": "Запустить агенты",
"run-at": "Run at",
"run-now": "Run now",
"run-type": "Run Type",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "Найдите соответствующие объекты данных по «имени», «описанию», «названию столбца» и т. д. в поле <0>{{text}}</0>.",
"tour-step-trace-path-across-tables": "С помощью <0>{{text}}</0> отслеживайте путь данных между таблицами, пайплайнами и дашбордами.",
"tour-step-type-search-term": "В поле поиска введите <0>\"{{текст}}\"</0>. Нажмите <0>{{enterText}}.</0>",
"trigger-day-one-application": "Применить приложение Day One",
"try-adjusting-filter": "Попробуйте настроить поиск или фильтр, чтобы найти то, что вы ищете.",
"try-different-time-period-filtering": "Нет доступных результатов. Попробуйте отфильтровать по другому периоду времени.",
"try-extending-time-frame": "Try extending the time frame to view the results.",

View File

@ -1155,6 +1155,7 @@
"rule-name": "ชื่อกฎ",
"rule-plural": "กฎหลายรายการ",
"run": "รัน",
"run-agent-plural": "รันเอเจนต์",
"run-at": "รันที่",
"run-now": "รันตอนนี้",
"run-type": "ประเภทการรัน",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "ค้นหาสินทรัพย์ข้อมูลที่ตรงกันโดยใช้ \"ชื่อ\", \"คำอธิบาย\", \"ชื่อคอลัมน์\" และอื่น ๆ จากกล่อง <0>{{text}}</0>",
"tour-step-trace-path-across-tables": "ด้วย <0>{{text}}</0>, ติดตามเส้นทางของข้อมูลข้ามตาราง, ท่อ, & แดชบอร์ด",
"tour-step-type-search-term": "ในกล่องค้นหา, พิมพ์ <0>\"{{text}}\"</0>. กด <0>{{enterText}}</0>",
"trigger-day-one-application": "เปิดใช้งานแอพพลิเคชัน Day One",
"try-adjusting-filter": "ลองปรับการค้นหาหรือกรองเพื่อหาสิ่งที่คุณกำลังมองหา",
"try-different-time-period-filtering": "ไม่มีผลลัพธ์ที่สามารถใช้งานได้ ลองกรองด้วยช่วงเวลาที่แตกต่างกัน",
"try-extending-time-frame": "ลองขยายกรอบเวลาเพื่อดูผลลัพธ์",

View File

@ -1155,6 +1155,7 @@
"rule-name": "规则名称",
"rule-plural": "规则",
"run": "运行",
"run-agent-plural": "运行代理",
"run-at": "运行于",
"run-now": "立即运行",
"run-type": "运行类型",
@ -2110,6 +2111,7 @@
"tour-step-search-for-matching-dataset": "通过在<0>{{text}}</0>框中输入\"名称\"、\"描述\"、\"列名\"等关键字搜索匹配数据资产。",
"tour-step-trace-path-across-tables": "使用<0>{{text}}</0>, 跟踪数据在表、工作流和仪表板之间的路径。",
"tour-step-type-search-term": "在搜索框中输入<0>\"{{text}}\"</0>, 并在键盘上点击<0>{{enterText}}</0>回车键。",
"trigger-day-one-application": "触发 Day One 应用程序",
"try-adjusting-filter": "没有找到相关的数据, 请修改搜索或过滤条件。",
"try-different-time-period-filtering": "没有可用的结果, 请选择不同的时间段再次过滤",
"try-extending-time-frame": "尝试延长时间框架以查看结果",

View File

@ -268,6 +268,7 @@ const AddServicePage = () => {
{activeServiceStep === 3 && (
<ConnectionConfigForm
cancelText={t('label.back')}
data={serviceConfig as ServicesType}
okText={t('label.next')}
serviceCategory={serviceCategory}
serviceType={serviceConfig.serviceType}

View File

@ -40,6 +40,7 @@ import { DataAssetsHeader } from '../../components/DataAssets/DataAssetsHeader/D
import { EntityName } from '../../components/Modals/EntityNameModal/EntityNameModal.interface';
import PageLayoutV1 from '../../components/PageLayoutV1/PageLayoutV1';
import ServiceInsightsTab from '../../components/ServiceInsights/ServiceInsightsTab';
import { WorkflowStatesData } from '../../components/ServiceInsights/ServiceInsightsTab.interface';
import Ingestion from '../../components/Settings/Services/Ingestion/Ingestion.component';
import ServiceConnectionDetails from '../../components/Settings/Services/ServiceConnectionDetails/ServiceConnectionDetails.component';
import {
@ -49,6 +50,7 @@ import {
ROUTES,
} from '../../constants/constants';
import { GlobalSettingsMenuCategory } from '../../constants/GlobalSettings.constants';
import { SERVICE_INSIGHTS_WORKFLOW_DEFINITION_NAME } from '../../constants/ServiceInsightsTab.constants';
import {
OPEN_METADATA,
SERVICE_INGESTION_PIPELINE_TYPES,
@ -78,6 +80,7 @@ import { StoredProcedure } from '../../generated/entity/data/storedProcedure';
import { Topic } from '../../generated/entity/data/topic';
import { DashboardConnection } from '../../generated/entity/services/dashboardService';
import { IngestionPipeline } from '../../generated/entity/services/ingestionPipelines/ingestionPipeline';
import { WorkflowStatus } from '../../generated/governance/workflows/workflowInstance';
import { Include } from '../../generated/type/include';
import { Paging } from '../../generated/type/paging';
import { useAuth } from '../../hooks/authHooks';
@ -106,9 +109,17 @@ import {
} from '../../rest/serviceAPI';
import { getContainers } from '../../rest/storageAPI';
import { getTopics } from '../../rest/topicsAPI';
import {
getWorkflowInstancesForApplication,
getWorkflowInstanceStateById,
} from '../../rest/workflowAPI';
import { getEntityMissingError } from '../../utils/CommonUtils';
import {
getCurrentMillis,
getDayAgoStartGMTinMillis,
} from '../../utils/date-time/DateTimeUtils';
import entityUtilClassBase from '../../utils/EntityUtilClassBase';
import { getEntityName } from '../../utils/EntityUtils';
import { getEntityFeedLink, getEntityName } from '../../utils/EntityUtils';
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
import {
getEditConnectionPath,
@ -173,6 +184,9 @@ const ServiceDetailsPage: FunctionComponent = () => {
const ingestionPagingInfo = usePaging(PAGE_SIZE_BASE);
const collateAgentPagingInfo = usePaging(PAGE_SIZE_BASE);
const pagingInfo = usePaging(PAGE_SIZE_BASE);
const [workflowStatesData, setWorkflowStatesData] =
useState<WorkflowStatesData>();
const [isWorkflowStatusLoading, setIsWorkflowStatusLoading] = useState(true);
const {
paging: collateAgentPaging,
@ -354,6 +368,45 @@ const ServiceDetailsPage: FunctionComponent = () => {
[activeTab, decodedServiceFQN, serviceCategory]
);
const fetchWorkflowInstanceStates = useCallback(async () => {
try {
setIsWorkflowStatusLoading(true);
const startTs = getDayAgoStartGMTinMillis(6);
const endTs = getCurrentMillis();
const entityType = getEntityTypeFromServiceCategory(serviceCategory);
const workflowInstances = await getWorkflowInstancesForApplication({
startTs,
endTs,
workflowDefinitionName: SERVICE_INSIGHTS_WORKFLOW_DEFINITION_NAME,
entityLink: getEntityFeedLink(
entityType,
serviceDetails.fullyQualifiedName
),
});
const workflowInstanceId = workflowInstances.data[0]?.id;
if (workflowInstanceId) {
const workflowInstanceStates = await getWorkflowInstanceStateById(
SERVICE_INSIGHTS_WORKFLOW_DEFINITION_NAME,
workflowInstanceId,
{
startTs,
endTs,
}
);
setWorkflowStatesData({
mainInstanceState: workflowInstances.data[0],
subInstanceStates: workflowInstanceStates.data,
});
}
} catch (error) {
showErrorToast(error as AxiosError);
} finally {
setIsWorkflowStatusLoading(false);
}
}, [serviceDetails.fullyQualifiedName, serviceCategory]);
const fetchCollateAgentsList = useCallback(
async (paging?: Omit<Paging, 'total'>) => {
try {
@ -661,7 +714,7 @@ const ServiceDetailsPage: FunctionComponent = () => {
default:
break;
}
} catch (error) {
} catch {
setData([]);
handlePagingChange(pagingObject);
} finally {
@ -989,6 +1042,15 @@ const ServiceDetailsPage: FunctionComponent = () => {
]
);
const disableRunAgentsButton = useMemo(
() =>
workflowStatesData?.mainInstanceState.status &&
![WorkflowStatus.Exception, WorkflowStatus.Failure].includes(
workflowStatesData?.mainInstanceState.status
),
[workflowStatesData?.mainInstanceState.status]
);
useEffect(() => {
handlePageChange(INITIAL_PAGING_VALUE);
getOtherDetails({ limit: pageSize });
@ -1038,6 +1100,10 @@ const ServiceDetailsPage: FunctionComponent = () => {
}
}, [collateAgentPageSize]);
useEffect(() => {
fetchWorkflowInstanceStates();
}, [serviceDetails.fullyQualifiedName]);
const agentCounts = useMemo(() => {
return {
[ServiceAgentSubTabs.COLLATE_AI]: collateAgentPaging.total,
@ -1209,7 +1275,13 @@ const ServiceDetailsPage: FunctionComponent = () => {
{
name: t('label.insight-plural'),
key: EntityTabs.INSIGHTS,
children: <ServiceInsightsTab serviceDetails={serviceDetails} />,
children: (
<ServiceInsightsTab
isWorkflowStatusLoading={isWorkflowStatusLoading}
serviceDetails={serviceDetails}
workflowStatesData={workflowStatesData}
/>
),
},
{
name: getCountLabel(serviceCategory),
@ -1298,6 +1370,8 @@ const ServiceDetailsPage: FunctionComponent = () => {
testConnectionTab,
activeTab,
isMetadataService,
workflowStatesData,
isWorkflowStatusLoading,
]);
if (isLoading) {
@ -1320,14 +1394,17 @@ const ServiceDetailsPage: FunctionComponent = () => {
</ErrorPlaceHolder>
) : (
<Row data-testid="service-page" gutter={[0, 12]}>
<Col className="" span={24}>
<Col span={24}>
<DataAssetsHeader
isRecursiveDelete
afterDeleteAction={afterDeleteAction}
afterDomainUpdateAction={afterDomainUpdateAction}
afterTriggerAction={fetchWorkflowInstanceStates}
dataAsset={serviceDetails}
disableRunAgentsButton={disableRunAgentsButton}
entityType={entityType}
extraDropdownContent={extraDropdownContent}
isDayOneWorkflowStatusLoading={isWorkflowStatusLoading}
permissions={servicePermission}
showDomain={!isMetadataService}
onDisplayNameUpdate={handleUpdateDisplayName}

View File

@ -242,83 +242,81 @@ function ServiceMainTabContent({
firstPanel={{
className: 'entity-resizable-panel-container',
children: (
<div className="p-t-sm m-x-lg">
<Row gutter={[16, 16]}>
<Col data-testid="description-container" span={24}>
<DescriptionV1
description={serviceDetails.description}
entityName={serviceName}
entityType={entityType}
hasEditAccess={editDescriptionPermission}
showActions={!serviceDetails.deleted}
showCommentsIcon={false}
onDescriptionUpdate={handleDescriptionUpdate}
/>
</Col>
<Col data-testid="table-container" span={24}>
<Space
className="w-full m-b-md"
direction="vertical"
size="large">
{isServiceLoading ? (
<Loader />
) : (
<Table
columns={tableColumn}
customPaginationProps={{
currentPage,
isLoading: isServiceLoading,
showPagination:
!isUndefined(pagingInfo) &&
pagingInfo.showPagination,
pageSize: pagingInfo.pageSize,
paging,
<Row gutter={[16, 16]}>
<Col data-testid="description-container" span={24}>
<DescriptionV1
description={serviceDetails.description}
entityName={serviceName}
entityType={entityType}
hasEditAccess={editDescriptionPermission}
showActions={!serviceDetails.deleted}
showCommentsIcon={false}
onDescriptionUpdate={handleDescriptionUpdate}
/>
</Col>
<Col data-testid="table-container" span={24}>
<Space
className="w-full m-b-md"
direction="vertical"
size="large">
{isServiceLoading ? (
<Loader />
) : (
<Table
columns={tableColumn}
customPaginationProps={{
currentPage,
isLoading: isServiceLoading,
showPagination:
!isUndefined(pagingInfo) &&
pagingInfo.showPagination,
pageSize: pagingInfo.pageSize,
paging,
pagingHandler: pagingHandler,
onShowSizeChange: pagingInfo.handlePageSizeChange,
}}
data-testid="service-children-table"
dataSource={pageData}
defaultVisibleColumns={
DEFAULT_SERVICE_TAB_VISIBLE_COLUMNS
}
entityType={serviceCategory}
extraTableFilters={
<>
<span>
<Switch
checked={showDeleted}
data-testid="show-deleted"
onClick={onShowDeletedChange}
/>
<Typography.Text className="m-l-xs">
{t('label.deleted')}
</Typography.Text>
</span>
pagingHandler: pagingHandler,
onShowSizeChange: pagingInfo.handlePageSizeChange,
}}
data-testid="service-children-table"
dataSource={pageData}
defaultVisibleColumns={
DEFAULT_SERVICE_TAB_VISIBLE_COLUMNS
}
entityType={serviceCategory}
extraTableFilters={
<>
<span>
<Switch
checked={showDeleted}
data-testid="show-deleted"
onClick={onShowDeletedChange}
/>
<Typography.Text className="m-l-xs">
{t('label.deleted')}
</Typography.Text>
</span>
{entityType === EntityType.DATABASE_SERVICE &&
getBulkEditButton(
servicePermission.EditAll &&
!serviceDetails.deleted,
handleEditTable
)}
</>
}
locale={{
emptyText: <ErrorPlaceHolder className="m-y-md" />,
}}
pagination={false}
rowKey="id"
size="small"
staticVisibleColumns={
COMMON_STATIC_TABLE_VISIBLE_COLUMNS
}
/>
)}
</Space>
</Col>
</Row>
</div>
{entityType === EntityType.DATABASE_SERVICE &&
getBulkEditButton(
servicePermission.EditAll &&
!serviceDetails.deleted,
handleEditTable
)}
</>
}
locale={{
emptyText: <ErrorPlaceHolder className="m-y-md" />,
}}
pagination={false}
rowKey="id"
size="small"
staticVisibleColumns={
COMMON_STATIC_TABLE_VISIBLE_COLUMNS
}
/>
)}
</Space>
</Col>
</Row>
),
...COMMON_RESIZABLE_PANEL_CONFIG.LEFT_PANEL,
}}

View File

@ -45,7 +45,6 @@
background-color: transparent;
}
.main-tab-content,
.connection-tab-content {
background-color: @white;
border-radius: @border-radius-sm;

View File

@ -117,9 +117,24 @@ button {
height: 40px;
min-width: 50px;
padding: 10px 16px;
background-color: @background-color;
border-radius: 8px;
box-shadow: 0px 1px 2px 0px #0a0d120f, 0px 1px 3px 0px #0a0d121a;
}
.ant-btn-primary:first-child:not(:last-child) {
border-right-color: transparent;
}
.ant-btn-default:not(.ant-btn-primary):not([disabled]) {
border-left-color: @blue-15;
&:hover {
border-left-color: @primary-color;
}
}
.ant-btn-default {
background-color: @background-color;
&:hover {
background-color: @blue-11;