diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-table.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-table.svg index d737e19f8a6..908aa754865 100644 --- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-table.svg +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/ic-format-table.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormV1.less b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormV1.less new file mode 100644 index 00000000000..693fe075847 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormV1.less @@ -0,0 +1,33 @@ +/* + * 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) '../../../../styles/variables.less'; + +.test-case-form-v1 { + .test-level-section { + margin-bottom: @size-lg; + + .form-section-title { + color: @grey-800; + font-size: @size-md; + font-weight: @font-medium; + margin-bottom: @size-mlg; + line-height: 1.4; + } + + .ant-form-item-explain-error { + margin-top: @size-xs; + color: @error-color; + font-size: @font-size-base; + } + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormV1.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormV1.tsx new file mode 100644 index 00000000000..9c636def69a --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormV1.tsx @@ -0,0 +1,113 @@ +/* + * 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 { Drawer, DrawerProps, Form } from 'antd'; +import { useForm } from 'antd/lib/form/Form'; +import classNames from 'classnames'; +import { FC, useEffect } from 'react'; +import { ReactComponent as ColumnIcon } from '../../../../assets/svg/ic-column.svg'; +import { ReactComponent as TableIcon } from '../../../../assets/svg/ic-format-table.svg'; +import SelectionCardGroup from '../../../common/SelectionCardGroup/SelectionCardGroup'; +import { SelectionOption } from '../../../common/SelectionCardGroup/SelectionCardGroup.interface'; +import './TestCaseFormV1.less'; + +export interface TestCaseFormV1Props { + isDrawer?: boolean; + drawerProps?: DrawerProps; + className?: string; + onFormSubmit?: (values: FormValues) => void; + initialValues?: Partial; +} + +interface FormValues { + testLevel: TestLevel; +} + +export enum TestLevel { + TABLE = 'table', + COLUMN = 'column', +} + +const TEST_LEVEL_OPTIONS: SelectionOption[] = [ + { + value: 'table', + label: 'Table Level', + description: 'Test applied on table', + icon: , + }, + { + value: 'column', + label: 'Column Level', + description: 'Test applied on column', + icon: , + }, +]; + +const TestCaseFormV1: FC = ({ + className, + drawerProps, + initialValues, + isDrawer = false, + onFormSubmit, +}) => { + const [form] = useForm(); + + const handleSubmit = (values: FormValues) => { + onFormSubmit?.(values); + }; + + useEffect(() => { + form.setFieldsValue({ testLevel: TestLevel.TABLE }); + }, [form]); + + const formContent = ( +
+
+ + + +
+
+ ); + + if (isDrawer) { + return ( + + {formContent} + + ); + } + + return formContent; +}; + +export default TestCaseFormV1; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/SelectionCardGroup/SelectionCardGroup.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/common/SelectionCardGroup/SelectionCardGroup.interface.ts new file mode 100644 index 00000000000..99f407797d7 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/SelectionCardGroup/SelectionCardGroup.interface.ts @@ -0,0 +1,33 @@ +/* + * 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 { ReactNode } from 'react'; + +export interface SelectionOption { + value: string; + label: string; + description: string; + icon: ReactNode; +} + +export interface SelectionCardGroupProps { + options: SelectionOption[]; + value?: string; + onChange?: (value: string) => void; + className?: string; +} + +export interface SelectionCardProps { + option: SelectionOption; + isSelected: boolean; + onClick: () => void; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/SelectionCardGroup/SelectionCardGroup.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/SelectionCardGroup/SelectionCardGroup.tsx new file mode 100644 index 00000000000..9722822fb20 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/SelectionCardGroup/SelectionCardGroup.tsx @@ -0,0 +1,76 @@ +/* + * 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 { CheckOutlined } from '@ant-design/icons'; +import { Card } from 'antd'; +import classNames from 'classnames'; +import { FC } from 'react'; +import './selection-card-group.less'; +import { + SelectionCardGroupProps, + SelectionCardProps, +} from './SelectionCardGroup.interface'; + +const SelectionCard: FC = ({ + option, + isSelected, + onClick, +}: SelectionCardProps) => ( + +
+
+ {option.icon} +
+
{option.label}
+
{option.description}
+
+
+ {isSelected ? ( +
+ +
+ ) : ( +
+ )} +
+ +); + +const SelectionCardGroup: FC = ({ + options, + value, + onChange, + className, +}: SelectionCardGroupProps) => { + const handleOptionSelect = (selectedValue: string) => { + onChange?.(selectedValue); + }; + + return ( +
+ {options.map((option) => ( + handleOptionSelect(option.value)} + /> + ))} +
+ ); +}; + +export default SelectionCardGroup; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/SelectionCardGroup/selection-card-group.less b/openmetadata-ui/src/main/resources/ui/src/components/common/SelectionCardGroup/selection-card-group.less new file mode 100644 index 00000000000..d154094bdd7 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/SelectionCardGroup/selection-card-group.less @@ -0,0 +1,132 @@ +/* + * 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) '../../../styles/variables.less'; + +.selection-card-group { + display: flex; + gap: @size-mlg; + width: 100%; + + .selection-card { + flex: 1; + border: 1px solid @grey-200; + border-radius: @border-radius-xs; + cursor: pointer; + transition: all 0.3s ease; + background: @grey-50; + + .ant-card-body { + padding: @size-mlg @size-lg; + min-height: 90px; + + .selection-content { + display: flex; + justify-content: space-between; + width: 100%; + + .selection-icon { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 50%; + background-color: @grey-100; + transition: all 0.3s ease; + flex-shrink: 0; + + svg { + width: @size-mlg; + height: @size-mlg; + color: @grey-500; + } + } + + .selection-header { + flex: 1; + } + + .selection-title { + color: @grey-800; + font-size: @size-md; + font-weight: @font-medium; + line-height: 1.4; + margin-bottom: @size-xxs; + } + + .selection-description { + color: @grey-400; + font-size: @font-size-base; + line-height: 1.4; + } + + .custom-radio { + margin-left: @size-md; + flex-shrink: 0; + width: 22px; + height: 22px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; + + &.unchecked { + border: 2px solid @grey-300; + background-color: transparent; + } + + &.checked { + background-color: @primary-color; + border: none; + color: @white; + + .anticon { + font-size: 14px; + font-weight: @font-bold; + } + } + } + } + } + + // Selected state styles + &.selected, + &:hover { + border-color: @primary-color; + background-color: @blue-22; + + .selection-content { + .selection-icon { + background-color: @primary-1; + + svg { + color: @primary-color; + } + } + + .selection-title { + color: @primary-color; + } + + .selection-description { + color: @blue-9; + } + + .custom-radio.unchecked { + border-color: @primary-color; + } + } + } + } +} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DataQuality/DataQualityPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DataQuality/DataQualityPage.tsx index c4971eb214a..825f7790b90 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DataQuality/DataQualityPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DataQuality/DataQualityPage.tsx @@ -18,6 +18,7 @@ import { useTranslation } from 'react-i18next'; import { Link, useNavigate } from 'react-router-dom'; import ManageButton from '../../components/common/EntityPageInfos/ManageButton/ManageButton'; import TabsLabel from '../../components/common/TabsLabel/TabsLabel.component'; +import TestCaseFormV1 from '../../components/DataQuality/AddDataQualityTest/components/TestCaseFormV1'; import PageHeader from '../../components/PageHeader/PageHeader.component'; import { ROUTES } from '../../constants/constants'; import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider'; @@ -119,6 +120,15 @@ const DataQualityPage = () => { /> + ); };