mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-12-16 18:15:17 +00:00
Data Contract UI Improvement (#22705)
* Data Contract UI Improvement * fix the semantic card not visibel on expand and switch not working on outside * fix the add new semantic not being disbaled on first edit * added the status badge for latestRun in DataAssetHeader
This commit is contained in:
parent
287c1b6138
commit
34cd7178e2
@ -0,0 +1,16 @@
|
|||||||
|
<svg viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g opacity="0.3">
|
||||||
|
<path d="M13.999 5.19995C18.8591 5.19995 22.7987 9.13974 22.7988 13.9998C22.7988 18.8599 18.8591 22.7996 13.999 22.7996C9.13901 22.7995 5.19922 18.8598 5.19922 13.9998C5.19932 9.13981 9.13907 5.20006 13.999 5.19995Z" stroke="#D92D20" stroke-width="2"/>
|
||||||
|
</g>
|
||||||
|
<g opacity="0.1">
|
||||||
|
<path d="M13.999 1.69995C20.7921 1.69995 26.2987 7.20675 26.2988 13.9998C26.2988 20.7929 20.7921 26.2996 13.999 26.2996C7.20601 26.2995 1.69922 20.7928 1.69922 13.9998C1.69932 7.20681 7.20608 1.70006 13.999 1.69995Z" stroke="#D92D20" stroke-width="2"/>
|
||||||
|
</g>
|
||||||
|
<g clip-path="url(#clip0_542_17892)">
|
||||||
|
<path d="M15.7513 12.2501L12.2513 15.7501M12.2513 12.2501L15.7513 15.7501M19.8346 14.0001C19.8346 17.2217 17.223 19.8334 14.0013 19.8334C10.7796 19.8334 8.16797 17.2217 8.16797 14.0001C8.16797 10.7784 10.7796 8.16675 14.0013 8.16675C17.223 8.16675 19.8346 10.7784 19.8346 14.0001Z" stroke="#D92D20" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_542_17892">
|
||||||
|
<rect width="14" height="14" fill="white" transform="translate(7 7)"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
@ -11,7 +11,16 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import Icon from '@ant-design/icons';
|
import Icon from '@ant-design/icons';
|
||||||
import { Button, Col, Divider, Row, Space, Tooltip, Typography } from 'antd';
|
import {
|
||||||
|
Button,
|
||||||
|
Col,
|
||||||
|
Divider,
|
||||||
|
Row,
|
||||||
|
Space,
|
||||||
|
Tag,
|
||||||
|
Tooltip,
|
||||||
|
Typography,
|
||||||
|
} from 'antd';
|
||||||
import ButtonGroup from 'antd/lib/button/button-group';
|
import ButtonGroup from 'antd/lib/button/button-group';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
@ -46,6 +55,7 @@ import {
|
|||||||
import { ServiceCategory } from '../../../enums/service.enum';
|
import { ServiceCategory } from '../../../enums/service.enum';
|
||||||
import { LineageLayer } from '../../../generated/configuration/lineageSettings';
|
import { LineageLayer } from '../../../generated/configuration/lineageSettings';
|
||||||
import { Container } from '../../../generated/entity/data/container';
|
import { Container } from '../../../generated/entity/data/container';
|
||||||
|
import { ContractExecutionStatus } from '../../../generated/entity/data/dataContract';
|
||||||
import { Table } from '../../../generated/entity/data/table';
|
import { Table } from '../../../generated/entity/data/table';
|
||||||
import { Thread } from '../../../generated/entity/feed/thread';
|
import { Thread } from '../../../generated/entity/feed/thread';
|
||||||
import { useApplicationStore } from '../../../hooks/useApplicationStore';
|
import { useApplicationStore } from '../../../hooks/useApplicationStore';
|
||||||
@ -62,6 +72,7 @@ import {
|
|||||||
import EntityLink from '../../../utils/EntityLink';
|
import EntityLink from '../../../utils/EntityLink';
|
||||||
import entityUtilClassBase from '../../../utils/EntityUtilClassBase';
|
import entityUtilClassBase from '../../../utils/EntityUtilClassBase';
|
||||||
import {
|
import {
|
||||||
|
getDataContractStatusIcon,
|
||||||
getEntityFeedLink,
|
getEntityFeedLink,
|
||||||
getEntityName,
|
getEntityName,
|
||||||
getEntityVoteStatus,
|
getEntityVoteStatus,
|
||||||
@ -100,6 +111,7 @@ export const DataAssetsHeader = ({
|
|||||||
showDomain = true,
|
showDomain = true,
|
||||||
afterDeleteAction,
|
afterDeleteAction,
|
||||||
dataAsset,
|
dataAsset,
|
||||||
|
dataContract,
|
||||||
onUpdateVote,
|
onUpdateVote,
|
||||||
onOwnerUpdate,
|
onOwnerUpdate,
|
||||||
onTierUpdate,
|
onTierUpdate,
|
||||||
@ -421,6 +433,25 @@ export const DataAssetsHeader = ({
|
|||||||
selectedUserSuggestions,
|
selectedUserSuggestions,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const dataContractLatestResultButton = useMemo(() => {
|
||||||
|
if (dataContract?.latestResult?.status === ContractExecutionStatus.Failed) {
|
||||||
|
return (
|
||||||
|
<Tag
|
||||||
|
className={classNames(
|
||||||
|
`data-contract-latest-result-button
|
||||||
|
${dataContract?.latestResult?.status}`
|
||||||
|
)}>
|
||||||
|
{getDataContractStatusIcon(dataContract?.latestResult?.status)}
|
||||||
|
{t('label.entity-failed', {
|
||||||
|
entity: t('label.contract'),
|
||||||
|
})}
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}, [dataContract]);
|
||||||
|
|
||||||
const triggerTheAutoPilotApplication = useCallback(async () => {
|
const triggerTheAutoPilotApplication = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setIsAutoPilotTriggering(true);
|
setIsAutoPilotTriggering(true);
|
||||||
@ -517,6 +548,8 @@ export const DataAssetsHeader = ({
|
|||||||
data-testid="asset-header-btn-group"
|
data-testid="asset-header-btn-group"
|
||||||
size="small">
|
size="small">
|
||||||
{triggerAutoPilotApplicationButton}
|
{triggerAutoPilotApplicationButton}
|
||||||
|
{dataContractLatestResultButton}
|
||||||
|
|
||||||
{onUpdateVote && (
|
{onUpdateVote && (
|
||||||
<Voting
|
<Voting
|
||||||
disabled={deleted}
|
disabled={deleted}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import { Dashboard } from '../../../generated/entity/data/dashboard';
|
|||||||
import { DashboardDataModel } from '../../../generated/entity/data/dashboardDataModel';
|
import { DashboardDataModel } from '../../../generated/entity/data/dashboardDataModel';
|
||||||
import { Database } from '../../../generated/entity/data/database';
|
import { Database } from '../../../generated/entity/data/database';
|
||||||
import { DatabaseSchema } from '../../../generated/entity/data/databaseSchema';
|
import { DatabaseSchema } from '../../../generated/entity/data/databaseSchema';
|
||||||
|
import { DataContract } from '../../../generated/entity/data/dataContract';
|
||||||
import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm';
|
import { GlossaryTerm } from '../../../generated/entity/data/glossaryTerm';
|
||||||
import { Metric } from '../../../generated/entity/data/metric';
|
import { Metric } from '../../../generated/entity/data/metric';
|
||||||
import { Mlmodel } from '../../../generated/entity/data/mlmodel';
|
import { Mlmodel } from '../../../generated/entity/data/mlmodel';
|
||||||
@ -115,6 +116,7 @@ export type DataAssetWithDomains =
|
|||||||
| GlossaryTerm;
|
| GlossaryTerm;
|
||||||
|
|
||||||
export type DataAssetsHeaderProps = {
|
export type DataAssetsHeaderProps = {
|
||||||
|
dataContract?: DataContract;
|
||||||
permissions: OperationPermission;
|
permissions: OperationPermission;
|
||||||
openTaskCount?: number;
|
openTaskCount?: number;
|
||||||
allowSoftDelete?: boolean;
|
allowSoftDelete?: boolean;
|
||||||
|
|||||||
@ -128,4 +128,26 @@
|
|||||||
fill: @de-active-color;
|
fill: @de-active-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.data-contract-latest-result-button {
|
||||||
|
font-size: 14px;
|
||||||
|
&.Failed {
|
||||||
|
color: @red-14;
|
||||||
|
font-weight: 600;
|
||||||
|
border: 1px solid @red-19;
|
||||||
|
background-color: @red-9;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
box-shadow: 0px 2px 2px -1px @grey-35, 0px 4px 6px -2px @grey-35,
|
||||||
|
0px 12px 16px -4px @grey-35;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
font-size: 26px;
|
||||||
|
fill: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,8 +18,10 @@ import React, { useEffect, useMemo, useState } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg';
|
import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg';
|
||||||
import { ReactComponent as EmptyContractIcon } from '../../../assets/svg/empty-contract.svg';
|
import { ReactComponent as EmptyContractIcon } from '../../../assets/svg/empty-contract.svg';
|
||||||
|
import { ReactComponent as FailIcon } from '../../../assets/svg/fail-badge.svg';
|
||||||
import { ReactComponent as FlagIcon } from '../../../assets/svg/flag.svg';
|
import { ReactComponent as FlagIcon } from '../../../assets/svg/flag.svg';
|
||||||
import { ReactComponent as CheckIcon } from '../../../assets/svg/ic-check-circle.svg';
|
import { ReactComponent as CheckIcon } from '../../../assets/svg/ic-check-circle.svg';
|
||||||
|
import { ReactComponent as DefaultIcon } from '../../../assets/svg/ic-task.svg';
|
||||||
import { ReactComponent as DeleteIcon } from '../../../assets/svg/ic-trash.svg';
|
import { ReactComponent as DeleteIcon } from '../../../assets/svg/ic-trash.svg';
|
||||||
|
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
@ -52,6 +54,7 @@ import { getRelativeTime } from '../../../utils/date-time/DateTimeUtils';
|
|||||||
import { getEntityName } from '../../../utils/EntityUtils';
|
import { getEntityName } from '../../../utils/EntityUtils';
|
||||||
import { pruneEmptyChildren } from '../../../utils/TableUtils';
|
import { pruneEmptyChildren } from '../../../utils/TableUtils';
|
||||||
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
|
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
|
||||||
|
import AlertBar from '../../AlertBar/AlertBar';
|
||||||
import DescriptionV1 from '../../common/EntityDescription/DescriptionV1';
|
import DescriptionV1 from '../../common/EntityDescription/DescriptionV1';
|
||||||
import ErrorPlaceHolderNew from '../../common/ErrorWithPlaceholder/ErrorPlaceHolderNew';
|
import ErrorPlaceHolderNew from '../../common/ErrorWithPlaceholder/ErrorPlaceHolderNew';
|
||||||
import ExpandableCard from '../../common/ExpandableCard/ExpandableCard';
|
import ExpandableCard from '../../common/ExpandableCard/ExpandableCard';
|
||||||
@ -171,6 +174,22 @@ const ContractDetail: React.FC<{
|
|||||||
return getTestCaseSummaryChartItems(testCaseSummary);
|
return getTestCaseSummaryChartItems(testCaseSummary);
|
||||||
}, [testCaseSummary]);
|
}, [testCaseSummary]);
|
||||||
|
|
||||||
|
const getSemanticIconPerLastExecution = (semanticName: string) => {
|
||||||
|
if (!latestContractResults) {
|
||||||
|
return DefaultIcon;
|
||||||
|
}
|
||||||
|
const isRuleFailed =
|
||||||
|
latestContractResults?.semanticsValidation?.failedRules?.find(
|
||||||
|
(rule) => rule.ruleName === semanticName
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isRuleFailed) {
|
||||||
|
return FailIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CheckIcon;
|
||||||
|
};
|
||||||
|
|
||||||
const getTestCaseStatusIcon = (record: TestCase) => (
|
const getTestCaseStatusIcon = (record: TestCase) => (
|
||||||
<Icon
|
<Icon
|
||||||
className="test-status-icon"
|
className="test-status-icon"
|
||||||
@ -389,38 +408,49 @@ const ContractDetail: React.FC<{
|
|||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Loading />
|
<Loading />
|
||||||
) : (
|
) : (
|
||||||
constraintStatus.map((item) => (
|
<>
|
||||||
<div
|
{latestContractResults?.result && (
|
||||||
className="contract-status-card-item d-flex justify-between items-center"
|
<AlertBar
|
||||||
key={item.label}>
|
defafultExpand
|
||||||
<div className="d-flex items-center">
|
className="h-full m-b-md"
|
||||||
<Icon
|
message={latestContractResults.result}
|
||||||
className="contract-status-card-icon"
|
type="error"
|
||||||
component={item.icon}
|
/>
|
||||||
data-testid={`${item.label}-icon`}
|
)}
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="d-flex flex-column m-l-md">
|
{constraintStatus.map((item) => (
|
||||||
<Typography.Text className="contract-status-card-label">
|
<div
|
||||||
{item.label}
|
className="contract-status-card-item d-flex justify-between items-center"
|
||||||
</Typography.Text>
|
key={item.label}>
|
||||||
<div>
|
<div className="d-flex items-center">
|
||||||
<Typography.Text className="contract-status-card-desc">
|
<Icon
|
||||||
{item.desc}
|
className="contract-status-card-icon"
|
||||||
</Typography.Text>
|
component={item.icon}
|
||||||
<Typography.Text className="contract-status-card-time">
|
data-testid={`${item.label}-icon`}
|
||||||
{item.time}
|
/>
|
||||||
|
|
||||||
|
<div className="d-flex flex-column m-l-md">
|
||||||
|
<Typography.Text className="contract-status-card-label">
|
||||||
|
{item.label}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
<div>
|
||||||
|
<Typography.Text className="contract-status-card-desc">
|
||||||
|
{item.desc}
|
||||||
|
</Typography.Text>
|
||||||
|
<Typography.Text className="contract-status-card-time">
|
||||||
|
{item.time}
|
||||||
|
</Typography.Text>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<StatusBadgeV2
|
<StatusBadgeV2
|
||||||
label={item.status}
|
label={item.status}
|
||||||
status={getContractStatusType(item.status)}
|
status={getContractStatusType(item.status)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))
|
))}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</ExpandableCard>
|
</ExpandableCard>
|
||||||
</Col>
|
</Col>
|
||||||
@ -450,7 +480,12 @@ const ContractDetail: React.FC<{
|
|||||||
<div className="rule-item-container">
|
<div className="rule-item-container">
|
||||||
{(contract?.semantics ?? []).map((item) => (
|
{(contract?.semantics ?? []).map((item) => (
|
||||||
<div className="rule-item">
|
<div className="rule-item">
|
||||||
<Icon className="rule-icon" component={CheckIcon} />
|
<Icon
|
||||||
|
className="rule-icon"
|
||||||
|
component={getSemanticIconPerLastExecution(
|
||||||
|
item.name
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<span className="rule-name">{item.name}</span>{' '}
|
<span className="rule-name">{item.name}</span>{' '}
|
||||||
<span className="rule-description">
|
<span className="rule-description">
|
||||||
{item.description}
|
{item.description}
|
||||||
|
|||||||
@ -12,12 +12,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Icon, { ArrowLeftOutlined, ArrowRightOutlined } from '@ant-design/icons';
|
import Icon, { ArrowLeftOutlined, ArrowRightOutlined } from '@ant-design/icons';
|
||||||
|
import { Actions } from '@react-awesome-query-builder/antd';
|
||||||
import { FieldErrorProps } from '@rjsf/utils';
|
import { FieldErrorProps } from '@rjsf/utils';
|
||||||
import { Button, Col, Form, Input, Row, Switch, Typography } from 'antd';
|
import { Button, Col, Form, Input, Row, Switch, Typography } from 'antd';
|
||||||
import Card from 'antd/lib/card/Card';
|
import Card from 'antd/lib/card/Card';
|
||||||
import TextArea from 'antd/lib/input/TextArea';
|
import TextArea from 'antd/lib/input/TextArea';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { isNull } from 'lodash';
|
||||||
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ReactComponent as PlusIcon } from '../../../assets/svg/x-colored.svg';
|
import { ReactComponent as PlusIcon } from '../../../assets/svg/x-colored.svg';
|
||||||
import { EntityType } from '../../../enums/entity.enum';
|
import { EntityType } from '../../../enums/entity.enum';
|
||||||
@ -40,25 +42,34 @@ export const ContractSemanticFormTab: React.FC<{
|
|||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const semanticsData = Form.useWatch('semantics', form);
|
const semanticsData = Form.useWatch('semantics', form);
|
||||||
const [editingKey, setEditingKey] = useState<number | null>(null);
|
const [editingKey, setEditingKey] = useState<number | null>(null);
|
||||||
|
const [queryBuilderAddRule, setQueryBuilderAddRule] = useState<Actions>();
|
||||||
const addFunctionRef = useRef<((defaultValue?: any) => void) | null>(null);
|
const addFunctionRef = useRef<((defaultValue?: any) => void) | null>(null);
|
||||||
|
|
||||||
|
const handleAddQueryBuilderRule = (actionFunctions: Actions) => {
|
||||||
|
setQueryBuilderAddRule(actionFunctions);
|
||||||
|
};
|
||||||
|
|
||||||
const handleAddSemantic = () => {
|
const handleAddSemantic = () => {
|
||||||
addFunctionRef.current?.({
|
addFunctionRef.current?.({
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
rule: '',
|
rule: '',
|
||||||
enabled: false,
|
enabled: true,
|
||||||
});
|
});
|
||||||
setEditingKey(semanticsData.length);
|
setEditingKey(semanticsData.length);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAddNewRule = useCallback(() => {
|
||||||
|
queryBuilderAddRule?.addRule([]);
|
||||||
|
}, [queryBuilderAddRule]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
semantics: [
|
semantics: [
|
||||||
{
|
{
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
enabled: false,
|
enabled: true,
|
||||||
rule: '',
|
rule: '',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -88,7 +99,7 @@ export const ContractSemanticFormTab: React.FC<{
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="add-semantic-button"
|
className="add-semantic-button"
|
||||||
disabled={!!editingKey || !addFunctionRef.current}
|
disabled={!isNull(editingKey) || !addFunctionRef.current}
|
||||||
icon={<Icon className="anticon" component={PlusIcon} />}
|
icon={<Icon className="anticon" component={PlusIcon} />}
|
||||||
type="link"
|
type="link"
|
||||||
onClick={handleAddSemantic}>
|
onClick={handleAddSemantic}>
|
||||||
@ -125,9 +136,13 @@ export const ContractSemanticFormTab: React.FC<{
|
|||||||
{editingKey === field.key ? null : (
|
{editingKey === field.key ? null : (
|
||||||
<>
|
<>
|
||||||
<div className="d-flex items-center gap-6">
|
<div className="d-flex items-center gap-6">
|
||||||
<Switch
|
<Form.Item
|
||||||
checked={semanticsData[field.key].enabled}
|
{...field}
|
||||||
/>
|
name={[field.name, 'enabled']}
|
||||||
|
valuePropName="checked">
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<div className="d-flex flex-column">
|
<div className="d-flex flex-column">
|
||||||
<Typography.Text>
|
<Typography.Text>
|
||||||
{semanticsData[field.key]?.name ||
|
{semanticsData[field.key]?.name ||
|
||||||
@ -151,74 +166,91 @@ export const ContractSemanticFormTab: React.FC<{
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
|
defaultExpanded={editingKey === field.key}
|
||||||
key={field.key}>
|
key={field.key}>
|
||||||
{editingKey === field.key ? (
|
{editingKey === field.key ? (
|
||||||
<Row>
|
<>
|
||||||
<Col span={24}>
|
<Row className="semantic-form-item-content">
|
||||||
<Form.Item
|
<Col span={24}>
|
||||||
{...field}
|
<Form.Item
|
||||||
label={t('label.name')}
|
{...field}
|
||||||
name={[field.name, 'name']}>
|
label={t('label.name')}
|
||||||
<Input />
|
name={[field.name, 'name']}>
|
||||||
</Form.Item>
|
<Input />
|
||||||
</Col>
|
</Form.Item>
|
||||||
<Col span={24}>
|
</Col>
|
||||||
<Form.Item
|
<Col span={24}>
|
||||||
{...field}
|
<Form.Item
|
||||||
label={t('label.description')}
|
{...field}
|
||||||
name={[field.name, 'description']}>
|
label={t('label.description')}
|
||||||
<TextArea />
|
name={[field.name, 'description']}>
|
||||||
</Form.Item>
|
<TextArea />
|
||||||
</Col>
|
</Form.Item>
|
||||||
<Col span={24}>
|
</Col>
|
||||||
<Form.Item
|
<Col span={24}>
|
||||||
{...field}
|
<Form.Item
|
||||||
label={t('label.enabled')}
|
{...field}
|
||||||
name={[field.name, 'enabled']}>
|
label={t('label.enabled')}
|
||||||
<Switch />
|
name={[field.name, 'enabled']}
|
||||||
</Form.Item>
|
valuePropName="checked">
|
||||||
</Col>
|
<Switch />
|
||||||
<Col span={24}>
|
</Form.Item>
|
||||||
<Form.Item
|
</Col>
|
||||||
{...field}
|
<Col span={24}>
|
||||||
label={t('label.add-entity', {
|
<Form.Item
|
||||||
|
{...field}
|
||||||
|
label={t('label.add-entity', {
|
||||||
|
entity: t('label.rule-plural'),
|
||||||
|
})}
|
||||||
|
name={[field.name, 'rule']}>
|
||||||
|
{/* @ts-expect-error because Form.Item will provide value and onChange */}
|
||||||
|
<QueryBuilderWidget
|
||||||
|
formContext={{
|
||||||
|
entityType: EntityType.TABLE,
|
||||||
|
}}
|
||||||
|
getQueryActions={handleAddQueryBuilderRule}
|
||||||
|
id="rule"
|
||||||
|
name={`${field.name}.rule`}
|
||||||
|
options={{
|
||||||
|
addButtonText: t('label.add-semantic'),
|
||||||
|
removeButtonText: t(
|
||||||
|
'label.remove-semantic'
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
registry={{} as FieldErrorProps['registry']}
|
||||||
|
schema={{
|
||||||
|
outputType: SearchOutputType.JSONLogic,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<div className="semantic-form-item-actions">
|
||||||
|
<Button
|
||||||
|
className="add-semantic-button"
|
||||||
|
disabled={!queryBuilderAddRule?.addRule}
|
||||||
|
icon={<Icon component={PlusIcon} />}
|
||||||
|
type="link"
|
||||||
|
onClick={handleAddNewRule}>
|
||||||
|
{t('label.add-new-entity', {
|
||||||
entity: t('label.rule'),
|
entity: t('label.rule'),
|
||||||
})}
|
})}
|
||||||
name={[field.name, 'rule']}>
|
</Button>
|
||||||
{/* @ts-expect-error because Form.Item will provide value and onChange */}
|
|
||||||
<QueryBuilderWidget
|
|
||||||
formContext={{
|
|
||||||
entityType: EntityType.TABLE,
|
|
||||||
}}
|
|
||||||
id="rule"
|
|
||||||
label={t('label.rule')}
|
|
||||||
name={`${field.name}.rule`}
|
|
||||||
options={{
|
|
||||||
addButtonText: t('label.add-semantic'),
|
|
||||||
removeButtonText: t(
|
|
||||||
'label.remove-semantic'
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
registry={{} as FieldErrorProps['registry']}
|
|
||||||
schema={{
|
|
||||||
outputType: SearchOutputType.JSONLogic,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
|
|
||||||
<Col className="d-flex justify-end" span={24}>
|
<div className="d-flex items-center">
|
||||||
<Button onClick={() => setEditingKey(null)}>
|
<Button onClick={() => setEditingKey(null)}>
|
||||||
{t('label.cancel')}
|
{t('label.cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className="m-l-md"
|
className="m-l-md"
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => setEditingKey(null)}>
|
onClick={() => setEditingKey(null)}>
|
||||||
{t('label.save')}
|
{t('label.save')}
|
||||||
</Button>
|
</Button>
|
||||||
</Col>
|
</div>
|
||||||
</Row>
|
</div>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="semantic-rule-editor-view-only">
|
<div className="semantic-rule-editor-view-only">
|
||||||
{/* @ts-expect-error because Form.Item will provide value and onChange */}
|
{/* @ts-expect-error because Form.Item will provide value and onChange */}
|
||||||
|
|||||||
@ -24,8 +24,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.contract-semantic-form-container {
|
.contract-semantic-form-container {
|
||||||
|
.expanded {
|
||||||
|
.ant-card-head {
|
||||||
|
border-bottom-left-radius: 0 !important;
|
||||||
|
border-bottom-right-radius: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.expandable-card {
|
.expandable-card {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
|
border: 1px solid @border-color-7 !important;
|
||||||
|
box-shadow: 0 1px 2px 0 @grey-27;
|
||||||
|
|
||||||
.ant-card-head {
|
.ant-card-head {
|
||||||
background: @white !important;
|
background: @white !important;
|
||||||
@ -37,6 +46,48 @@
|
|||||||
.ant-card-head {
|
.ant-card-head {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-card-body {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.semantic-form-item-content {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.semantic-form-item-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 16px 24px;
|
||||||
|
border-top: 1px solid @grey-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-builder-form-field {
|
||||||
|
.ant-card {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.ant-card-body {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.ant-divider {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn-group {
|
||||||
|
.action--DELETE {
|
||||||
|
border: 1px solid @grey-34;
|
||||||
|
|
||||||
|
.anticon {
|
||||||
|
color: @grey-400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.action--ADD-RULE {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,12 +12,13 @@
|
|||||||
*/
|
*/
|
||||||
import { Card, CardProps } from 'antd';
|
import { Card, CardProps } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { CardExpandCollapseIconButton } from '../IconButtons/EditIconButton';
|
import { CardExpandCollapseIconButton } from '../IconButtons/EditIconButton';
|
||||||
|
|
||||||
interface ExpandableCardProps {
|
interface ExpandableCardProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
defaultExpanded?: boolean;
|
||||||
onExpandStateChange?: (isExpanded: boolean) => void;
|
onExpandStateChange?: (isExpanded: boolean) => void;
|
||||||
isExpandDisabled?: boolean;
|
isExpandDisabled?: boolean;
|
||||||
cardProps: CardProps;
|
cardProps: CardProps;
|
||||||
@ -30,9 +31,10 @@ const ExpandableCard = ({
|
|||||||
onExpandStateChange,
|
onExpandStateChange,
|
||||||
isExpandDisabled,
|
isExpandDisabled,
|
||||||
dataTestId,
|
dataTestId,
|
||||||
|
defaultExpanded = true,
|
||||||
}: ExpandableCardProps) => {
|
}: ExpandableCardProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [isExpanded, setIsExpanded] = useState(true);
|
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
|
||||||
|
|
||||||
const handleExpandClick = useCallback(() => {
|
const handleExpandClick = useCallback(() => {
|
||||||
setIsExpanded((prev) => {
|
setIsExpanded((prev) => {
|
||||||
@ -42,6 +44,10 @@ const ExpandableCard = ({
|
|||||||
});
|
});
|
||||||
}, [onExpandStateChange]);
|
}, [onExpandStateChange]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsExpanded(defaultExpanded);
|
||||||
|
}, [defaultExpanded]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
bodyStyle={{
|
bodyStyle={{
|
||||||
|
|||||||
@ -23,8 +23,10 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
Actions,
|
||||||
Builder,
|
Builder,
|
||||||
Config,
|
Config,
|
||||||
ImmutableTree,
|
ImmutableTree,
|
||||||
@ -34,7 +36,7 @@ import {
|
|||||||
import 'antd/dist/antd.css';
|
import 'antd/dist/antd.css';
|
||||||
import { debounce, isEmpty, isUndefined } from 'lodash';
|
import { debounce, isEmpty, isUndefined } from 'lodash';
|
||||||
import Qs from 'qs';
|
import Qs from 'qs';
|
||||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
import { FC, useCallback, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
EntityFields,
|
EntityFields,
|
||||||
@ -84,6 +86,7 @@ const QueryBuilderWidget: FC<WidgetProps> = ({
|
|||||||
const [initDone, setInitDone] = useState<boolean>(false);
|
const [initDone, setInitDone] = useState<boolean>(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [queryURL, setQueryURL] = useState<string>('');
|
const [queryURL, setQueryURL] = useState<string>('');
|
||||||
|
const [queryActions, setQueryActions] = useState<Actions>();
|
||||||
|
|
||||||
const fetchEntityCount = useCallback(
|
const fetchEntityCount = useCallback(
|
||||||
async (queryFilter: Record<string, unknown>) => {
|
async (queryFilter: Record<string, unknown>) => {
|
||||||
@ -224,6 +227,12 @@ const QueryBuilderWidget: FC<WidgetProps> = ({
|
|||||||
}
|
}
|
||||||
}, [isSearchIndexUpdatedInContext, isUpdating]);
|
}, [isSearchIndexUpdatedInContext, isUpdating]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.getQueryActions) {
|
||||||
|
props.getQueryActions(queryActions);
|
||||||
|
}
|
||||||
|
}, [queryActions]);
|
||||||
|
|
||||||
if (!initDone) {
|
if (!initDone) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
@ -249,11 +258,18 @@ const QueryBuilderWidget: FC<WidgetProps> = ({
|
|||||||
)}
|
)}
|
||||||
<Query
|
<Query
|
||||||
{...config}
|
{...config}
|
||||||
renderBuilder={(props) => (
|
renderBuilder={(props) => {
|
||||||
<div className="query-builder-container query-builder qb-lite">
|
// Store the actions for external access
|
||||||
<Builder {...props} />
|
if (!queryActions) {
|
||||||
</div>
|
setQueryActions(props.actions);
|
||||||
)}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="query-builder-container query-builder qb-lite">
|
||||||
|
<Builder {...props} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
settings={{
|
settings={{
|
||||||
...config.settings,
|
...config.settings,
|
||||||
...(props.readonly ? READONLY_SETTINGS : {}),
|
...(props.readonly ? READONLY_SETTINGS : {}),
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} Abdeckung",
|
"entity-coverage": "{{entity}} Abdeckung",
|
||||||
"entity-detail-plural": "Details von {{entity}}",
|
"entity-detail-plural": "Details von {{entity}}",
|
||||||
"entity-distribution": "{{entity}} Verteilung",
|
"entity-distribution": "{{entity}} Verteilung",
|
||||||
|
"entity-failed": "{{entity}} fehlgeschlagen",
|
||||||
"entity-feed-plural": "Entitäts-Feeds",
|
"entity-feed-plural": "Entitäts-Feeds",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "{{entity}} Id",
|
"entity-id": "{{entity}} Id",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} Coverage",
|
"entity-coverage": "{{entity}} Coverage",
|
||||||
"entity-detail-plural": "{{entity}} Details",
|
"entity-detail-plural": "{{entity}} Details",
|
||||||
"entity-distribution": "{{entity}} Distribution",
|
"entity-distribution": "{{entity}} Distribution",
|
||||||
|
"entity-failed": "{{entity}} Failed",
|
||||||
"entity-feed-plural": "Entity feeds",
|
"entity-feed-plural": "Entity feeds",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "{{entity}} Id",
|
"entity-id": "{{entity}} Id",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} Abdeckung",
|
"entity-coverage": "{{entity}} Abdeckung",
|
||||||
"entity-detail-plural": "Detalles de {{entity}}",
|
"entity-detail-plural": "Detalles de {{entity}}",
|
||||||
"entity-distribution": "Distribución de {{entity}}",
|
"entity-distribution": "Distribución de {{entity}}",
|
||||||
|
"entity-failed": "{{entity}} Falló",
|
||||||
"entity-feed-plural": "Feeds de entidad",
|
"entity-feed-plural": "Feeds de entidad",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "{{entity}} Id",
|
"entity-id": "{{entity}} Id",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} Couverture",
|
"entity-coverage": "{{entity}} Couverture",
|
||||||
"entity-detail-plural": "Détails de {{entity}}",
|
"entity-detail-plural": "Détails de {{entity}}",
|
||||||
"entity-distribution": "Distribution de {{entity}}",
|
"entity-distribution": "Distribution de {{entity}}",
|
||||||
|
"entity-failed": "{{entity}} Échoué",
|
||||||
"entity-feed-plural": "Flux de l'Entité",
|
"entity-feed-plural": "Flux de l'Entité",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "{{entity}} Id",
|
"entity-id": "{{entity}} Id",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} Cobertura",
|
"entity-coverage": "{{entity}} Cobertura",
|
||||||
"entity-detail-plural": "Detalles de {{entity}}",
|
"entity-detail-plural": "Detalles de {{entity}}",
|
||||||
"entity-distribution": "Distribución de {{entity}}",
|
"entity-distribution": "Distribución de {{entity}}",
|
||||||
|
"entity-failed": "{{entity}} Fallado",
|
||||||
"entity-feed-plural": "Fontes de entidade",
|
"entity-feed-plural": "Fontes de entidade",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "ID de {{entity}}",
|
"entity-id": "ID de {{entity}}",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} כיסוי",
|
"entity-coverage": "{{entity}} כיסוי",
|
||||||
"entity-detail-plural": "פרטי {{entity}}",
|
"entity-detail-plural": "פרטי {{entity}}",
|
||||||
"entity-distribution": "הפצת {{entity}}",
|
"entity-distribution": "הפצת {{entity}}",
|
||||||
|
"entity-failed": "{{entity}} נכשל",
|
||||||
"entity-feed-plural": "הזנות ישויות",
|
"entity-feed-plural": "הזנות ישויות",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "{{entity}} Id",
|
"entity-id": "{{entity}} Id",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} カバレッジ",
|
"entity-coverage": "{{entity}} カバレッジ",
|
||||||
"entity-detail-plural": "{{entity}}の詳細",
|
"entity-detail-plural": "{{entity}}の詳細",
|
||||||
"entity-distribution": "{{entity}} の分布",
|
"entity-distribution": "{{entity}} の分布",
|
||||||
|
"entity-failed": "{{entity}} 失敗",
|
||||||
"entity-feed-plural": "エンティティフィード",
|
"entity-feed-plural": "エンティティフィード",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "{{entity}} ID",
|
"entity-id": "{{entity}} ID",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} 커버리지",
|
"entity-coverage": "{{entity}} 커버리지",
|
||||||
"entity-detail-plural": "{{entity}} 세부사항",
|
"entity-detail-plural": "{{entity}} 세부사항",
|
||||||
"entity-distribution": "{{entity}} 분포",
|
"entity-distribution": "{{entity}} 분포",
|
||||||
|
"entity-failed": "{{entity}} 실패",
|
||||||
"entity-feed-plural": "엔티티 피드",
|
"entity-feed-plural": "엔티티 피드",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "{{entity}} ID",
|
"entity-id": "{{entity}} ID",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} कवर",
|
"entity-coverage": "{{entity}} कवर",
|
||||||
"entity-detail-plural": "{{entity}} तपशील",
|
"entity-detail-plural": "{{entity}} तपशील",
|
||||||
"entity-distribution": "{{entity}} वितरण",
|
"entity-distribution": "{{entity}} वितरण",
|
||||||
|
"entity-failed": "{{entity}} अयशस्वी",
|
||||||
"entity-feed-plural": "घटक फीड्स",
|
"entity-feed-plural": "घटक फीड्स",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "{{entity}} आयडी",
|
"entity-id": "{{entity}} आयडी",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} Dekkingsg",
|
"entity-coverage": "{{entity}} Dekkingsg",
|
||||||
"entity-detail-plural": "{{entity}}-details",
|
"entity-detail-plural": "{{entity}}-details",
|
||||||
"entity-distribution": "{{entity}} Verdeling",
|
"entity-distribution": "{{entity}} Verdeling",
|
||||||
|
"entity-failed": "{{entity}} Mislukt",
|
||||||
"entity-feed-plural": "Entiteitsfeeds",
|
"entity-feed-plural": "Entiteitsfeeds",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "{{entity}} Id",
|
"entity-id": "{{entity}} Id",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} پوشش",
|
"entity-coverage": "{{entity}} پوشش",
|
||||||
"entity-detail-plural": "جزئیات {{entity}}",
|
"entity-detail-plural": "جزئیات {{entity}}",
|
||||||
"entity-distribution": "توزیع {{entity}}",
|
"entity-distribution": "توزیع {{entity}}",
|
||||||
|
"entity-failed": "{{entity}} ناموفق",
|
||||||
"entity-feed-plural": "فیدهای نهاد",
|
"entity-feed-plural": "فیدهای نهاد",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "شناسه {{entity}}",
|
"entity-id": "شناسه {{entity}}",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} Cobertura",
|
"entity-coverage": "{{entity}} Cobertura",
|
||||||
"entity-detail-plural": "Detalhes de {{entity}}",
|
"entity-detail-plural": "Detalhes de {{entity}}",
|
||||||
"entity-distribution": "Distribuição de {{entity}}",
|
"entity-distribution": "Distribuição de {{entity}}",
|
||||||
|
"entity-failed": "{{entity}} Falhou",
|
||||||
"entity-feed-plural": "Feeds de Entidade",
|
"entity-feed-plural": "Feeds de Entidade",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "{{entity}} Id",
|
"entity-id": "{{entity}} Id",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} Cobertura",
|
"entity-coverage": "{{entity}} Cobertura",
|
||||||
"entity-detail-plural": "Detalhes de {{entity}}",
|
"entity-detail-plural": "Detalhes de {{entity}}",
|
||||||
"entity-distribution": "Distribuição de {{entity}}",
|
"entity-distribution": "Distribuição de {{entity}}",
|
||||||
|
"entity-failed": "{{entity}} Falhou",
|
||||||
"entity-feed-plural": "Feeds de Entidade",
|
"entity-feed-plural": "Feeds de Entidade",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "{{entity}} Id",
|
"entity-id": "{{entity}} Id",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "Покрытие {{entity}}",
|
"entity-coverage": "Покрытие {{entity}}",
|
||||||
"entity-detail-plural": "Детали {{entity}}",
|
"entity-detail-plural": "Детали {{entity}}",
|
||||||
"entity-distribution": "Распределение {{entity}}",
|
"entity-distribution": "Распределение {{entity}}",
|
||||||
|
"entity-failed": "{{entity}} Неудачно",
|
||||||
"entity-feed-plural": "Фиды сущностец",
|
"entity-feed-plural": "Фиды сущностец",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "Идентификатор {{entity}}",
|
"entity-id": "Идентификатор {{entity}}",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} ความครอบคลุม",
|
"entity-coverage": "{{entity}} ความครอบคลุม",
|
||||||
"entity-detail-plural": "รายละเอียด {{entity}}",
|
"entity-detail-plural": "รายละเอียด {{entity}}",
|
||||||
"entity-distribution": "{{entity}} การแจกแจง",
|
"entity-distribution": "{{entity}} การแจกแจง",
|
||||||
|
"entity-failed": "{{entity}} ล้มเหลว",
|
||||||
"entity-feed-plural": "ฟีดเอนทิตี",
|
"entity-feed-plural": "ฟีดเอนทิตี",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "รหัส {{entity}}",
|
"entity-id": "รหัส {{entity}}",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} Kapsamı",
|
"entity-coverage": "{{entity}} Kapsamı",
|
||||||
"entity-detail-plural": "{{entity}} Detayları",
|
"entity-detail-plural": "{{entity}} Detayları",
|
||||||
"entity-distribution": "{{entity}} Dağılımı",
|
"entity-distribution": "{{entity}} Dağılımı",
|
||||||
|
"entity-failed": "{{entity}} Başarısız",
|
||||||
"entity-feed-plural": "Varlık akışları",
|
"entity-feed-plural": "Varlık akışları",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "{{entity}} Kimliği",
|
"entity-id": "{{entity}} Kimliği",
|
||||||
|
|||||||
@ -562,6 +562,7 @@
|
|||||||
"entity-coverage": "{{entity}} 覆盖",
|
"entity-coverage": "{{entity}} 覆盖",
|
||||||
"entity-detail-plural": "{{entity}}详情",
|
"entity-detail-plural": "{{entity}}详情",
|
||||||
"entity-distribution": "{{entity}} 分布",
|
"entity-distribution": "{{entity}} 分布",
|
||||||
|
"entity-failed": "{{entity}} 失败",
|
||||||
"entity-feed-plural": "实体信息流",
|
"entity-feed-plural": "实体信息流",
|
||||||
"entity-hyphen-value": "{{entity}} - {{value}}",
|
"entity-hyphen-value": "{{entity}} - {{value}}",
|
||||||
"entity-id": "{{entity}} ID",
|
"entity-id": "{{entity}} ID",
|
||||||
|
|||||||
@ -51,6 +51,7 @@ import {
|
|||||||
TabSpecificField,
|
TabSpecificField,
|
||||||
} from '../../enums/entity.enum';
|
} from '../../enums/entity.enum';
|
||||||
import { Tag } from '../../generated/entity/classification/tag';
|
import { Tag } from '../../generated/entity/classification/tag';
|
||||||
|
import { DataContract } from '../../generated/entity/data/dataContract';
|
||||||
import { Table, TableType } from '../../generated/entity/data/table';
|
import { Table, TableType } from '../../generated/entity/data/table';
|
||||||
import {
|
import {
|
||||||
Suggestion,
|
Suggestion,
|
||||||
@ -66,6 +67,7 @@ import { useCustomPages } from '../../hooks/useCustomPages';
|
|||||||
import { useFqn } from '../../hooks/useFqn';
|
import { useFqn } from '../../hooks/useFqn';
|
||||||
import { useSub } from '../../hooks/usePubSub';
|
import { useSub } from '../../hooks/usePubSub';
|
||||||
import { FeedCounts } from '../../interface/feed.interface';
|
import { FeedCounts } from '../../interface/feed.interface';
|
||||||
|
import { getContractByEntityId } from '../../rest/contractAPI';
|
||||||
import { getDataQualityLineage } from '../../rest/lineageAPI';
|
import { getDataQualityLineage } from '../../rest/lineageAPI';
|
||||||
import { getQueriesList } from '../../rest/queryAPI';
|
import { getQueriesList } from '../../rest/queryAPI';
|
||||||
import {
|
import {
|
||||||
@ -135,6 +137,7 @@ const TableDetailsPageV1: React.FC = () => {
|
|||||||
const [dqFailureCount, setDqFailureCount] = useState(0);
|
const [dqFailureCount, setDqFailureCount] = useState(0);
|
||||||
const { customizedPage, isLoading } = useCustomPages(PageType.Table);
|
const { customizedPage, isLoading } = useCustomPages(PageType.Table);
|
||||||
const [isTabExpanded, setIsTabExpanded] = useState(false);
|
const [isTabExpanded, setIsTabExpanded] = useState(false);
|
||||||
|
const [dataContract, setDataContract] = useState<DataContract>();
|
||||||
|
|
||||||
const tableFqn = useMemo(
|
const tableFqn = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -212,7 +215,6 @@ const TableDetailsPageV1: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const details = await getTableDetailsByFQN(tableFqn, { fields });
|
const details = await getTableDetailsByFQN(tableFqn, { fields });
|
||||||
|
|
||||||
setTableDetails(details);
|
setTableDetails(details);
|
||||||
addToRecentViewed({
|
addToRecentViewed({
|
||||||
displayName: getEntityName(details),
|
displayName: getEntityName(details),
|
||||||
@ -297,6 +299,15 @@ const TableDetailsPageV1: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchDataContract = async (tableId: string) => {
|
||||||
|
try {
|
||||||
|
const contract = await getContractByEntityId(tableId, EntityType.TABLE);
|
||||||
|
setDataContract(contract);
|
||||||
|
} catch {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
tableTags,
|
tableTags,
|
||||||
deleted,
|
deleted,
|
||||||
@ -780,6 +791,12 @@ const TableDetailsPageV1: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}, [tableDetails?.fullyQualifiedName]);
|
}, [tableDetails?.fullyQualifiedName]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (tableDetails) {
|
||||||
|
fetchDataContract(tableDetails.id);
|
||||||
|
}
|
||||||
|
}, [tableDetails?.id]);
|
||||||
|
|
||||||
useSub(
|
useSub(
|
||||||
'updateDetails',
|
'updateDetails',
|
||||||
(suggestion: Suggestion) => {
|
(suggestion: Suggestion) => {
|
||||||
@ -847,6 +864,7 @@ const TableDetailsPageV1: React.FC = () => {
|
|||||||
afterDomainUpdateAction={updateTableDetailsState}
|
afterDomainUpdateAction={updateTableDetailsState}
|
||||||
badge={alertBadge}
|
badge={alertBadge}
|
||||||
dataAsset={tableDetails}
|
dataAsset={tableDetails}
|
||||||
|
dataContract={dataContract}
|
||||||
entityType={EntityType.TABLE}
|
entityType={EntityType.TABLE}
|
||||||
extraDropdownContent={extraDropdownContent}
|
extraDropdownContent={extraDropdownContent}
|
||||||
openTaskCount={feedCount.openTaskCount}
|
openTaskCount={feedCount.openTaskCount}
|
||||||
|
|||||||
@ -161,6 +161,8 @@
|
|||||||
@grey-31: #f1f3fc;
|
@grey-31: #f1f3fc;
|
||||||
@grey-32: #6b7f99;
|
@grey-32: #6b7f99;
|
||||||
@grey-33: #4c526c;
|
@grey-33: #4c526c;
|
||||||
|
@grey-34: #d3d3d3;
|
||||||
|
@grey-35: #0a0d120a;
|
||||||
|
|
||||||
@text-grey-muted: @grey-4;
|
@text-grey-muted: @grey-4;
|
||||||
@de-active-color: #6b7280;
|
@de-active-color: #6b7280;
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import Icon from '@ant-design/icons';
|
||||||
import { Popover, Space, Typography } from 'antd';
|
import { Popover, Space, Typography } from 'antd';
|
||||||
import i18next, { t } from 'i18next';
|
import i18next, { t } from 'i18next';
|
||||||
import {
|
import {
|
||||||
@ -26,6 +27,7 @@ import QueryString from 'qs';
|
|||||||
import { Fragment } from 'react';
|
import { Fragment } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Node } from 'reactflow';
|
import { Node } from 'reactflow';
|
||||||
|
import { ReactComponent as CancelOutlineIcon } from '../assets/svg/ic-cancel-outline.svg';
|
||||||
import { DomainLabel } from '../components/common/DomainLabel/DomainLabel.component';
|
import { DomainLabel } from '../components/common/DomainLabel/DomainLabel.component';
|
||||||
import { OwnerLabel } from '../components/common/OwnerLabel/OwnerLabel.component';
|
import { OwnerLabel } from '../components/common/OwnerLabel/OwnerLabel.component';
|
||||||
import QueryCount from '../components/common/QueryCount/QueryCount.component';
|
import QueryCount from '../components/common/QueryCount/QueryCount.component';
|
||||||
@ -102,6 +104,7 @@ import {
|
|||||||
EventSubscription,
|
EventSubscription,
|
||||||
} from '../generated/events/eventSubscription';
|
} from '../generated/events/eventSubscription';
|
||||||
import { TestCase, TestSuite } from '../generated/tests/testCase';
|
import { TestCase, TestSuite } from '../generated/tests/testCase';
|
||||||
|
import { ContractExecutionStatus } from '../generated/type/contractExecutionStatus';
|
||||||
import { EntityReference } from '../generated/type/entityUsage';
|
import { EntityReference } from '../generated/type/entityUsage';
|
||||||
import { TagLabel } from '../generated/type/tagLabel';
|
import { TagLabel } from '../generated/type/tagLabel';
|
||||||
import { UsageDetails } from '../generated/type/usageDetails';
|
import { UsageDetails } from '../generated/type/usageDetails';
|
||||||
@ -2637,5 +2640,10 @@ export const EntityTypeName: Record<EntityType, string> = {
|
|||||||
[EntityType.SERVICE]: t('label.service'),
|
[EntityType.SERVICE]: t('label.service'),
|
||||||
[EntityType.DATA_CONTRACT]: t('label.data-contract'),
|
[EntityType.DATA_CONTRACT]: t('label.data-contract'),
|
||||||
[EntityType.SECURITY_SERVICE]: t('label.security-service'),
|
[EntityType.SECURITY_SERVICE]: t('label.security-service'),
|
||||||
[EntityType.DATA_CONTRACT]: t('label.data-contract'),
|
};
|
||||||
|
|
||||||
|
export const getDataContractStatusIcon = (status: ContractExecutionStatus) => {
|
||||||
|
return status === ContractExecutionStatus.Failed ? (
|
||||||
|
<Icon component={CancelOutlineIcon} />
|
||||||
|
) : null;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user