mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-02 04:13:17 +00:00
Fix(UI):Contract form design changes (#22712)
* fixed contract form design changes * semantic-form-design-fix * localization fix * fix the alert contract staus, redirect to tab from failed contract badge, hide the quality chart if all the values are 0, and icon fix around quality and semantic * added button to remove a semantic in the card and minor fix * tag styling new look * fixed checkbox and buttons * added yaml page and fix layout issue * fix the semantic rule component styling and enable first semantic in edit mode when coming to edit or new cntract * fix the owner not seeing while edit in modal and fix the default rule not visible * fix the edit button styling * remove the important from less and optimize tagChip newLook code * fix the file casing --------- Co-authored-by: Ashish Gupta <ashish@getcollate.io> Co-authored-by: Karan Hotchandani <33024356+karanh37@users.noreply.github.com>
This commit is contained in:
parent
5e41039b97
commit
adb0bacffb
@ -94,6 +94,7 @@
|
||||
"https-browserify": "^1.0.0",
|
||||
"i18next": "^21.10.0",
|
||||
"i18next-browser-languagedetector": "^6.1.6",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"katex": "^0.16.21",
|
||||
"lodash": "^4.17.21",
|
||||
@ -163,6 +164,7 @@
|
||||
"@types/dagre": "^0.7.47",
|
||||
"@types/diff": "^5.0.2",
|
||||
"@types/jest": "^26.0.23",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/katex": "^0.16.7",
|
||||
"@types/lodash": "^4.14.167",
|
||||
"@types/luxon": "^3.0.1",
|
||||
|
@ -0,0 +1,6 @@
|
||||
<svg viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.91406 15.3386V6.17192C2.91406 2.83858 3.7474 2.00525 7.08073 2.00525H12.9141C16.2474 2.00525 17.0807 2.83858 17.0807 6.17192V14.5052C17.0807 14.6219 17.0807 14.7386 17.0724 14.8552" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M5.28906 12.8386H17.0807V15.7553C17.0807 17.3636 15.7724 18.672 14.1641 18.672H5.83073C4.2224 18.672 2.91406 17.3636 2.91406 15.7553V15.2136C2.91406 13.9053 3.98073 12.8386 5.28906 12.8386Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.66406 6.172H13.3307" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M6.66406 9.08862H10.8307" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 903 B |
@ -0,0 +1,5 @@
|
||||
<svg viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.74219 7.83862C6.55885 8.24696 7.25885 8.86362 7.76719 9.63029C8.05885 10.0636 8.05885 10.622 7.76719 11.0553C7.25885 11.8136 6.55885 12.4303 5.74219 12.8386" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M10.8359 12.8386H14.1693" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.4974 18.6719H12.4974C16.6641 18.6719 18.3307 17.0052 18.3307 12.8386V7.83858C18.3307 3.67192 16.6641 2.00525 12.4974 2.00525H7.4974C3.33073 2.00525 1.66406 3.67192 1.66406 7.83858V12.8386C1.66406 17.0052 3.33073 18.6719 7.4974 18.6719Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 804 B |
@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.78105 2.39815C9.24259 2.13675 9.78912 2.06941 10.3003 2.21094C10.527 2.27369 10.7207 2.38301 10.9032 2.50648C11.0766 2.62375 11.2755 2.779 11.506 2.95894L11.5396 2.9852C11.7701 3.16514 11.9691 3.32039 12.1249 3.46013C12.289 3.60726 12.4421 3.7686 12.558 3.97325C12.8194 4.4348 12.8867 4.98129 12.7452 5.49249C12.6825 5.71915 12.5731 5.91284 12.4497 6.0954C12.3324 6.26879 12.1771 6.4677 11.9971 6.69822L7.26199 12.7644C7.24999 12.7798 7.23819 12.7949 7.22652 12.8099C7.00425 13.0949 6.83619 13.3104 6.62812 13.4874C6.45203 13.6372 6.25719 13.7634 6.04853 13.863C5.80202 13.9806 5.53667 14.0459 5.18568 14.1325C5.16728 14.137 5.14864 14.1416 5.12976 14.1463L3.71321 14.496C3.61511 14.5202 3.50313 14.548 3.40435 14.5631C3.29423 14.58 3.12371 14.5954 2.93538 14.5351C2.70898 14.4626 2.51581 14.3118 2.39051 14.1098C2.28629 13.9417 2.25977 13.7726 2.24945 13.6616C2.2402 13.5621 2.23987 13.4468 2.23959 13.3457C2.23957 13.3394 2.23955 13.3332 2.23953 13.3271L2.23495 11.8866C2.23489 11.8672 2.23482 11.848 2.23475 11.829C2.23345 11.4676 2.23247 11.1942 2.28672 10.9266C2.33264 10.7 2.40783 10.4803 2.51039 10.2732C2.63157 10.0284 2.79981 9.81302 3.02236 9.52815C3.03403 9.51322 3.04585 9.49808 3.05782 9.48275L7.79299 3.41652C7.97292 3.18599 8.12819 2.98708 8.26792 2.83122C8.41505 2.66712 8.57639 2.51405 8.78105 2.39815ZM9.94452 3.49593C9.77412 3.44876 9.59199 3.4712 9.43812 3.55834C9.41199 3.57314 9.36239 3.60785 9.26072 3.72128C9.15499 3.83919 9.02765 4.00169 8.83092 4.25374L10.933 5.89458C11.1298 5.64254 11.2565 5.47959 11.3452 5.34842C11.4306 5.22222 11.4522 5.16567 11.4602 5.13673C11.5074 4.96633 11.4849 4.78417 11.3978 4.63032C11.383 4.60419 11.3483 4.55458 11.2349 4.45288C11.1169 4.34717 10.9545 4.21987 10.7024 4.02312C10.4503 3.82638 10.2874 3.69964 10.1563 3.61093C10.0301 3.52558 9.97345 3.50394 9.94452 3.49593ZM10.1126 6.94562L8.01052 5.30478L4.10886 10.3032C3.83569 10.6531 3.75865 10.757 3.70533 10.8647C3.65405 10.9683 3.61645 11.0781 3.59349 11.1914C3.56962 11.3092 3.56687 11.4384 3.56828 11.8824L3.57233 13.1574L4.81019 12.8518C5.24119 12.7454 5.36591 12.7114 5.47439 12.6596C5.57873 12.6098 5.67614 12.5467 5.76419 12.4718C5.85574 12.3939 5.93777 12.294 6.21093 11.944L10.1126 6.94562ZM7.99845 14.0003C7.99845 13.6321 8.29692 13.3336 8.66512 13.3336H13.3318C13.7 13.3336 13.9985 13.6321 13.9985 14.0003C13.9985 14.3685 13.7 14.667 13.3318 14.667H8.66512C8.29692 14.667 7.99845 14.3685 7.99845 14.0003Z" fill="currentColor"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 20 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.5 15.8661L7.5 10.8661L12.5 5.86609" stroke="currentColor" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 223 B |
@ -11,16 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import Icon from '@ant-design/icons';
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Divider,
|
||||
Row,
|
||||
Space,
|
||||
Tag,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { Button, Col, Divider, Row, Space, Tooltip, Typography } from 'antd';
|
||||
import ButtonGroup from 'antd/lib/button/button-group';
|
||||
import { AxiosError } from 'axios';
|
||||
import classNames from 'classnames';
|
||||
@ -436,16 +427,25 @@ export const DataAssetsHeader = ({
|
||||
const dataContractLatestResultButton = useMemo(() => {
|
||||
if (dataContract?.latestResult?.status === ContractExecutionStatus.Failed) {
|
||||
return (
|
||||
<Tag
|
||||
<Button
|
||||
className={classNames(
|
||||
`data-contract-latest-result-button
|
||||
${dataContract?.latestResult?.status}`
|
||||
)}>
|
||||
{getDataContractStatusIcon(dataContract?.latestResult?.status)}
|
||||
)}
|
||||
icon={getDataContractStatusIcon(dataContract?.latestResult?.status)}
|
||||
onClick={() => {
|
||||
navigate(
|
||||
getEntityDetailsPath(
|
||||
entityType,
|
||||
dataAsset?.fullyQualifiedName ?? '',
|
||||
EntityTabs.CONTRACT
|
||||
)
|
||||
);
|
||||
}}>
|
||||
{t('label.entity-failed', {
|
||||
entity: t('label.contract'),
|
||||
})}
|
||||
</Tag>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -129,24 +129,29 @@
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
.ant-btn-group.spaced {
|
||||
.ant-btn.data-contract-latest-result-button {
|
||||
font-size: 14px;
|
||||
padding: 6px 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
border-radius: 12px !important;
|
||||
font-weight: 600;
|
||||
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;
|
||||
&.Failed {
|
||||
color: @red-14;
|
||||
border: 1px solid @red-19;
|
||||
background-color: @red-9;
|
||||
|
||||
svg {
|
||||
font-size: 26px;
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border: 1px solid @red-19;
|
||||
background-color: @red-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,16 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CodeOutlined, EditOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Divider,
|
||||
Radio,
|
||||
RadioChangeEvent,
|
||||
Tabs,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { Button, Card, RadioChangeEvent, Tabs, Typography } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
@ -112,7 +103,7 @@ const AddDataContract: React.FC<{
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}, [contract, formValues]);
|
||||
}, [contract, formValues, table.id]);
|
||||
|
||||
const onFormChange = useCallback(
|
||||
(data: Partial<DataContract>) => {
|
||||
@ -220,27 +211,13 @@ const AddDataContract: React.FC<{
|
||||
const cardTitle = useMemo(() => {
|
||||
return (
|
||||
<div className="add-contract-card-header d-flex items-center justify-between">
|
||||
<div className="d-flex item-center justify-between flex-1">
|
||||
<div>
|
||||
<Typography.Text className="add-contract-card-title">
|
||||
{t('label.add-contract-detail-plural')}
|
||||
</Typography.Text>
|
||||
<Typography.Paragraph className="add-contract-card-description">
|
||||
{t('message.add-contract-detail-description')}
|
||||
</Typography.Paragraph>
|
||||
</div>
|
||||
<div className="d-flex items-center">
|
||||
<Radio.Group
|
||||
optionType="button"
|
||||
options={[
|
||||
{ label: <CodeOutlined />, value: DataContractMode.YAML },
|
||||
{ label: <EditOutlined />, value: DataContractMode.UI },
|
||||
]}
|
||||
value={mode}
|
||||
onChange={handleModeChange}
|
||||
/>
|
||||
<Divider type="vertical" />
|
||||
</div>
|
||||
<div>
|
||||
<Typography.Text className="add-contract-card-title">
|
||||
{t('label.add-contract-detail-plural')}
|
||||
</Typography.Text>
|
||||
<Typography.Paragraph className="add-contract-card-description">
|
||||
{t('message.add-contract-detail-description')}
|
||||
</Typography.Paragraph>
|
||||
</div>
|
||||
<div>
|
||||
<Button type="default" onClick={onCancel}>
|
||||
|
@ -28,11 +28,54 @@
|
||||
overflow: initial;
|
||||
}
|
||||
}
|
||||
.contract-prev-button,
|
||||
.contract-next-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: @size-xs @size-sm;
|
||||
border: 1px solid @grey-300;
|
||||
border-radius: 8px;
|
||||
gap: 4px;
|
||||
font-weight: @font-semibold;
|
||||
|
||||
box-shadow: 0px 1px 2px 0px @grey-27;
|
||||
|
||||
box-shadow: 0px -2px 0px 0px @grey-27 inset;
|
||||
|
||||
box-shadow: 0px 0px 0px 1px @grey-27 inset;
|
||||
}
|
||||
.contract-prev-button {
|
||||
color: @grey-700;
|
||||
svg {
|
||||
stroke: @grey-400;
|
||||
}
|
||||
}
|
||||
.ant-tabs-left-content {
|
||||
height: 100%;
|
||||
}
|
||||
.ant-checkbox-checked {
|
||||
.ant-checkbox-inner {
|
||||
background-color: @primary-1;
|
||||
border: 1px solid @primary-color;
|
||||
&::after {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-checkbox-indeterminate {
|
||||
.ant-checkbox-inner {
|
||||
background-color: @primary-1;
|
||||
border: 1px solid @primary-color;
|
||||
&::after {
|
||||
width: 7px;
|
||||
height: 2px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-tabs-nav {
|
||||
width: 180px;
|
||||
height: initial;
|
||||
@ -104,6 +147,10 @@
|
||||
min-height: 500px;
|
||||
border-radius: 0 8px 8px 8px;
|
||||
}
|
||||
.schema-table-name {
|
||||
color: @grey-600;
|
||||
font-weight: @font-medium;
|
||||
}
|
||||
|
||||
// Contract detail Forms
|
||||
.contract-detail-form-tab-title {
|
||||
@ -147,23 +194,6 @@
|
||||
& > div:first-child {
|
||||
background: transparent;
|
||||
padding: 0 0 20px 0;
|
||||
|
||||
.ant-typography {
|
||||
margin: 0;
|
||||
|
||||
&.ant-typography-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
margin-bottom: 4px !important;
|
||||
}
|
||||
|
||||
&.ant-typography-paragraph {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
margin: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Form styling within tabs
|
||||
@ -173,28 +203,44 @@
|
||||
|
||||
.ant-form-item-label {
|
||||
padding-bottom: 8px;
|
||||
|
||||
label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #374151;
|
||||
color: @grey-700;
|
||||
|
||||
&.ant-form-item-required::before {
|
||||
color: #ef4444;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-form-item-control-input-content {
|
||||
input[type='text'] {
|
||||
padding: 8px 14px;
|
||||
color: @grey-700;
|
||||
border: 1px solid @grey-300;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 2px 0px @grey-27;
|
||||
}
|
||||
}
|
||||
.ant-form-item-control {
|
||||
.ant-input,
|
||||
.ant-select-selector,
|
||||
.ant-mentions {
|
||||
border-radius: 6px;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 8px;
|
||||
border: 1px solid @grey-300;
|
||||
padding: 4px 12px;
|
||||
color: @grey-700;
|
||||
height: 40px;
|
||||
box-shadow: 0 1px 2px 0px @grey-27;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
border-color: #3b82f6;
|
||||
border-color: @primary-color;
|
||||
}
|
||||
|
||||
.ant-select-selection-search {
|
||||
input[type='search'] {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -227,3 +273,26 @@
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.semantic-rule-editor-view-only {
|
||||
input[type='text'] {
|
||||
padding: 8px 14px;
|
||||
color: @grey-700;
|
||||
box-shadow: 0px 1px 2px 0px @grey-27;
|
||||
border: 1px solid @grey-300;
|
||||
}
|
||||
|
||||
.ant-select-selector,
|
||||
.ant-mentions {
|
||||
border: 1px solid @grey-300;
|
||||
padding: 4px 12px !important;
|
||||
color: @grey-700;
|
||||
height: 40px !important;
|
||||
box-shadow: 0 1px 2px 0px @grey-27;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ArrowRightOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { PlusOutlined, RightOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Form, Typography } from 'antd';
|
||||
import { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -61,6 +61,7 @@ export const ContractDetailFormTab: React.FC<{
|
||||
type: FieldTypes.USER_TEAM_SELECT,
|
||||
required: false,
|
||||
props: {
|
||||
owner: initialValues?.owners,
|
||||
hasPermission: true,
|
||||
children: (
|
||||
<Button
|
||||
@ -115,9 +116,13 @@ export const ContractDetailFormTab: React.FC<{
|
||||
</div>
|
||||
</Card>
|
||||
<div className="d-flex justify-end m-t-md">
|
||||
<Button htmlType="submit" type="primary" onClick={onNext}>
|
||||
<Button
|
||||
className="contract-next-button"
|
||||
htmlType="submit"
|
||||
type="primary"
|
||||
onClick={onNext}>
|
||||
{nextLabel ?? t('label.next')}
|
||||
<ArrowRightOutlined />
|
||||
<RightOutlined height={15} width={8} />
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
|
@ -12,30 +12,44 @@
|
||||
*/
|
||||
import Icon, { PlayCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { Loading } from '@melloware/react-logviewer';
|
||||
import { Button, Card, Col, Row, Space, Tag, Typography } from 'antd';
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Divider,
|
||||
RadioChangeEvent,
|
||||
Row,
|
||||
Space,
|
||||
Tag,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg';
|
||||
import { Cell, Pie, PieChart } from 'recharts';
|
||||
import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new-thick.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 CheckIcon } from '../../../assets/svg/ic-check-circle.svg';
|
||||
import { ReactComponent as FailIcon } from '../../../assets/svg/ic-fail.svg';
|
||||
import { ReactComponent as CheckIcon } from '../../../assets/svg/ic-successful.svg';
|
||||
import { ReactComponent as DefaultIcon } from '../../../assets/svg/ic-task.svg';
|
||||
import { ReactComponent as DeleteIcon } from '../../../assets/svg/ic-trash.svg';
|
||||
|
||||
import { isEmpty } from 'lodash';
|
||||
import { Cell, Pie, PieChart } from 'recharts';
|
||||
import {
|
||||
ICON_DIMENSION,
|
||||
ICON_DIMENSION_USER_PAGE,
|
||||
NO_DATA_PLACEHOLDER,
|
||||
} from '../../../constants/constants';
|
||||
import { DataContractMode } from '../../../constants/DataContract.constants';
|
||||
import { TEST_CASE_STATUS_ICON } from '../../../constants/DataQuality.constants';
|
||||
import { DEFAULT_SORT_ORDER } from '../../../constants/profiler.constant';
|
||||
import { ERROR_PLACEHOLDER_TYPE } from '../../../enums/common.enum';
|
||||
import { EntityType } from '../../../enums/entity.enum';
|
||||
import { DataContract } from '../../../generated/entity/data/dataContract';
|
||||
import { DataContractResult } from '../../../generated/entity/datacontract/dataContractResult';
|
||||
import {
|
||||
ContractExecutionStatus,
|
||||
DataContractResult,
|
||||
} from '../../../generated/entity/datacontract/dataContractResult';
|
||||
import { TestCase, TestSummary } from '../../../generated/tests/testCase';
|
||||
import {
|
||||
getContractResultByResultId,
|
||||
@ -46,6 +60,7 @@ import {
|
||||
getTestCaseExecutionSummary,
|
||||
} from '../../../rest/testAPI';
|
||||
import {
|
||||
downloadContractYamlFile,
|
||||
getConstraintStatus,
|
||||
getContractStatusType,
|
||||
getTestCaseSummaryChartItems,
|
||||
@ -63,6 +78,8 @@ import RichTextEditorPreviewerNew from '../../common/RichTextEditor/RichTextEdit
|
||||
import { StatusType } from '../../common/StatusBadge/StatusBadge.interface';
|
||||
import StatusBadgeV2 from '../../common/StatusBadge/StatusBadgeV2.component';
|
||||
import Table from '../../common/Table/Table';
|
||||
import ContractViewSwitchTab from '../ContractViewSwitchTab/ContractViewSwitchTab.component';
|
||||
import ContractYaml from '../ContractYaml/ContractYaml.component';
|
||||
import './contract-detail.less';
|
||||
|
||||
const ContractDetail: React.FC<{
|
||||
@ -75,9 +92,10 @@ const ContractDetail: React.FC<{
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isTestCaseLoading, setIsTestCaseLoading] = useState(false);
|
||||
const [latestContractResults, setLatestContractResults] =
|
||||
useState<DataContractResult | null>(null);
|
||||
useState<DataContractResult>();
|
||||
const [testCaseSummary, setTestCaseSummary] = useState<TestSummary>();
|
||||
const [testCaseResult, setTestCaseResult] = useState<TestCase[]>([]);
|
||||
const [mode, setMode] = useState<DataContractMode>(DataContractMode.UI);
|
||||
|
||||
const fetchLatestContractResults = async () => {
|
||||
try {
|
||||
@ -176,9 +194,29 @@ const ContractDetail: React.FC<{
|
||||
return getConstraintStatus(latestContractResults);
|
||||
}, [latestContractResults]);
|
||||
|
||||
const testCaseSummaryChartItems = useMemo(() => {
|
||||
return getTestCaseSummaryChartItems(testCaseSummary);
|
||||
}, [testCaseSummary]);
|
||||
const { showTestCaseSummaryChart, testCaseSummaryChartItems } =
|
||||
useMemo(() => {
|
||||
return {
|
||||
showTestCaseSummaryChart: Boolean(
|
||||
testCaseSummary?.total ??
|
||||
testCaseSummary?.success ??
|
||||
testCaseSummary?.failed ??
|
||||
testCaseSummary?.aborted
|
||||
),
|
||||
testCaseSummaryChartItems:
|
||||
getTestCaseSummaryChartItems(testCaseSummary),
|
||||
};
|
||||
}, [testCaseSummary]);
|
||||
|
||||
const showContractStatusAlert = useMemo(() => {
|
||||
const { result, contractExecutionStatus } = latestContractResults ?? {};
|
||||
|
||||
return (
|
||||
result &&
|
||||
(contractExecutionStatus === ContractExecutionStatus.Failed ||
|
||||
contractExecutionStatus === ContractExecutionStatus.Aborted)
|
||||
);
|
||||
}, [latestContractResults]);
|
||||
|
||||
const getSemanticIconPerLastExecution = (semanticName: string) => {
|
||||
if (!latestContractResults) {
|
||||
@ -208,6 +246,14 @@ const ContractDetail: React.FC<{
|
||||
/>
|
||||
);
|
||||
|
||||
const handleExportContract = useCallback(() => {
|
||||
if (!contract) {
|
||||
return;
|
||||
}
|
||||
|
||||
downloadContractYamlFile(contract);
|
||||
}, [contract]);
|
||||
|
||||
const handleRunNow = () => {
|
||||
if (contract?.id) {
|
||||
setValidateLoading(true);
|
||||
@ -221,6 +267,116 @@ const ContractDetail: React.FC<{
|
||||
}
|
||||
};
|
||||
|
||||
const handleModeChange = useCallback((e: RadioChangeEvent) => {
|
||||
setMode(e.target.value);
|
||||
}, []);
|
||||
|
||||
const renderDataContractHeader = useMemo(() => {
|
||||
if (!contract) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Row align="middle" justify="space-between">
|
||||
<Col flex="auto">
|
||||
<Typography.Text className="contract-title">
|
||||
{getEntityName(contract)}
|
||||
</Typography.Text>
|
||||
|
||||
<Typography.Text className="contract-time">
|
||||
{t('message.modified-time-ago-by', {
|
||||
time: getRelativeTime(contract.updatedAt),
|
||||
by: contract.updatedBy,
|
||||
})}
|
||||
</Typography.Text>
|
||||
|
||||
<div className="contract-status-badge-container">
|
||||
<StatusBadgeV2
|
||||
externalIcon={FlagIcon}
|
||||
label={contract.status ?? t('label.active')}
|
||||
status={StatusType.Success}
|
||||
/>
|
||||
|
||||
<StatusBadgeV2
|
||||
className="contract-version-badge"
|
||||
label={t('label.version-number', {
|
||||
version: contract.version,
|
||||
})}
|
||||
status={StatusType.Version}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<div className="contract-action-container">
|
||||
{!isEmpty(contract.owners) && (
|
||||
<div className="contract-owner-label-container">
|
||||
<Typography.Text>{t('label.owner-plural')}</Typography.Text>
|
||||
<OwnerLabel
|
||||
avatarSize={24}
|
||||
isCompactView={false}
|
||||
maxVisibleOwners={5}
|
||||
owners={contract.owners}
|
||||
showLabel={false}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ContractViewSwitchTab
|
||||
handleModeChange={handleModeChange}
|
||||
mode={mode}
|
||||
/>
|
||||
|
||||
<Divider className="contract-divider" type="vertical" />
|
||||
|
||||
<Button
|
||||
className="contract-run-now-button"
|
||||
icon={<PlayCircleOutlined />}
|
||||
loading={validateLoading}
|
||||
size="middle"
|
||||
onClick={handleRunNow}>
|
||||
{t('label.run-now')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
className="contract-export-button"
|
||||
data-testid="export-contract-button"
|
||||
onClick={handleExportContract}>
|
||||
{t('label.export')}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
danger
|
||||
className="delete-button"
|
||||
icon={<DeleteIcon />}
|
||||
size="small"
|
||||
onClick={onDelete}
|
||||
/>
|
||||
<Button
|
||||
className="contract-edit-button"
|
||||
icon={
|
||||
<EditIcon
|
||||
className="anticon"
|
||||
style={{ ...ICON_DIMENSION_USER_PAGE }}
|
||||
/>
|
||||
}
|
||||
type="primary"
|
||||
onClick={onEdit}>
|
||||
{t('label.edit')}
|
||||
</Button>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}, [
|
||||
contract,
|
||||
mode,
|
||||
onDelete,
|
||||
onEdit,
|
||||
handleRunNow,
|
||||
handleModeChange,
|
||||
validateLoading,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (contract?.id && contract?.latestResult?.resultId) {
|
||||
fetchLatestContractResults();
|
||||
@ -255,147 +411,17 @@ const ContractDetail: React.FC<{
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Header Section */}
|
||||
<Card className="contract-header-container" style={{ marginBottom: 16 }}>
|
||||
<Row align="middle" justify="space-between">
|
||||
<Col flex="auto">
|
||||
<Typography.Text className="contract-title">
|
||||
{getEntityName(contract)}
|
||||
</Typography.Text>
|
||||
|
||||
<Typography.Text className="contract-time">
|
||||
{t('message.modified-time-ago-by', {
|
||||
time: getRelativeTime(contract.updatedAt),
|
||||
by: contract.updatedBy,
|
||||
})}
|
||||
</Typography.Text>
|
||||
|
||||
<div className="d-flex items-center gap-2 m-t-xs">
|
||||
<StatusBadgeV2
|
||||
externalIcon={FlagIcon}
|
||||
label={contract.status ?? t('label.active')}
|
||||
status={StatusType.Success}
|
||||
/>
|
||||
|
||||
<StatusBadgeV2
|
||||
className="contract-version-badge"
|
||||
label={t('label.version-number', {
|
||||
version: contract.version,
|
||||
})}
|
||||
status={StatusType.Version}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col>
|
||||
<div className="contract-action-container">
|
||||
{!isEmpty(contract.owners) && (
|
||||
<div className="contract-owner-label-container">
|
||||
<Typography.Text>{t('label.owner-plural')}</Typography.Text>
|
||||
<OwnerLabel
|
||||
avatarSize={24}
|
||||
isCompactView={false}
|
||||
maxVisibleOwners={5}
|
||||
owners={contract.owners}
|
||||
showLabel={false}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Button
|
||||
className="contract-run-now-button"
|
||||
icon={<PlayCircleOutlined />}
|
||||
loading={validateLoading}
|
||||
size="middle"
|
||||
onClick={handleRunNow}>
|
||||
{t('label.run-now')}
|
||||
</Button>
|
||||
<Button
|
||||
danger
|
||||
className="delete-button"
|
||||
icon={<DeleteIcon />}
|
||||
size="small"
|
||||
onClick={onDelete}
|
||||
/>
|
||||
<Button
|
||||
icon={
|
||||
<EditIcon className="anticon" style={{ ...ICON_DIMENSION }} />
|
||||
}
|
||||
size="small"
|
||||
type="primary"
|
||||
onClick={onEdit}>
|
||||
{t('label.edit')}
|
||||
</Button>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
<Row className="contract-detail-container" gutter={[16, 0]}>
|
||||
{/* Left Column */}
|
||||
<Col span={12}>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<ExpandableCard
|
||||
cardProps={{
|
||||
className: 'expandable-card-contract',
|
||||
title: (
|
||||
<div className="contract-card-title-container">
|
||||
<Typography.Text className="contract-card-title">
|
||||
{t('label.entity-detail-plural', {
|
||||
entity: t('label.contract'),
|
||||
})}
|
||||
</Typography.Text>
|
||||
<Typography.Text className="contract-card-description">
|
||||
{t('message.expected-schema-structure-of-this-asset')}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
),
|
||||
}}>
|
||||
<div className="expandable-card-contract-body">
|
||||
<DescriptionV1
|
||||
description={contract.description}
|
||||
entityType={EntityType.DATA_CONTRACT}
|
||||
showCommentsIcon={false}
|
||||
showSuggestions={false}
|
||||
/>
|
||||
</div>
|
||||
</ExpandableCard>
|
||||
</Col>
|
||||
|
||||
<Col span={24}>
|
||||
<ExpandableCard
|
||||
cardProps={{
|
||||
className: 'expandable-card-contract',
|
||||
title: (
|
||||
<div className="contract-card-title-container">
|
||||
<Typography.Text className="contract-card-title">
|
||||
{t('label.schema')}
|
||||
</Typography.Text>
|
||||
<Typography.Text className="contract-card-description">
|
||||
{t('message.expected-schema-structure-of-this-asset')}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
),
|
||||
}}>
|
||||
<Table
|
||||
columns={schemaColumns}
|
||||
dataSource={schemaDetail}
|
||||
pagination={false}
|
||||
rowKey="name"
|
||||
size="small"
|
||||
/>
|
||||
</ExpandableCard>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
|
||||
{/* Right Column */}
|
||||
<Col span={12}>
|
||||
{/* Contract Status Card */}
|
||||
|
||||
<Row gutter={[16, 16]}>
|
||||
{contract?.latestResult?.resultId && (
|
||||
<Card
|
||||
className="contract-header-container"
|
||||
style={{ marginBottom: 16 }}
|
||||
title={renderDataContractHeader}>
|
||||
{mode === DataContractMode.YAML ? (
|
||||
<ContractYaml contract={contract} />
|
||||
) : (
|
||||
<Row className="contract-detail-container" gutter={[16, 0]}>
|
||||
{/* Left Column */}
|
||||
<Col span={12}>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={24}>
|
||||
<ExpandableCard
|
||||
cardProps={{
|
||||
@ -403,109 +429,27 @@ const ContractDetail: React.FC<{
|
||||
title: (
|
||||
<div className="contract-card-title-container">
|
||||
<Typography.Text className="contract-card-title">
|
||||
{t('label.contract-status')}
|
||||
{t('label.entity-detail-plural', {
|
||||
entity: t('label.contract'),
|
||||
})}
|
||||
</Typography.Text>
|
||||
<Typography.Text className="contract-card-description">
|
||||
{t('message.contract-status-description')}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
),
|
||||
}}>
|
||||
{isLoading ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<>
|
||||
{latestContractResults?.result && (
|
||||
<AlertBar
|
||||
defafultExpand
|
||||
className="h-full m-b-md"
|
||||
message={latestContractResults.result}
|
||||
type="error"
|
||||
/>
|
||||
)}
|
||||
|
||||
{constraintStatus.map((item) => (
|
||||
<div
|
||||
className="contract-status-card-item d-flex justify-between items-center"
|
||||
key={item.label}>
|
||||
<div className="d-flex items-center">
|
||||
<Icon
|
||||
className="contract-status-card-icon"
|
||||
component={item.icon}
|
||||
data-testid={`${item.label}-icon`}
|
||||
/>
|
||||
|
||||
<div className="d-flex flex-column m-l-md">
|
||||
<Typography.Text className="contract-status-card-label">
|
||||
{item.label}
|
||||
</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>
|
||||
|
||||
<StatusBadgeV2
|
||||
label={item.status}
|
||||
status={getContractStatusType(item.status)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</ExpandableCard>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
{/* Semantics Card */}
|
||||
{contract?.semantics && contract?.semantics.length > 0 && (
|
||||
<Col span={24}>
|
||||
<ExpandableCard
|
||||
cardProps={{
|
||||
className: 'expandable-card-contract',
|
||||
title: (
|
||||
<div className="contract-card-title-container">
|
||||
<Typography.Text className="contract-card-title">
|
||||
{t('label.semantic-plural')}
|
||||
</Typography.Text>
|
||||
<Typography.Text className="contract-card-description">
|
||||
{t('message.semantics-description')}
|
||||
{t('message.expected-schema-structure-of-this-asset')}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
),
|
||||
}}>
|
||||
<div className="expandable-card-contract-body">
|
||||
<Typography.Text className="card-subtitle">
|
||||
{t('label.custom-integrity-rules')}
|
||||
</Typography.Text>
|
||||
<div className="rule-item-container">
|
||||
{(contract?.semantics ?? []).map((item) => (
|
||||
<div className="rule-item">
|
||||
<Icon
|
||||
className="rule-icon"
|
||||
component={getSemanticIconPerLastExecution(
|
||||
item.name
|
||||
)}
|
||||
/>
|
||||
<span className="rule-name">{item.name}</span>{' '}
|
||||
<span className="rule-description">
|
||||
{item.description}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<DescriptionV1
|
||||
description={contract.description}
|
||||
entityType={EntityType.DATA_CONTRACT}
|
||||
showCommentsIcon={false}
|
||||
showSuggestions={false}
|
||||
/>
|
||||
</div>
|
||||
</ExpandableCard>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
{/* Quality Card */}
|
||||
{contract?.testSuite?.id && (
|
||||
<Col span={24}>
|
||||
<ExpandableCard
|
||||
cardProps={{
|
||||
@ -513,86 +457,235 @@ const ContractDetail: React.FC<{
|
||||
title: (
|
||||
<div className="contract-card-title-container">
|
||||
<Typography.Text className="contract-card-title">
|
||||
{t('label.quality')}
|
||||
{t('label.schema')}
|
||||
</Typography.Text>
|
||||
<Typography.Text className="contract-card-description">
|
||||
{t('message.data-quality-test-contract-title')}
|
||||
{t('message.expected-schema-structure-of-this-asset')}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
),
|
||||
}}>
|
||||
<div className="expandable-card-contract-body">
|
||||
{isTestCaseLoading ? (
|
||||
<Table
|
||||
columns={schemaColumns}
|
||||
dataSource={schemaDetail}
|
||||
pagination={false}
|
||||
rowKey="name"
|
||||
size="small"
|
||||
/>
|
||||
</ExpandableCard>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
|
||||
{/* Right Column */}
|
||||
<Col span={12}>
|
||||
{/* Contract Status Card */}
|
||||
|
||||
<Row gutter={[16, 16]}>
|
||||
{contract?.latestResult?.resultId && (
|
||||
<Col span={24}>
|
||||
<ExpandableCard
|
||||
cardProps={{
|
||||
className: 'expandable-card-contract',
|
||||
title: (
|
||||
<div className="contract-card-title-container">
|
||||
<Typography.Text className="contract-card-title">
|
||||
{t('label.contract-status')}
|
||||
</Typography.Text>
|
||||
<Typography.Text className="contract-card-description">
|
||||
{t('message.contract-status-description')}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
),
|
||||
}}>
|
||||
{isLoading ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<div className="data-quality-card-container">
|
||||
<div className="data-quality-chart-container">
|
||||
{testCaseSummaryChartItems.map((item) => (
|
||||
<div
|
||||
className="data-quality-chart-item"
|
||||
key={item.label}>
|
||||
<Typography.Text className="chart-label">
|
||||
{item.label}
|
||||
</Typography.Text>
|
||||
<>
|
||||
{showContractStatusAlert && (
|
||||
<AlertBar
|
||||
defafultExpand
|
||||
className="h-full m-b-md"
|
||||
message={latestContractResults?.result ?? ''}
|
||||
type="error"
|
||||
/>
|
||||
)}
|
||||
|
||||
<PieChart height={120} width={120}>
|
||||
<Pie
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
data={item.chartData}
|
||||
dataKey="value"
|
||||
innerRadius={40}
|
||||
outerRadius={50}>
|
||||
{item.chartData.map((entry, index) => (
|
||||
<Cell
|
||||
fill={entry.color}
|
||||
key={`cell-${index}`}
|
||||
/>
|
||||
))}
|
||||
</Pie>
|
||||
<text
|
||||
className="chart-center-text"
|
||||
dominantBaseline="middle"
|
||||
textAnchor="middle"
|
||||
x="50%"
|
||||
y="50%">
|
||||
{item.value}
|
||||
</text>
|
||||
</PieChart>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<Space direction="vertical">
|
||||
{testCaseResult.map((item) => {
|
||||
return (
|
||||
<div
|
||||
className="data-quality-item d-flex items-center"
|
||||
key={item.id}>
|
||||
{getTestCaseStatusIcon(item)}
|
||||
<div className="data-quality-item-content">
|
||||
<Typography.Text className="data-quality-item-name">
|
||||
{item.name}
|
||||
{constraintStatus.map((item) => (
|
||||
<div
|
||||
className="contract-status-card-item d-flex justify-between items-center"
|
||||
key={item.label}>
|
||||
<div className="d-flex items-center">
|
||||
<Icon
|
||||
className="contract-status-card-icon"
|
||||
component={item.icon}
|
||||
data-testid={`${item.label}-icon`}
|
||||
/>
|
||||
|
||||
<div className="d-flex flex-column m-l-md">
|
||||
<Typography.Text className="contract-status-card-label">
|
||||
{item.label}
|
||||
</Typography.Text>
|
||||
<div>
|
||||
<Typography.Text className="contract-status-card-desc">
|
||||
{item.desc}
|
||||
</Typography.Text>
|
||||
<Typography.Text className="data-quality-item-description">
|
||||
<RichTextEditorPreviewerNew
|
||||
markdown={item.description ?? ''}
|
||||
/>
|
||||
<Typography.Text className="contract-status-card-time">
|
||||
{item.time}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<StatusBadgeV2
|
||||
label={item.status}
|
||||
status={getContractStatusType(item.status)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</ExpandableCard>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
</ExpandableCard>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
{/* Semantics Card */}
|
||||
{contract?.semantics && contract?.semantics.length > 0 && (
|
||||
<Col span={24}>
|
||||
<ExpandableCard
|
||||
cardProps={{
|
||||
className: 'expandable-card-contract',
|
||||
title: (
|
||||
<div className="contract-card-title-container">
|
||||
<Typography.Text className="contract-card-title">
|
||||
{t('label.semantic-plural')}
|
||||
</Typography.Text>
|
||||
<Typography.Text className="contract-card-description">
|
||||
{t('message.semantics-description')}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
),
|
||||
}}>
|
||||
<div className="expandable-card-contract-body">
|
||||
<Typography.Text className="card-subtitle">
|
||||
{t('label.custom-integrity-rules')}
|
||||
</Typography.Text>
|
||||
<div className="rule-item-container">
|
||||
{(contract?.semantics ?? []).map((item) => (
|
||||
<div className="rule-item">
|
||||
<Icon
|
||||
className={classNames('rule-icon', {
|
||||
'rule-icon-default': !latestContractResults,
|
||||
})}
|
||||
component={getSemanticIconPerLastExecution(
|
||||
item.name
|
||||
)}
|
||||
/>
|
||||
<span className="rule-name">{item.name}</span>{' '}
|
||||
<span className="rule-description">
|
||||
{item.description}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</ExpandableCard>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
{/* Quality Card */}
|
||||
{contract?.testSuite?.id && (
|
||||
<Col span={24}>
|
||||
<ExpandableCard
|
||||
cardProps={{
|
||||
className: 'expandable-card-contract',
|
||||
title: (
|
||||
<div className="contract-card-title-container">
|
||||
<Typography.Text className="contract-card-title">
|
||||
{t('label.quality')}
|
||||
</Typography.Text>
|
||||
<Typography.Text className="contract-card-description">
|
||||
{t('message.data-quality-test-contract-title')}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
),
|
||||
}}>
|
||||
<div className="expandable-card-contract-body">
|
||||
{isTestCaseLoading ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<div className="data-quality-card-container">
|
||||
{showTestCaseSummaryChart && (
|
||||
<div className="data-quality-chart-container">
|
||||
{testCaseSummaryChartItems.map((item) => (
|
||||
<div
|
||||
className="data-quality-chart-item"
|
||||
key={item.label}>
|
||||
<Typography.Text className="chart-label">
|
||||
{item.label}
|
||||
</Typography.Text>
|
||||
|
||||
<PieChart height={120} width={120}>
|
||||
<Pie
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
data={item.chartData}
|
||||
dataKey="value"
|
||||
innerRadius={40}
|
||||
outerRadius={50}>
|
||||
{item.chartData.map((entry, index) => (
|
||||
<Cell
|
||||
fill={entry.color}
|
||||
key={`cell-${index}`}
|
||||
/>
|
||||
))}
|
||||
</Pie>
|
||||
<text
|
||||
className="chart-center-text"
|
||||
dominantBaseline="middle"
|
||||
textAnchor="middle"
|
||||
x="50%"
|
||||
y="50%">
|
||||
{item.value}
|
||||
</text>
|
||||
</PieChart>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Space direction="vertical">
|
||||
{testCaseResult.map((item) => {
|
||||
return (
|
||||
<div
|
||||
className="data-quality-item d-flex items-center"
|
||||
key={item.id}>
|
||||
{getTestCaseStatusIcon(item)}
|
||||
<div className="data-quality-item-content">
|
||||
<Typography.Text className="data-quality-item-name">
|
||||
{item.name}
|
||||
</Typography.Text>
|
||||
<Typography.Text className="data-quality-item-description">
|
||||
<RichTextEditorPreviewerNew
|
||||
markdown={item.description ?? ''}
|
||||
/>
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Space>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ExpandableCard>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -63,6 +63,43 @@
|
||||
border-color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-btn.delete-button {
|
||||
background-color: transparent;
|
||||
border-color: @error-color;
|
||||
|
||||
svg {
|
||||
width: 20px;
|
||||
color: @error-color;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: @red-9;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-btn.contract-edit-button {
|
||||
font-weight: @font-semibold;
|
||||
span {
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.contract-status-badge-container {
|
||||
margin-top: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.status-badge {
|
||||
padding: 2px 12px;
|
||||
|
||||
.status-badge-label {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,10 +122,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.schema-description {
|
||||
gap: 8px !important;
|
||||
}
|
||||
|
||||
.expandable-card-contract {
|
||||
background: @grey-50;
|
||||
|
||||
@ -114,22 +147,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.delete-button {
|
||||
background: transparent !important;
|
||||
border-color: @error-color !important;
|
||||
|
||||
svg {
|
||||
width: 20px;
|
||||
color: @error-color;
|
||||
}
|
||||
}
|
||||
|
||||
.contract-status-card-item {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid @border-color;
|
||||
margin-bottom: 12px;
|
||||
background: @white;
|
||||
|
||||
.contract-status-card-icon {
|
||||
font-size: 20px;
|
||||
@ -167,8 +191,13 @@
|
||||
gap: 4px;
|
||||
|
||||
.rule-icon {
|
||||
font-size: 20px;
|
||||
font-size: 32px;
|
||||
margin-right: 4px;
|
||||
color: transparent;
|
||||
|
||||
&.rule-icon-default {
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.rule-name {
|
||||
@ -199,6 +228,7 @@
|
||||
border: 1px solid @border-color-8;
|
||||
border-radius: @card-radius;
|
||||
padding: @padding-md;
|
||||
background: @white;
|
||||
box-shadow: @button-box-shadow-default;
|
||||
|
||||
.data-quality-chart-item {
|
||||
@ -252,3 +282,11 @@
|
||||
text-wrap: auto;
|
||||
border: none;
|
||||
}
|
||||
.ant-tag.custom-tag.ant-tag-purple {
|
||||
background-color: @purple-6;
|
||||
color: @purple-5;
|
||||
}
|
||||
.ant-tag.custom-tag.ant-tag-blue {
|
||||
background-color: @blue-33;
|
||||
color: @blue-34;
|
||||
}
|
||||
|
@ -11,13 +11,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Icon, { ArrowLeftOutlined } from '@ant-design/icons';
|
||||
import Icon from '@ant-design/icons';
|
||||
import { Button, Card, Typography } from 'antd';
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import { AxiosError } from 'axios';
|
||||
import { toLower } from 'lodash';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReactComponent as LeftOutlined } from '../../../assets/svg/left-arrow.svg';
|
||||
import { ReactComponent as PlusIcon } from '../../../assets/svg/x-colored.svg';
|
||||
import { DEFAULT_SORT_ORDER } from '../../../constants/profiler.constant';
|
||||
import { EntityType, TabSpecificField } from '../../../enums/entity.enum';
|
||||
@ -36,7 +37,7 @@ import { TEST_LEVEL_OPTIONS } from '../../../utils/DataQuality/DataQualityUtils'
|
||||
import { generateEntityLink } from '../../../utils/TableUtils';
|
||||
import { showErrorToast } from '../../../utils/ToastUtils';
|
||||
import { PagingHandlerParams } from '../../common/NextPrevious/NextPrevious.interface';
|
||||
import { SelectionCard } from '../../common/SelectionCardGroup/SelectionCardGroup';
|
||||
import SelectionCardGroup from '../../common/SelectionCardGroup/SelectionCardGroup';
|
||||
import StatusBadge from '../../common/StatusBadge/StatusBadge.component';
|
||||
import { StatusType } from '../../common/StatusBadge/StatusBadge.interface';
|
||||
import Table from '../../common/Table/Table';
|
||||
@ -203,7 +204,7 @@ export const ContractQualityFormTab: React.FC<{
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className="add-test-case-button"
|
||||
className="contract-export-button"
|
||||
data-testid="add-test-button"
|
||||
icon={<Icon className="anticon" component={PlusIcon} />}
|
||||
onClick={handleOpenTestCaseDrawer}>
|
||||
@ -214,16 +215,11 @@ export const ContractQualityFormTab: React.FC<{
|
||||
</div>
|
||||
|
||||
<div className="contract-form-content-container ">
|
||||
<div className="w-full selection-card-group">
|
||||
{TEST_LEVEL_OPTIONS.map((option) => (
|
||||
<SelectionCard
|
||||
isSelected={testType === option.value}
|
||||
key={option.value}
|
||||
option={option}
|
||||
onClick={() => setTestType(option.value as TestCaseType)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<SelectionCardGroup
|
||||
options={TEST_LEVEL_OPTIONS}
|
||||
value={testType}
|
||||
onChange={(value) => setTestType(value as TestCaseType)}
|
||||
/>
|
||||
<Table
|
||||
columns={columns}
|
||||
customPaginationProps={paginationProps}
|
||||
@ -242,7 +238,11 @@ export const ContractQualityFormTab: React.FC<{
|
||||
</div>
|
||||
|
||||
<div className="d-flex justify-between m-t-md">
|
||||
<Button icon={<ArrowLeftOutlined />} type="default" onClick={onPrev}>
|
||||
<Button
|
||||
className="contract-prev-button"
|
||||
icon={<LeftOutlined height={22} width={20} />}
|
||||
type="default"
|
||||
onClick={onPrev}>
|
||||
{prevLabel ?? t('label.previous')}
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -14,13 +14,7 @@
|
||||
@import (reference) url('../../../styles/variables.less');
|
||||
|
||||
.contract-quality-form-tab-container {
|
||||
.add-test-case-button {
|
||||
font-weight: 600;
|
||||
color: @grey-700;
|
||||
border: 1px solid @grey-300 !important;
|
||||
box-shadow: 0px 1px 2px rgba(10, 13, 18, 0.05),
|
||||
inset 0px -2px 0px rgba(10, 13, 18, 0.05);
|
||||
|
||||
.contract-export-button {
|
||||
.anticon {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
@ -10,12 +10,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ArrowLeftOutlined, ArrowRightOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Tag, Typography } from 'antd';
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { isEmpty, pick } from 'lodash';
|
||||
import { Key, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReactComponent as LeftOutlined } from '../../../assets/svg/left-arrow.svg';
|
||||
import { ReactComponent as RightOutlined } from '../../../assets/svg/right-arrow.svg';
|
||||
import { FQN_SEPARATOR_CHAR } from '../../../constants/char.constants';
|
||||
import {
|
||||
NO_DATA_PLACEHOLDER,
|
||||
@ -213,6 +214,7 @@ export const ContractSchemaFormTab: React.FC<{
|
||||
render: (tags: TagLabel[], record: Column, index: number) => (
|
||||
<TableTags<Column>
|
||||
isReadOnly
|
||||
newLook
|
||||
entityFqn={tableFqn}
|
||||
entityType={EntityType.TABLE}
|
||||
handleTagSelection={() => Promise.resolve()}
|
||||
@ -228,19 +230,37 @@ export const ContractSchemaFormTab: React.FC<{
|
||||
title: t('label.glossary-term-plural'),
|
||||
dataIndex: TABLE_COLUMNS_KEYS.TAGS,
|
||||
key: TABLE_COLUMNS_KEYS.GLOSSARY,
|
||||
render: (tags: TagLabel[], record: Column, index: number) => (
|
||||
<TableTags<Column>
|
||||
isReadOnly
|
||||
entityFqn={tableFqn}
|
||||
entityType={EntityType.TABLE}
|
||||
handleTagSelection={() => Promise.resolve()}
|
||||
hasTagEditAccess={false}
|
||||
index={index}
|
||||
record={record}
|
||||
tags={tags}
|
||||
type={TagSource.Glossary}
|
||||
/>
|
||||
),
|
||||
render: (tags: TagLabel[], record: Column, index: number) => {
|
||||
// To remove Source from the tag so that we can have consistant tag icon
|
||||
const newTags = tags.map((tag) => {
|
||||
return {
|
||||
tagFQN: tag.tagFQN,
|
||||
...pick(
|
||||
tag,
|
||||
'description',
|
||||
'displayName',
|
||||
'labelType',
|
||||
'name',
|
||||
'style'
|
||||
),
|
||||
} as TagLabel;
|
||||
});
|
||||
|
||||
return (
|
||||
<TableTags<Column>
|
||||
isReadOnly
|
||||
newLook
|
||||
entityFqn={tableFqn}
|
||||
entityType={EntityType.TABLE}
|
||||
handleTagSelection={() => Promise.resolve()}
|
||||
hasTagEditAccess={false}
|
||||
index={index}
|
||||
record={record}
|
||||
tags={newTags}
|
||||
type={TagSource.Glossary}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('label.constraint-plural'),
|
||||
@ -249,7 +269,7 @@ export const ContractSchemaFormTab: React.FC<{
|
||||
render: renderConstraint,
|
||||
},
|
||||
],
|
||||
[]
|
||||
[tableFqn]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@ -281,12 +301,19 @@ export const ContractSchemaFormTab: React.FC<{
|
||||
/>
|
||||
</Card>
|
||||
<div className="d-flex justify-between m-t-md">
|
||||
<Button icon={<ArrowLeftOutlined />} type="default" onClick={onPrev}>
|
||||
<Button
|
||||
className="contract-prev-button"
|
||||
icon={<LeftOutlined height={22} width={20} />}
|
||||
type="default"
|
||||
onClick={onPrev}>
|
||||
{prevLabel ?? t('label.previous')}
|
||||
</Button>
|
||||
<Button type="primary" onClick={onNext}>
|
||||
<Button
|
||||
className="contract-next-button"
|
||||
type="primary"
|
||||
onClick={onNext}>
|
||||
{nextLabel ?? t('label.next')}
|
||||
<ArrowRightOutlined />
|
||||
<RightOutlined height={15} width={8} />
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
|
@ -11,7 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Icon, { ArrowLeftOutlined, ArrowRightOutlined } from '@ant-design/icons';
|
||||
import Icon from '@ant-design/icons';
|
||||
import { Actions } from '@react-awesome-query-builder/antd';
|
||||
import { FieldErrorProps } from '@rjsf/utils';
|
||||
import { Button, Col, Form, Input, Row, Switch, Typography } from 'antd';
|
||||
@ -21,6 +21,9 @@ import classNames from 'classnames';
|
||||
import { isNull } from 'lodash';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReactComponent as DeleteIcon } from '../../../assets/svg/ic-trash.svg';
|
||||
import { ReactComponent as LeftOutlined } from '../../../assets/svg/left-arrow.svg';
|
||||
import { ReactComponent as RightOutlined } from '../../../assets/svg/right-arrow.svg';
|
||||
import { ReactComponent as PlusIcon } from '../../../assets/svg/x-colored.svg';
|
||||
import { EntityType } from '../../../enums/entity.enum';
|
||||
import { DataContract } from '../../../generated/entity/data/dataContract';
|
||||
@ -40,7 +43,10 @@ export const ContractSemanticFormTab: React.FC<{
|
||||
}> = ({ onChange, onNext, onPrev, nextLabel, prevLabel, initialValues }) => {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const semanticsData = Form.useWatch('semantics', form);
|
||||
const semanticsFormData: DataContract['semantics'] = Form.useWatch(
|
||||
'semantics',
|
||||
form
|
||||
);
|
||||
const [editingKey, setEditingKey] = useState<number | null>(null);
|
||||
const [queryBuilderAddRule, setQueryBuilderAddRule] = useState<Actions>();
|
||||
const addFunctionRef = useRef<((defaultValue?: any) => void) | null>(null);
|
||||
@ -56,32 +62,47 @@ export const ContractSemanticFormTab: React.FC<{
|
||||
rule: '',
|
||||
enabled: true,
|
||||
});
|
||||
setEditingKey(semanticsData.length);
|
||||
setEditingKey(semanticsFormData?.length ?? 0);
|
||||
};
|
||||
|
||||
const handleDeleteSemantic = useCallback(
|
||||
(key: number) => {
|
||||
const filteredValue = semanticsFormData?.filter(
|
||||
(_, index) => index !== key
|
||||
);
|
||||
|
||||
form.setFieldsValue({
|
||||
semantics: filteredValue,
|
||||
});
|
||||
onChange({
|
||||
semantics: filteredValue,
|
||||
});
|
||||
},
|
||||
[semanticsFormData]
|
||||
);
|
||||
|
||||
const handleAddNewRule = useCallback(() => {
|
||||
queryBuilderAddRule?.addRule([]);
|
||||
}, [queryBuilderAddRule]);
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue({
|
||||
semantics: [
|
||||
{
|
||||
name: '',
|
||||
description: '',
|
||||
enabled: true,
|
||||
rule: '',
|
||||
},
|
||||
],
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (initialValues?.semantics) {
|
||||
form.setFieldsValue({
|
||||
semantics: initialValues.semantics,
|
||||
});
|
||||
} else {
|
||||
form.setFieldsValue({
|
||||
semantics: [
|
||||
{
|
||||
name: '',
|
||||
description: '',
|
||||
enabled: true,
|
||||
rule: '',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
setEditingKey(0);
|
||||
}, [initialValues]);
|
||||
|
||||
return (
|
||||
@ -110,6 +131,7 @@ export const ContractSemanticFormTab: React.FC<{
|
||||
</div>
|
||||
|
||||
<Form
|
||||
className="new-form-style"
|
||||
form={form}
|
||||
layout="vertical"
|
||||
onValuesChange={(_, allValues) => {
|
||||
@ -134,34 +156,50 @@ export const ContractSemanticFormTab: React.FC<{
|
||||
title: (
|
||||
<div className="w-full d-flex justify-between items-center">
|
||||
{editingKey === field.key ? null : (
|
||||
<>
|
||||
<div className="semantic-form-item-title-container">
|
||||
<div className="d-flex items-center gap-6">
|
||||
<Form.Item
|
||||
{...field}
|
||||
className="enable-form-item"
|
||||
name={[field.name, 'enabled']}
|
||||
valuePropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<div className="d-flex flex-column">
|
||||
<Typography.Text>
|
||||
{semanticsData[field.key]?.name ||
|
||||
<Typography.Text className="semantic-form-item-title">
|
||||
{semanticsFormData?.[field.key]?.name ||
|
||||
t('label.untitled')}
|
||||
</Typography.Text>
|
||||
<Typography.Text type="secondary">
|
||||
{semanticsData[field.key]
|
||||
<Typography.Text
|
||||
ellipsis
|
||||
className="semantic-form-item-description">
|
||||
{semanticsFormData?.[field.key]
|
||||
?.description ||
|
||||
t('label.no-description')}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
<EditIconButton
|
||||
newLook
|
||||
data-testid={`edit-semantic=${field.key}`}
|
||||
size="small"
|
||||
onClick={() => setEditingKey(field.key)}
|
||||
/>
|
||||
</>
|
||||
<div className="d-flex items-center gap-2">
|
||||
<EditIconButton
|
||||
newLook
|
||||
className="edit-expand-button"
|
||||
data-testid={`edit-semantic-${field.key}`}
|
||||
size="middle"
|
||||
onClick={() => setEditingKey(field.key)}
|
||||
/>
|
||||
|
||||
<Button
|
||||
danger
|
||||
className="delete-expand-button"
|
||||
icon={<DeleteIcon />}
|
||||
size="middle"
|
||||
onClick={() => {
|
||||
handleDeleteSemantic(field.key);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
@ -188,13 +226,20 @@ export const ContractSemanticFormTab: React.FC<{
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item
|
||||
{...field}
|
||||
label={t('label.enabled')}
|
||||
name={[field.name, 'enabled']}
|
||||
valuePropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<div className="d-flex gap-2 items-center m-b-md">
|
||||
<Form.Item
|
||||
{...field}
|
||||
className="m-b-0"
|
||||
name={[field.name, 'enabled']}
|
||||
valuePropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Typography.Paragraph className="font-medium m-0">
|
||||
{t('label.enable-entity', {
|
||||
entity: t('label.semantic-plural'),
|
||||
})}
|
||||
</Typography.Paragraph>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item
|
||||
@ -211,12 +256,6 @@ export const ContractSemanticFormTab: React.FC<{
|
||||
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,
|
||||
@ -263,7 +302,7 @@ export const ContractSemanticFormTab: React.FC<{
|
||||
schema={{
|
||||
outputType: SearchOutputType.JSONLogic,
|
||||
}}
|
||||
value={semanticsData[field.key]?.rule ?? {}}
|
||||
value={semanticsFormData?.[field.key]?.rule ?? {}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@ -278,12 +317,18 @@ export const ContractSemanticFormTab: React.FC<{
|
||||
</Card>
|
||||
|
||||
<div className="d-flex justify-between m-t-md">
|
||||
<Button icon={<ArrowLeftOutlined />} onClick={onPrev}>
|
||||
<Button
|
||||
className="contract-prev-button"
|
||||
icon={<LeftOutlined height={22} width={20} />}
|
||||
onClick={onPrev}>
|
||||
{prevLabel ?? t('label.previous')}
|
||||
</Button>
|
||||
<Button type="primary" onClick={onNext}>
|
||||
<Button
|
||||
className="contract-next-button"
|
||||
type="primary"
|
||||
onClick={onNext}>
|
||||
{nextLabel ?? t('label.next')}
|
||||
<ArrowRightOutlined />
|
||||
<RightOutlined height={15} width={8} />
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
|
@ -24,21 +24,92 @@
|
||||
}
|
||||
|
||||
.contract-semantic-form-container {
|
||||
.expanded {
|
||||
.ant-card.expanded {
|
||||
.ant-card-head {
|
||||
border-bottom-left-radius: 0 !important;
|
||||
border-bottom-right-radius: 0 !important;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.expandable-card {
|
||||
.ant-card.new-header-border-card.expandable-card {
|
||||
margin-top: 16px;
|
||||
border: 1px solid @border-color-7 !important;
|
||||
border: 1px solid @border-color-7;
|
||||
box-shadow: 0 1px 2px 0 @grey-27;
|
||||
|
||||
.ant-card-head {
|
||||
background: @white !important;
|
||||
border-bottom: 1px solid @grey-200 !important;
|
||||
background: @white;
|
||||
border-bottom: 1px solid @grey-200;
|
||||
|
||||
.semantic-form-item-title-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
gap: 12px;
|
||||
|
||||
.enable-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
> div:first-child {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.semantic-form-item-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.semantic-form-item-description {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: @grey-600;
|
||||
}
|
||||
|
||||
.ant-btn.edit-expand-button {
|
||||
border-radius: 6px;
|
||||
border: 1px solid @border-light;
|
||||
|
||||
svg {
|
||||
font-size: 20px;
|
||||
color: @grey-600;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-btn.delete-expand-button {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
border-color: @error-color;
|
||||
|
||||
svg {
|
||||
width: 20px;
|
||||
color: @error-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-card-extra {
|
||||
.ant-btn.expand-collapse-icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid @border-light;
|
||||
|
||||
svg {
|
||||
font-size: 20px;
|
||||
color: @grey-600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,6 +148,8 @@
|
||||
.ant-btn-group {
|
||||
.action--DELETE {
|
||||
border: 1px solid @grey-34;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
|
||||
.anticon {
|
||||
color: @grey-400;
|
||||
@ -92,9 +165,14 @@
|
||||
}
|
||||
|
||||
.semantic-rule-editor-view-only {
|
||||
.ant-divider,
|
||||
.group--conjunctions {
|
||||
display: none !important;
|
||||
.ant-divider {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.query-builder {
|
||||
.group--conjunctions {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
|
@ -26,6 +26,7 @@ import Loader from '../../common/Loader/Loader';
|
||||
import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider';
|
||||
import AddDataContract from '../AddDataContract/AddDataContract';
|
||||
import { ContractDetail } from '../ContractDetailTab/ContractDetail';
|
||||
import './contract-tab.less';
|
||||
|
||||
export const ContractTab = () => {
|
||||
const {
|
||||
@ -118,5 +119,9 @@ export const ContractTab = () => {
|
||||
}
|
||||
}, [tabMode, contract]);
|
||||
|
||||
return isLoading ? <Loader /> : content;
|
||||
return isLoading ? (
|
||||
<Loader />
|
||||
) : (
|
||||
<div className="contract-tab-container">{content}</div>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2025 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@import (reference) url('../../../styles/variables.less');
|
||||
|
||||
.contract-tab-container {
|
||||
.contract-divider {
|
||||
height: 24px;
|
||||
border-left: 1px solid @grey-15;
|
||||
}
|
||||
|
||||
.ant-btn.ant-btn-default.contract-export-button {
|
||||
font-weight: 600;
|
||||
color: @grey-700;
|
||||
border: 1px solid @grey-300;
|
||||
box-shadow: 0px 1px 2px rgba(10, 13, 18, 0.05),
|
||||
inset 0px -2px 0px rgba(10, 13, 18, 0.05);
|
||||
|
||||
.anticon {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: @grey-700;
|
||||
background-color: @grey-50;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
color: @grey-700;
|
||||
background-color: @grey-50;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2025 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Radio, RadioChangeEvent } from 'antd';
|
||||
import { ReactComponent as BookOutline } from '../../../assets/svg/bookoutline.svg';
|
||||
import { ReactComponent as CodeOutline } from '../../../assets/svg/codeOutline.svg';
|
||||
import { DE_ACTIVE_COLOR } from '../../../constants/constants';
|
||||
import { DataContractMode } from '../../../constants/DataContract.constants';
|
||||
import './contract-view-switch-tab.less';
|
||||
|
||||
const ContractViewSwitchTab = ({
|
||||
mode,
|
||||
handleModeChange,
|
||||
}: {
|
||||
mode: DataContractMode;
|
||||
handleModeChange: (e: RadioChangeEvent) => void;
|
||||
}) => {
|
||||
return (
|
||||
<Radio.Group
|
||||
className="contract-mode-radio-group"
|
||||
optionType="button"
|
||||
options={[
|
||||
{
|
||||
label: (
|
||||
<CodeOutline
|
||||
className="align-middle"
|
||||
color={DE_ACTIVE_COLOR}
|
||||
height={20}
|
||||
width={20}
|
||||
/>
|
||||
),
|
||||
value: DataContractMode.YAML,
|
||||
},
|
||||
{
|
||||
label: (
|
||||
<BookOutline
|
||||
className="align-middle"
|
||||
color={DE_ACTIVE_COLOR}
|
||||
height={20}
|
||||
width={20}
|
||||
/>
|
||||
),
|
||||
value: DataContractMode.UI,
|
||||
},
|
||||
]}
|
||||
value={mode}
|
||||
onChange={handleModeChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContractViewSwitchTab;
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2025 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@import (reference) url('../../../styles/variables.less');
|
||||
|
||||
.contract-mode-radio-group {
|
||||
border-radius: @size-xs;
|
||||
border: 1px solid @border-color-7;
|
||||
padding: 4px;
|
||||
overflow: hidden;
|
||||
.ant-radio-button-wrapper:first-child {
|
||||
border-radius: @size-xs 0 0 @size-xs;
|
||||
}
|
||||
.ant-radio-button-wrapper:last-child {
|
||||
border-radius: 0 @size-xs @size-xs 0;
|
||||
}
|
||||
|
||||
.ant-radio-button-wrapper-checked {
|
||||
svg {
|
||||
path {
|
||||
stroke: @primary-7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2025 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import yaml from 'js-yaml';
|
||||
import { useMemo } from 'react';
|
||||
import { CSMode } from '../../../enums/codemirror.enum';
|
||||
import { DataContract } from '../../../generated/entity/data/dataContract';
|
||||
import { getUpdatedContractDetails } from '../../../utils/DataContract/DataContractUtils';
|
||||
import SchemaEditor from '../../Database/SchemaEditor/SchemaEditor';
|
||||
import './contract-yaml.less';
|
||||
|
||||
const ContractYaml = ({ contract }: { contract: DataContract }) => {
|
||||
const schemaEditorValue = useMemo(() => {
|
||||
return yaml.dump(getUpdatedContractDetails(contract, contract));
|
||||
}, [contract]);
|
||||
|
||||
return (
|
||||
<div className="contract-yaml-container">
|
||||
<SchemaEditor
|
||||
className="contract-yaml-schema-editor"
|
||||
editorClass="custom-entity-schema"
|
||||
mode={{ name: CSMode.YAML }}
|
||||
value={schemaEditorValue}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContractYaml;
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2025 Collate.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
@import (reference) url('../../../styles/variables.less');
|
||||
|
||||
.contract-yaml-container {
|
||||
width: 100%;
|
||||
|
||||
.contract-yaml-schema-editor {
|
||||
border-radius: 12px;
|
||||
border: 1px solid @grey-200;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.contract-schema-editor > .CodeMirror {
|
||||
height: calc(
|
||||
100vh - @data-asset-header-height - @tab-height - @om-navbar-height -
|
||||
@size-xs - 120px
|
||||
);
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Icon from '@ant-design/icons';
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { Editor, EditorChange } from 'codemirror';
|
||||
@ -29,7 +30,7 @@ import { isUndefined } from 'lodash';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Controlled as CodeMirror } from 'react-codemirror2';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ReactComponent as CopyIcon } from '../../../assets/svg/icon-copy.svg';
|
||||
import { ReactComponent as CopyIcon } from '../../../assets/svg/ic-duplicate.svg';
|
||||
import { JSON_TAB_SIZE } from '../../../constants/constants';
|
||||
import { CSMode } from '../../../enums/codemirror.enum';
|
||||
import { useClipboard } from '../../../hooks/useClipBoard';
|
||||
@ -122,7 +123,7 @@ const SchemaEditor = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('relative', className)}
|
||||
className={classNames('schema-editor-container relative', className)}
|
||||
data-testid="code-mirror-container">
|
||||
{showCopyButton && (
|
||||
<div className="query-editor-button">
|
||||
@ -131,9 +132,9 @@ const SchemaEditor = ({
|
||||
hasCopied ? t('label.copied') : t('message.copy-to-clipboard')
|
||||
}>
|
||||
<Button
|
||||
className="flex-center bg-white"
|
||||
className="query-editor-copy-button"
|
||||
data-testid="query-copy-button"
|
||||
icon={<CopyIcon height={16} width={16} />}
|
||||
icon={<Icon component={CopyIcon} />}
|
||||
onClick={onCopyToClipBoard}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
@ -11,9 +11,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.query-editor-button {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
top: 14px;
|
||||
right: 14px;
|
||||
@import (reference) url('../../../styles/variables.less');
|
||||
|
||||
.schema-editor-container {
|
||||
.query-editor-button {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
top: 14px;
|
||||
right: 14px;
|
||||
}
|
||||
|
||||
.query-editor-copy-button {
|
||||
background: @white;
|
||||
color: @grey-600;
|
||||
|
||||
svg {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ const TableTags = <T extends TableUnion>({
|
||||
showInlineEditTagButton,
|
||||
handleTagSelection,
|
||||
entityType,
|
||||
newLook = false,
|
||||
}: TableTagsComponentProps<T>) => {
|
||||
const { onThreadLinkSelect, updateActiveTagDropdownKey } =
|
||||
useGenericContext();
|
||||
@ -54,6 +55,7 @@ const TableTags = <T extends TableUnion>({
|
||||
selectedTags={tags}
|
||||
showInlineEditButton={showInlineEditTagButton}
|
||||
sizeCap={TAG_LIST_SIZE}
|
||||
tagNewLook={newLook}
|
||||
tagType={type}
|
||||
onSelectionChange={async (selectedTags) => {
|
||||
await handleTagSelection(selectedTags, record);
|
||||
|
@ -34,6 +34,7 @@ export interface TableTagsComponentProps<T> {
|
||||
selectedTags: EntityTags[],
|
||||
editColumnTag: T
|
||||
) => Promise<void>;
|
||||
newLook?: boolean;
|
||||
}
|
||||
|
||||
export interface TableTagsProps {
|
||||
|
@ -37,4 +37,5 @@ export interface TagsContainerV2Props {
|
||||
newLook?: boolean;
|
||||
// Props to control the dropdown state from the Generic Provider
|
||||
useGenericControls?: boolean;
|
||||
tagNewLook?: boolean;
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ const TagsContainerV2 = ({
|
||||
newLook = false,
|
||||
sizeCap = LIST_SIZE,
|
||||
useGenericControls,
|
||||
tagNewLook = false,
|
||||
}: TagsContainerV2Props) => {
|
||||
const navigate = useNavigate();
|
||||
const [form] = Form.useForm();
|
||||
@ -231,6 +232,7 @@ const TagsContainerV2 = ({
|
||||
<Col span={24}>
|
||||
<TagsViewer
|
||||
displayType={displayType}
|
||||
newLook={tagNewLook}
|
||||
showNoDataPlaceholder={showNoDataPlaceholder}
|
||||
sizeCap={sizeCap}
|
||||
tagType={tagType}
|
||||
@ -388,6 +390,7 @@ const TagsContainerV2 = ({
|
||||
) : null}
|
||||
<TagsViewer
|
||||
displayType={displayType}
|
||||
newLook={newLook}
|
||||
showNoDataPlaceholder={showNoDataPlaceholder}
|
||||
sizeCap={sizeCap}
|
||||
tags={tags?.[tagType] ?? []}
|
||||
|
@ -120,11 +120,26 @@ const TagsV1 = ({
|
||||
[color]
|
||||
);
|
||||
|
||||
const tagChipStyleClass = useMemo(() => {
|
||||
if (newLook && !tag.style?.color) {
|
||||
return 'new-chip-style';
|
||||
}
|
||||
if (newLook && tag.style?.color) {
|
||||
return 'new-chip-style-with-color';
|
||||
}
|
||||
|
||||
return '';
|
||||
}, [newLook, tag.style?.color]);
|
||||
|
||||
const tagContent = useMemo(
|
||||
() => (
|
||||
<div className="d-flex w-full h-full">
|
||||
{tagColorBar}
|
||||
<div className="d-flex items-center p-x-xs w-full">
|
||||
<div
|
||||
className={classNames(
|
||||
'd-flex items-center p-x-xs w-full',
|
||||
tagChipStyleClass
|
||||
)}>
|
||||
{tag.style?.iconURL ? (
|
||||
<img
|
||||
className="m-r-xss"
|
||||
@ -155,12 +170,14 @@ const TagsV1 = ({
|
||||
<Tag
|
||||
className={classNames(
|
||||
className,
|
||||
'tag-chip tag-chip-content',
|
||||
tagChipStyleClass,
|
||||
{
|
||||
'tag-highlight': Boolean(
|
||||
(tag as HighlightedTagLabel).isHighlighted
|
||||
),
|
||||
},
|
||||
'tag-chip tag-chip-content',
|
||||
|
||||
size,
|
||||
'cursor-pointer'
|
||||
)}
|
||||
|
@ -52,7 +52,34 @@
|
||||
margin: inherit;
|
||||
}
|
||||
}
|
||||
.ant-tag.tag-chip-content.new-chip-style {
|
||||
.ant-select-selection-item {
|
||||
border: 1px solid @blue-30;
|
||||
border-radius: @border-rad-xs;
|
||||
background: @blue-29;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.ant-select-selection-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
border: 1px solid @blue-30;
|
||||
background: @blue-29;
|
||||
border-radius: @border-rad-xs;
|
||||
.tags-label {
|
||||
color: @tag-text;
|
||||
font-weight: @font-regular;
|
||||
}
|
||||
}
|
||||
.ant-tag.tag-chip-content.new-chip-style-with-color {
|
||||
border: 1px solid @blue-30;
|
||||
border-radius: @border-rad-xs;
|
||||
.tags-label {
|
||||
color: @tag-text;
|
||||
font-weight: @font-regular;
|
||||
}
|
||||
}
|
||||
.ant-tag.tag-chip-add-button {
|
||||
padding: 3px 8px;
|
||||
background: @white;
|
||||
|
@ -20,6 +20,7 @@ export interface TagsViewerProps {
|
||||
displayType?: DisplayType;
|
||||
showNoDataPlaceholder?: boolean;
|
||||
tagType?: TagSource;
|
||||
newLook?: boolean;
|
||||
}
|
||||
|
||||
export enum DisplayType {
|
||||
|
@ -29,6 +29,7 @@ const TagsViewer: FunctionComponent<TagsViewerProps> = ({
|
||||
sizeCap = LIST_SIZE,
|
||||
displayType = DisplayType.POPOVER,
|
||||
showNoDataPlaceholder = true,
|
||||
newLook = false,
|
||||
}: TagsViewerProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
@ -42,6 +43,7 @@ const TagsViewer: FunctionComponent<TagsViewerProps> = ({
|
||||
)}
|
||||
isVersionPage={tag?.added || tag?.removed}
|
||||
key={tag.tagFQN}
|
||||
newLook={newLook}
|
||||
showOnlyName={tag.source === TagSource.Glossary}
|
||||
startWith={TAG_START_WITH.SOURCE_ICON}
|
||||
tag={tag}
|
||||
|
@ -38,15 +38,14 @@ import { debounce, isEmpty, isUndefined } from 'lodash';
|
||||
import Qs from 'qs';
|
||||
import { FC, useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
EntityFields,
|
||||
EntityReferenceFields,
|
||||
} from '../../../../../../enums/AdvancedSearch.enum';
|
||||
import { EntityType } from '../../../../../../enums/entity.enum';
|
||||
import { SearchIndex } from '../../../../../../enums/search.enum';
|
||||
import { QueryFilterInterface } from '../../../../../../pages/ExplorePage/ExplorePage.interface';
|
||||
import { searchQuery } from '../../../../../../rest/searchAPI';
|
||||
import { getEmptyJsonTreeForQueryBuilder } from '../../../../../../utils/AdvancedSearchUtils';
|
||||
import {
|
||||
getEmptyJsonTree,
|
||||
getEmptyJsonTreeForQueryBuilder,
|
||||
} from '../../../../../../utils/AdvancedSearchUtils';
|
||||
import { elasticSearchFormat } from '../../../../../../utils/QueryBuilderElasticsearchFormatUtils';
|
||||
import {
|
||||
addEntityTypeFilter,
|
||||
@ -201,16 +200,12 @@ const QueryBuilderWidget: FC<WidgetProps> = ({
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const emptyJsonTree = getEmptyJsonTreeForQueryBuilder(
|
||||
const emptyJsonTree =
|
||||
outputType === SearchOutputType.JSONLogic
|
||||
? EntityReferenceFields.OWNERS
|
||||
: EntityFields.OWNERS
|
||||
);
|
||||
? getEmptyJsonTreeForQueryBuilder()
|
||||
: getEmptyJsonTree();
|
||||
|
||||
const tree = QbUtils.Validation.sanitizeTree(
|
||||
QbUtils.loadTree(emptyJsonTree),
|
||||
config
|
||||
).fixedTree;
|
||||
const tree = QbUtils.loadTree(emptyJsonTree);
|
||||
|
||||
onTreeUpdate(tree, config);
|
||||
}
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "Link einbetten",
|
||||
"enable": "Aktivieren",
|
||||
"enable-debug-log": "Debug-Protokoll aktivieren",
|
||||
"enable-entity": "Enable {{entity}}",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "aktivieren",
|
||||
"enable-partition": "Partition aktivieren",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "Embed link",
|
||||
"enable": "Enable",
|
||||
"enable-debug-log": "Enable Debug Log",
|
||||
"enable-entity": "Enable {{entity}}",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "enable",
|
||||
"enable-partition": "Enable Partition",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "Insertar Link",
|
||||
"enable": "Habilitar",
|
||||
"enable-debug-log": "Activar logs con debug",
|
||||
"enable-entity": "Habilitar {{entity}}",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "habilitar",
|
||||
"enable-partition": "Habilitar partición",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "Incorporer lien",
|
||||
"enable": "Activer",
|
||||
"enable-debug-log": "Activer le Journal de Débogage",
|
||||
"enable-entity": "Activer {{entity}}",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "activer",
|
||||
"enable-partition": "Activer la Partition",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "Incrustar ligazón",
|
||||
"enable": "Activar",
|
||||
"enable-debug-log": "Activar rexistro de depuración",
|
||||
"enable-entity": "Habilitar {{entity}}",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "activar",
|
||||
"enable-partition": "Activar partición",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "הטמע קישור",
|
||||
"enable": "הפעל",
|
||||
"enable-debug-log": "הפעל יומן דיבוג",
|
||||
"enable-entity": "הפעל {{entity}}",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "הפעל",
|
||||
"enable-partition": "הפעל מחיצה",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "リンクを埋め込む",
|
||||
"enable": "有効化",
|
||||
"enable-debug-log": "デバッグログを有効化",
|
||||
"enable-entity": "{{entity}} を有効化",
|
||||
"enable-incident-management": "インシデント管理を有効化",
|
||||
"enable-lowercase": "有効化",
|
||||
"enable-partition": "パーティションを有効化",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "링크 삽입",
|
||||
"enable": "활성화",
|
||||
"enable-debug-log": "디버그 로그 활성화",
|
||||
"enable-entity": "{{entity}} 활성화",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "활성화",
|
||||
"enable-partition": "파티션 활성화",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "लिंक एम्बेड करा",
|
||||
"enable": "सक्षम करा",
|
||||
"enable-debug-log": "डिबग लॉग सक्षम करा",
|
||||
"enable-entity": "{{entity}} सक्षम करा",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "सक्षम करा",
|
||||
"enable-partition": "विभाजन सक्षम करा",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "Koppeling insluiten",
|
||||
"enable": "Inschakelen",
|
||||
"enable-debug-log": "Debuglog inschakelen",
|
||||
"enable-entity": "{{entity}} inschakelen",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "inschakelen",
|
||||
"enable-partition": "Partitie inschakelen",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "قرار دادن لینک",
|
||||
"enable": "فعال کردن",
|
||||
"enable-debug-log": "فعال کردن گزارش اشکالزدایی",
|
||||
"enable-entity": "فعال کردن {{entity}}",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "فعال کردن",
|
||||
"enable-partition": "فعال کردن پارتیشن",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "Incorporar link",
|
||||
"enable": "Habilitar",
|
||||
"enable-debug-log": "Habilitar Log de Depuração",
|
||||
"enable-entity": "Habilitar {{entity}}",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "habilitar",
|
||||
"enable-partition": "Habilitar Partição",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "Incorporar link",
|
||||
"enable": "Habilitar",
|
||||
"enable-debug-log": "Habilitar Log de Depuração",
|
||||
"enable-entity": "Habilitar {{entity}}",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "habilitar",
|
||||
"enable-partition": "Habilitar Partição",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "Встроить ссылку",
|
||||
"enable": "Включить",
|
||||
"enable-debug-log": "Включить журнал отладки",
|
||||
"enable-entity": "Включить {{entity}}",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "включить",
|
||||
"enable-partition": "Включить раздел",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "ฝังลิงก์",
|
||||
"enable": "เปิดใช้งาน",
|
||||
"enable-debug-log": "เปิดใช้งานบันทึกการดีบัก",
|
||||
"enable-entity": "เปิดใช้งาน {{entity}}",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "เปิดใช้งาน",
|
||||
"enable-partition": "เปิดใช้งานการแบ่งส่วน",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "Bağlantı göm",
|
||||
"enable": "Etkinleştir",
|
||||
"enable-debug-log": "Hata Ayıklama Günlüğünü Etkinleştir",
|
||||
"enable-entity": "{{entity}} Etkinleştir",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "etkinleştir",
|
||||
"enable-partition": "Bölümü Etkinleştir",
|
||||
|
@ -535,6 +535,7 @@
|
||||
"embed-link": "插入链接",
|
||||
"enable": "启用",
|
||||
"enable-debug-log": "启用调试日志",
|
||||
"enable-entity": "启用{{entity}}",
|
||||
"enable-incident-management": "Enable Incident Management",
|
||||
"enable-lowercase": "启用",
|
||||
"enable-partition": "启用分区",
|
||||
|
@ -90,6 +90,7 @@
|
||||
@purple-3: #a2a1ff;
|
||||
@purple-4: #efedfe80;
|
||||
@purple-5: #6941c6;
|
||||
@purple-6: #f9f5ff;
|
||||
@blue-1: #ebf6fe;
|
||||
@blue-2: #3ca2f4;
|
||||
@blue-3: #0950c5;
|
||||
|
@ -33,7 +33,10 @@ import {
|
||||
LINEAGE_DROPDOWN_ITEMS,
|
||||
} from '../constants/AdvancedSearch.constants';
|
||||
import { NOT_INCLUDE_AGGREGATION_QUICK_FILTER } from '../constants/explore.constants';
|
||||
import { EntityFields } from '../enums/AdvancedSearch.enum';
|
||||
import {
|
||||
EntityFields,
|
||||
EntityReferenceFields,
|
||||
} from '../enums/AdvancedSearch.enum';
|
||||
import { EntityType } from '../enums/entity.enum';
|
||||
import { SearchIndex } from '../enums/search.enum';
|
||||
import {
|
||||
@ -451,23 +454,42 @@ export const getEmptyJsonTree = (
|
||||
* This structure allows easy addition of groups and rules
|
||||
*/
|
||||
export const getEmptyJsonTreeForQueryBuilder = (
|
||||
defaultField: string = EntityFields.OWNERS
|
||||
defaultField: string = EntityReferenceFields.OWNERS
|
||||
): OldJsonTree => {
|
||||
const uuid1 = QbUtils.uuid();
|
||||
const uuid2 = QbUtils.uuid();
|
||||
const uuid3 = QbUtils.uuid();
|
||||
|
||||
return {
|
||||
id: QbUtils.uuid(),
|
||||
id: uuid1,
|
||||
type: 'group',
|
||||
properties: {
|
||||
conjunction: 'AND',
|
||||
not: false,
|
||||
},
|
||||
children1: {
|
||||
[QbUtils.uuid()]: {
|
||||
type: 'rule',
|
||||
[uuid2]: {
|
||||
type: 'rule_group',
|
||||
id: uuid2,
|
||||
properties: {
|
||||
conjunction: 'AND',
|
||||
not: false,
|
||||
mode: 'some',
|
||||
field: defaultField,
|
||||
operator: null,
|
||||
value: [],
|
||||
valueSrc: ['value'],
|
||||
fieldSrc: 'field',
|
||||
},
|
||||
children1: {
|
||||
[uuid3]: {
|
||||
type: 'rule',
|
||||
id: uuid3,
|
||||
properties: {
|
||||
field: 'owners.fullyQualifiedName',
|
||||
operator: 'select_equals',
|
||||
value: [],
|
||||
valueSrc: ['value'],
|
||||
fieldSrc: 'field',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -11,6 +11,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import i18next from 'i18next';
|
||||
import yaml from 'js-yaml';
|
||||
import { omit } from 'lodash';
|
||||
import { ReactComponent as QualityIcon } from '../../assets/svg/policies.svg';
|
||||
import { ReactComponent as SemanticsIcon } from '../../assets/svg/semantics.svg';
|
||||
@ -183,3 +184,17 @@ export const getUpdatedContractDetails = (
|
||||
'incrementalChangeDescription',
|
||||
]);
|
||||
};
|
||||
|
||||
export const downloadContractYamlFile = (contract: DataContract) => {
|
||||
const data = yaml.dump(getUpdatedContractDetails(contract, contract));
|
||||
const element = document.createElement('a');
|
||||
const file = new Blob([data], { type: 'text/plain' });
|
||||
element.textContent = 'download-file';
|
||||
element.href = URL.createObjectURL(file);
|
||||
element.download = `${contract.name}.yaml`;
|
||||
document.body.appendChild(element);
|
||||
element.click();
|
||||
|
||||
URL.revokeObjectURL(element.href);
|
||||
document.body.removeChild(element);
|
||||
};
|
||||
|
@ -178,7 +178,7 @@ export const getField = (field: FieldProp) => {
|
||||
break;
|
||||
case FieldTypes.TAG_SUGGESTION:
|
||||
fieldElement = (
|
||||
<TagSuggestion {...(props as unknown as TagSuggestionProps)} />
|
||||
<TagSuggestion {...(props as unknown as TagSuggestionProps)} newLook />
|
||||
);
|
||||
|
||||
break;
|
||||
|
@ -4457,6 +4457,11 @@
|
||||
jest-diff "^26.0.0"
|
||||
pretty-format "^26.0.0"
|
||||
|
||||
"@types/js-yaml@^4.0.9":
|
||||
version "4.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2"
|
||||
integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==
|
||||
|
||||
"@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
|
||||
version "7.0.9"
|
||||
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz"
|
||||
@ -10015,7 +10020,7 @@ js-yaml@^3.13.1:
|
||||
|
||||
js-yaml@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
||||
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
|
||||
dependencies:
|
||||
argparse "^2.0.1"
|
||||
|
Loading…
x
Reference in New Issue
Block a user