This is an automated email from the ASF dual-hosted git repository. ppawar pushed a commit to branch ATLAS-5123 in repository https://gitbox.apache.org/repos/asf/atlas.git
commit 2007ca9a0448c6f48cdbb1b87c95ce41959c567d Author: Prasad Pawar <[email protected]> AuthorDate: Wed Sep 24 14:20:33 2025 +0530 ATLAS-5123: ATLAS-5142: [REACT UI] When creating a business metadata attribute, the attribute is always an array even if 'Multivalues' checkbox was not selected. Custom Filters - Basic and Advanced search displays ellipsis menu incorrectly. --- dashboard/src/components/TreeNodeIcons.tsx | 6 ++- dashboard/src/index.scss | 2 +- dashboard/src/styles/administration.scss | 26 +++++++-- dashboard/src/utils/CommonViewFunction.ts | 26 +++++++-- .../BusinessMetadataAtrributeForm.tsx | 49 ++++++++++++----- .../BusinessMetadata/BusinessMetadataForm.tsx | 62 ++++++++++++++-------- .../views/BusinessMetadata/EnumCreateUpdate.tsx | 9 +++- .../BusinessMetadataAtrribute.tsx | 9 ++-- .../BusinessMetadataDetailsLayout.tsx | 29 ++++++---- .../PropertiesTab/BMAttributes.tsx | 48 +++++++++++------ .../PropertiesTab/BMAttributesFields.tsx | 6 ++- .../src/views/SideBar/SideBarTree/SideBarTree.tsx | 9 +++- 12 files changed, 204 insertions(+), 77 deletions(-) diff --git a/dashboard/src/components/TreeNodeIcons.tsx b/dashboard/src/components/TreeNodeIcons.tsx index e56624030..c1995bc8a 100644 --- a/dashboard/src/components/TreeNodeIcons.tsx +++ b/dashboard/src/components/TreeNodeIcons.tsx @@ -164,7 +164,8 @@ const TreeNodeIcons = (props: { }; return ( <> - {(node.types == "child" || node.types == undefined) && + {((node.types == "child") || + (node.types == undefined && treeName != "CustomFilters")) && ((treeName == "Classifications" && node.types == "child" ? !isEmpty(node.children) : isEmpty(node.children)) || @@ -192,7 +193,8 @@ const TreeNodeIcons = (props: { (node.types == "parent" && isEmpty(node.children)) || (node.types == "child" && !isEmpty(node.children) && - node.cGuid != undefined)) && ( + node.cGuid != undefined)) && + !(treeName == "CustomFilters" && node.types == "parent") && ( <IconButton onClick={(e) => { handleClickNodeMenu(e); diff --git a/dashboard/src/index.scss b/dashboard/src/index.scss index 9c7cfd0a5..efef5940e 100644 --- a/dashboard/src/index.scss +++ b/dashboard/src/index.scss @@ -33,8 +33,8 @@ @use "@styles/stats.scss" as *; @use "@styles/filters.scss" as *; @use "@styles/errorPage.scss" as *; -@use "@styles/errorPage.scss" as *; @use "@styles/customdatepPicker.scss" as *; +@use "@styles/administration.scss" as *; @font-face { font-family: "Source Sans 3"; diff --git a/dashboard/src/styles/administration.scss b/dashboard/src/styles/administration.scss index cce35f481..a01286701 100644 --- a/dashboard/src/styles/administration.scss +++ b/dashboard/src/styles/administration.scss @@ -32,11 +32,27 @@ padding-left: 20px; } -.audit-results-lsit-item { - display: list-item; - padding-left: 0; -} - .audit-results-entityid { display: block; } + +.enum-value-selector { + display: inline-grid !important; + grid-template-columns: 1fr !important; +} + +.bmattributes-key .MuiAutocomplete-inputRoot, +.enum-value-selector .MuiAutocomplete-inputRoot { + align-items: flex-start; + flex-wrap: wrap; +} + +.bmattributes-key .MuiAutocomplete-listbox, +.enum-value-selector .MuiAutocomplete-listbox { + max-height: 280px; +} + +.bm-empty-field { + margin-top: 0 !important; + margin-bottom: 0 !important; +} diff --git a/dashboard/src/utils/CommonViewFunction.ts b/dashboard/src/utils/CommonViewFunction.ts index e0f345b34..d8d83b9d2 100644 --- a/dashboard/src/utils/CommonViewFunction.ts +++ b/dashboard/src/utils/CommonViewFunction.ts @@ -279,9 +279,7 @@ export const generateObjectForSaveSearchApi = (options) => { if (val !== undefined && val !== null) { if (k === "attributes") { val = val.split(","); - } else if ( - ["tagFilters", "entityFilters", "relationshipFilters"].includes(k) - ) { + } else if (["tagFilters", "entityFilters", "relationshipFilters"].includes(k)) { val = attributeFilter.generateAPIObj(val); } else if (["includeDE", "excludeST", "excludeSC"].includes(k)) { val = val ? false : true; @@ -305,3 +303,25 @@ export const generateObjectForSaveSearchApi = (options) => { return obj; } }; + +export const getTypeName = ( + multiValueSelect: boolean, + enumType: string, + rest: any +) => { + if (multiValueSelect) { + switch (rest.typeName) { + case 'enumeration': + return `array<${enumType}>` + default: + return `array<${rest.typeName}>` + } + } else { + switch (rest.typeName) { + case 'enumeration': + return enumType + default: + return rest.typeName + } + } +} diff --git a/dashboard/src/views/BusinessMetadata/BusinessMetadataAtrributeForm.tsx b/dashboard/src/views/BusinessMetadata/BusinessMetadataAtrributeForm.tsx index cfb070e51..1f4bc2acf 100644 --- a/dashboard/src/views/BusinessMetadata/BusinessMetadataAtrributeForm.tsx +++ b/dashboard/src/views/BusinessMetadata/BusinessMetadataAtrributeForm.tsx @@ -154,13 +154,23 @@ const BusinessMetadataAttributeForm = ({ await updateEnum(data); toast.dismiss(toastId.current); toastId.current = toast.success( - `Enumeration ${enumType} updated - successfully` + `Enumeration ${enumType} updated successfully` ); } else { toast.dismiss(toastId.current); toastId.current = toast.success("No updated values"); } + fields?.forEach((fieldItem: any, idx: number) => { + const fieldEnumType = + attributeDefsWatch && + attributeDefsWatch(`attributeDefs.${idx}.enumType`); + if (fieldEnumType === enumType) { + attributeDefsSetValue( + `attributeDefs.${idx}.enumValues`, + elementValues + ); + } + }); handleCloseEnumModal(); reset({ enumType: "", enumValues: [] }); dispatchState(fetchEnumData()); @@ -170,6 +180,7 @@ const BusinessMetadataAttributeForm = ({ } }; const handleCloseEnumModal = () => { + reset({ enumType: "", enumValues: [] }); setEnumModal(false); }; @@ -258,7 +269,13 @@ const BusinessMetadataAttributeForm = ({ data-cy={`attributeDefs.${index}.name`} key={`attributeDefs.${index}.name`} defaultValue={field?.name} - render={({ field: { value, onChange } }) => ( + rules={{ + required: true + }} + render={({ + field: { value, onChange }, + fieldState: { error } + }) => ( <Grid container columnSpacing={{ xs: 1, sm: 2, md: 2 }} @@ -281,6 +298,7 @@ const BusinessMetadataAttributeForm = ({ }} onChange={onChange} margin="none" + error={!!error} fullWidth variant="outlined" size="small" @@ -297,6 +315,9 @@ const BusinessMetadataAttributeForm = ({ data-cy={`attributeDefs.${index}.typeName`} key={`attributeDefs.${index}.typeName`} defaultValue={field?.typeName} + rules={{ + required: true + }} render={({ field: { value, onChange } }) => ( <> <Grid @@ -547,7 +568,13 @@ const BusinessMetadataAttributeForm = ({ data-cy={`attributeDefs.${index}.enumType`} key={`attributeDefs.${index}.enumType`} defaultValue={field?.enumType} - render={({ field: { value, onChange } }) => ( + rules={{ + required: true + }} + render={({ + field: { value, onChange }, + fieldState: { error } + }) => ( <> <Grid container @@ -571,7 +598,7 @@ const BusinessMetadataAttributeForm = ({ }} size="small" id="search-by-type" - value={value || []} + value={value || null} clearIcon={null} onChange={(_event, newValue) => { onChange(newValue); @@ -596,9 +623,6 @@ const BusinessMetadataAttributeForm = ({ } }} filterSelectedOptions - isOptionEqualToValue={(option, value) => - option === value - } options={ !isEmpty(enumTypes) ? enumTypes.map((option: any) => option) @@ -608,11 +632,11 @@ const BusinessMetadataAttributeForm = ({ renderInput={(params) => ( <TextField {...params} + error={!!error} fullWidth disabled={ isEmpty(editbmAttribute) ? false : true } - // label="Select type" InputLabelProps={{ style: { top: "unset", @@ -663,9 +687,6 @@ const BusinessMetadataAttributeForm = ({ data-cy={`attributeDefs.${index}.enumValues`} key={`attributeDefs.${index}.enumValues` as const} defaultValue={field?.enumValues} - rules={{ - required: true - }} render={({ field }) => { return ( <> @@ -681,11 +702,15 @@ const BusinessMetadataAttributeForm = ({ <Grid item md={7}> <Autocomplete size="small" + className="enum-value-selector" readOnly disableClearable={true} multiple={true} value={watched?.[index]?.enumValues || []} getOptionLabel={(option) => option.value} + isOptionEqualToValue={(option, value) => + option.value === value.value + } data-cy="enumValueSelector" options={ !isEmpty(enumTypeOptions) diff --git a/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx b/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx index f6e4d1e91..2f6af49dc 100644 --- a/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx +++ b/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx @@ -43,6 +43,7 @@ import { setEditBMAttribute } from "@redux/slice/createBMSlice"; import { cloneDeep } from "@utils/Helper"; import { fetchBusinessMetaData } from "@redux/slice/typeDefSlices/typedefBusinessMetadataSlice"; import { defaultType } from "@utils/Enum"; +import { getTypeName } from "@utils/CommonViewFunction"; const BusinessMetaDataForm = ({ setForm, @@ -77,7 +78,7 @@ const BusinessMetaDataForm = ({ : "enumeration"; let selectedEnumObj = !isEmpty(enumDefs) ? enumDefs.find((obj: { name: any }) => { - return obj.name == typeName; + return obj.name == (str.indexOf("<") != -1 ? extracted : str); }) : {}; let selectedEnumValues = !isEmpty(selectedEnumObj) @@ -108,7 +109,9 @@ const BusinessMetaDataForm = ({ : defaultType.includes(typeName) ? typeName : "enumeration", - ...(currentTypeName == "enumeration" && { enumType: typeName }), + ...(currentTypeName == "enumeration" && { + enumType: str.indexOf("<") != -1 ? extracted : str + }), ...(currentTypeName == "enumeration" && { enumValues: enumTypeOptions }), @@ -152,7 +155,10 @@ const BusinessMetaDataForm = ({ const { fields, append, remove } = useFieldArray({ control, - name: "attributeDefs" + name: "attributeDefs", + rules: { + required: true + } }); useEffect(() => { @@ -164,9 +170,11 @@ const BusinessMetaDataForm = ({ : defaultType.includes(typeName) ? typeName : "enumeration"; - let selectedEnumObj = enumDefs.find((obj: { name: any }) => { - return obj.name == typeName; - }); + let selectedEnumObj = !isEmpty(enumDefs) + ? enumDefs.find((obj: { name: any }) => { + return obj.name == (str.indexOf("<") != -1 ? extracted : str); + }) + : {}; let selectedEnumValues = !isEmpty(selectedEnumObj) ? selectedEnumObj?.elementDefs : []; @@ -189,7 +197,9 @@ const BusinessMetaDataForm = ({ : [] }, typeName: currentTypeName, - ...(currentTypeName == "enumeration" && { enumType: typeName }), + ...(currentTypeName == "enumeration" && { + enumType: str.indexOf("<") != -1 ? extracted : str + }), ...(currentTypeName == "enumeration" && { enumValues: enumTypeOptions }), @@ -233,6 +243,7 @@ const BusinessMetaDataForm = ({ const onSubmit = async (values: any) => { let formData = { ...values }; + let bmData = cloneDeep(bmAttribute); const { name, description, attributeDefs } = formData; @@ -243,24 +254,33 @@ const BusinessMetaDataForm = ({ let attributeDefsData = !isEmpty(attributeDefs) ? [...attributeDefs] : []; let attributes = !isEmpty(attributeDefsData) - ? attributeDefsData.map((item) => ({ - ...item, - ...{ + ? attributeDefsData.map((item) => { + const { multiValueSelect, enumType, enumValues, ...rest } = item; + + const baseObj = { + ...rest, options: { applicableEntityTypes: JSON.stringify( - item.options.applicableEntityTypes + rest.options.applicableEntityTypes ), - maxStrLength: item.options.maxStrLength + maxStrLength: rest.options.maxStrLength }, - typeName: item.multiValueSelect - ? item.typeName == "enumeration" - ? item.enumType - : `array<${item.typeName}>` - : item.typeName == "enumeration" - ? item.enumType - : `array<${item.typeName}>` - } - })) + ...(multiValueSelect && { + multiValueSelect: true, + multiValued: true + }), + ...(enumType && { + enumValues: !isEmpty(enumValues) + ? enumValues.map((enums: { value: any }) => { + return enums.value; + }) + : [] + }), + typeName: getTypeName(multiValueSelect, enumType, rest) + }; + + return baseObj; + }) : []; let data = { diff --git a/dashboard/src/views/BusinessMetadata/EnumCreateUpdate.tsx b/dashboard/src/views/BusinessMetadata/EnumCreateUpdate.tsx index be238d730..bd4eed7df 100644 --- a/dashboard/src/views/BusinessMetadata/EnumCreateUpdate.tsx +++ b/dashboard/src/views/BusinessMetadata/EnumCreateUpdate.tsx @@ -131,7 +131,12 @@ const EnumCreateUpdate = ({ sx={{ flex: "1" }} size="small" value={value} - onChange={(_event, newValue) => { + onChange={(_event, newValue, reason) => { + if (reason == "clear") { + onChange(""); + setValue("enumValues", []); + return; + } if (!isEmpty(newValue.label)) { onChange(newValue.value); } else { @@ -170,6 +175,7 @@ const EnumCreateUpdate = ({ return filtered; }} + clearIcon={null} filterSelectedOptions onBlur={onBlur} options={ @@ -261,7 +267,6 @@ const EnumCreateUpdate = ({ : [] } onBlur={onBlur} - className="advanced-search-autocomplete" renderInput={(params) => ( <TextField {...params} diff --git a/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataAtrribute.tsx b/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataAtrribute.tsx index 04fb2ac00..54f1e88ff 100644 --- a/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataAtrribute.tsx +++ b/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataAtrribute.tsx @@ -56,7 +56,8 @@ const BusinessMetadataAtrribute = ({ componentProps, row }: any) => { <span>N/A</span> ), header: "Type Name", - enableSorting: true + enableSorting: true, + width: "30%" }, { accessorKey: "searchWeight", @@ -176,7 +177,9 @@ const BusinessMetadataAtrribute = ({ componentProps, row }: any) => { : "enumeration"; let selectedEnumObj = !isEmpty(enumDefs) ? enumDefs.find((obj: { name: any }) => { - return obj.name == typeName; + return ( + obj.name == (str.indexOf("<") != -1 ? extracted : str) + ); }) : {}; let selectedEnumValues = !isEmpty(selectedEnumObj) @@ -208,7 +211,7 @@ const BusinessMetadataAtrribute = ({ componentProps, row }: any) => { ? typeName : "enumeration", ...(currentTypeName == "enumeration" && { - enumType: typeName + enumType: str.indexOf("<") != -1 ? extracted : str }), ...(currentTypeName == "enumeration" && { enumValues: enumTypeOptions diff --git a/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataDetailsLayout.tsx b/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataDetailsLayout.tsx index 5b07e1e70..1638565ff 100644 --- a/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataDetailsLayout.tsx +++ b/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataDetailsLayout.tsx @@ -45,6 +45,7 @@ import { createEditBusinessMetadata } from "@api/apiMethods/typeDefApiMethods"; import { cloneDeep } from "@utils/Helper"; import { defaultAttrObj, defaultType } from "@utils/Enum"; import { fetchBusinessMetaData } from "@redux/slice/typeDefSlices/typedefBusinessMetadataSlice"; +import { getTypeName } from "@utils/CommonViewFunction"; const BusinessMetadataDetailsLayout = () => { const { bmguid } = useParams(); @@ -97,9 +98,10 @@ const BusinessMetadataDetailsLayout = () => { : "enumeration"; let selectedEnumObj = !isEmpty(enumDefs) ? enumDefs.find((obj: { name: any }) => { - return obj.name == typeName; + return obj.name == (str.indexOf("<") != -1 ? extracted : str); }) : {}; + let selectedEnumValues = !isEmpty(selectedEnumObj) ? selectedEnumObj?.elementDefs : []; @@ -129,7 +131,9 @@ const BusinessMetadataDetailsLayout = () => { : defaultType.includes(typeName) ? typeName : "enumeration", - ...(currentTypeName == "enumeration" && { enumType: typeName }), + ...(currentTypeName == "enumeration" && { + enumType: str.indexOf("<") != -1 ? extracted : str + }), ...(currentTypeName == "enumeration" && { enumValues: enumTypeOptions }), @@ -175,7 +179,7 @@ const BusinessMetadataDetailsLayout = () => { let attributeDefsData = [...formAttributes]; let attributes = attributeDefsData.map((item) => { - const { multiValueSelect, ...rest } = item; + const { multiValueSelect, enumValues, enumType, ...rest } = item; return { ...rest, @@ -186,13 +190,18 @@ const BusinessMetadataDetailsLayout = () => { ), maxStrLength: rest.options.maxStrLength }, - typeName: rest.multiValueSelect - ? rest.typeName == "enumeration" - ? rest.enumType - : `array<${rest.typeName}>` - : rest.typeName == "enumeration" - ? rest.enumType - : `array<${rest.typeName}>` + ...(multiValueSelect && { + multiValueSelect: true, + multiValued: true + }), + ...(!isEmpty(enumType) && { + enumValues: !isEmpty(enumType) + ? enumValues.map((enums: { value: any }) => { + return enums.value; + }) + : [] + }), + typeName: getTypeName(multiValueSelect, enumType, rest) } }; }); diff --git a/dashboard/src/views/DetailPage/EntityDetailTabs/PropertiesTab/BMAttributes.tsx b/dashboard/src/views/DetailPage/EntityDetailTabs/PropertiesTab/BMAttributes.tsx index 0697efdb0..99ccd93e7 100644 --- a/dashboard/src/views/DetailPage/EntityDetailTabs/PropertiesTab/BMAttributes.tsx +++ b/dashboard/src/views/DetailPage/EntityDetailTabs/PropertiesTab/BMAttributes.tsx @@ -572,12 +572,20 @@ const BMAttributes = ({ loading, bmAttributes, entity }: any) => { </CustomButton> </LightTooltip> {fields.map((field, index) => { + const keySelected = !isEmpty( + bmAttributesValues?.[index]?.key + ); return ( <Stack gap="0.5rem" direction="row" key={field?.id} - alignItems="center" + alignItems={keySelected ? "flex-start" : "center"} + sx={{ + '& .MuiAutocomplete-root': { + alignSelf: keySelected ? 'stretch' : 'center' + } + }} > <Controller control={control} @@ -596,7 +604,12 @@ const BMAttributes = ({ loading, bmAttributes, entity }: any) => { onChange={(_, selectedOption) => { onChange(selectedOption); }} - sx={{ flexBasis: "35%" }} + sx={{ + flexBasis: "35%", + '& .MuiOutlinedInput-root': { + height: 36 + } + }} getOptionLabel={(option) => option.label} groupBy={(option) => option.group} noOptionsText="No results found" @@ -630,7 +643,11 @@ const BMAttributes = ({ loading, bmAttributes, entity }: any) => { </> )} /> - <span style={{ margin: "12px 0" }}>:</span> + <span + style={{ margin: 0, alignSelf: "center" }} + > + : + </span> {isEmpty(bmAttributesValues?.[index]?.key) ? ( <Controller control={control} @@ -644,29 +661,30 @@ const BMAttributes = ({ loading, bmAttributes, entity }: any) => { fieldState: { error } }) => ( <> - <div style={{ flex: "1" }}> + <div + style={{ + flex: "1", + display: "flex", + alignItems: "center" + }} + > <TextField - margin="normal" + margin="none" error={!!error} - className="form-textfield" + className="form-textfield bm-empty-field" onChange={onChange} value={value} variant="outlined" size="small" disabled sx={{ + '& .MuiInputBase-root': { + height: 36 + }, "& .MuiInputBase-root.Mui-disabled": { backgroundColor: "#eee", cursor: "not-allowed" - }, - "& .MuiOutlinedInput-root.Mui-error": { - "& fieldset": { - borderColor: "red" - } - }, - marginTop: "8px !important", - marginBottom: "8px !important", - width: "100%" + } }} type={"string"} /> diff --git a/dashboard/src/views/DetailPage/EntityDetailTabs/PropertiesTab/BMAttributesFields.tsx b/dashboard/src/views/DetailPage/EntityDetailTabs/PropertiesTab/BMAttributesFields.tsx index ad5852095..2cd05d688 100644 --- a/dashboard/src/views/DetailPage/EntityDetailTabs/PropertiesTab/BMAttributesFields.tsx +++ b/dashboard/src/views/DetailPage/EntityDetailTabs/PropertiesTab/BMAttributesFields.tsx @@ -249,8 +249,10 @@ const BMAttributesFields = ({ obj, control, index }: any) => { paddingTop: "4px", paddingBottom: "4px", paddingLeft: "6px", - height: "34px", - gap: "4px" + gap: "4px", + '& .MuiAutocomplete-inputRoot': { + flexWrap: 'wrap' + } }} filterOptions={(options, params) => { const filtered = filter(options, params); diff --git a/dashboard/src/views/SideBar/SideBarTree/SideBarTree.tsx b/dashboard/src/views/SideBar/SideBarTree/SideBarTree.tsx index 97ec42c1d..cafb261d4 100644 --- a/dashboard/src/views/SideBar/SideBarTree/SideBarTree.tsx +++ b/dashboard/src/views/SideBar/SideBarTree/SideBarTree.tsx @@ -339,6 +339,10 @@ const BarTreeView: FC<{ }); }, [treeData, searchTerm]); + const displayTreeName = useMemo(() => { + return treeName === "CustomFilters" ? "Custom Filters" : treeName + }, [treeName]); + const highlightText = useMemo(() => { return (text: string) => { if (!searchTerm) return text; @@ -557,6 +561,9 @@ const BarTreeView: FC<{ }; const shouldSetSearchParams = (node: TreeNode, treeName: string) => { + if (treeName === "CustomFilters" && node.types === "parent") { + return false; + } return ( node.children === undefined || isEmpty(node.children) || @@ -906,7 +913,7 @@ const BarTreeView: FC<{ className="tree-item-parent-label" > <Stack flexGrow={1}> - <span>{treeName}</span> + <span>{displayTreeName}</span> </Stack> <Stack direction="row" alignItems="center" gap="0.375rem"> <LightTooltip title="Refresh">
