This is an automated email from the ASF dual-hosted git repository.

ppawar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/atlas.git


The following commit(s) were added to refs/heads/master by this push:
     new 30244d710 ATLAS-5169: Atlas UI creates multi-valued Business Metadata 
with SINGLE cardinality (#492)
30244d710 is described below

commit 30244d7107302db513dc2d54c1c19345dc9e340b
Author: Prasad Pawar <[email protected]>
AuthorDate: Fri Jan 2 10:06:39 2026 +0530

    ATLAS-5169: Atlas UI creates multi-valued Business Metadata with SINGLE 
cardinality (#492)
---
 .../BusinessMetadataAtrributeForm.tsx              | 113 ++++++++++++++-------
 .../BusinessMetadata/BusinessMetadataForm.tsx      |  23 ++++-
 .../BusinessMetadataAtrribute.tsx                  |  16 ++-
 .../BusinessMetadataDetailsLayout.tsx              |  11 +-
 dashboardv2/public/css/scss/business-metadata.scss |   8 ++
 .../BusinessMetadataAttributeItemView_tmpl.html    |  12 ++-
 .../BusinessMetadataAttrTableLayoutView.js         |  30 ++++++
 .../BusinessMetadataAttributeItemView.js           |  79 ++++++++++++++
 .../BusinessMetadataTableLayoutView.js             |  18 +++-
 .../CreateBusinessMetadataLayoutView.js            |  28 ++++-
 dashboardv3/public/css/scss/business-metadata.scss |   8 ++
 .../BusinessMetadataAttributeItemView_tmpl.html    |  12 ++-
 .../BusinessMetadataAttrTableLayoutView.js         |  30 ++++++
 .../BusinessMetadataAttributeItemView.js           |  79 ++++++++++++++
 .../BusinessMetadataTableLayoutView.js             |  18 +++-
 .../CreateBusinessMetadataLayoutView.js            |  28 ++++-
 16 files changed, 460 insertions(+), 53 deletions(-)

diff --git 
a/dashboard/src/views/BusinessMetadata/BusinessMetadataAtrributeForm.tsx 
b/dashboard/src/views/BusinessMetadata/BusinessMetadataAtrributeForm.tsx
index 1f4bc2acf..883015078 100644
--- a/dashboard/src/views/BusinessMetadata/BusinessMetadataAtrributeForm.tsx
+++ b/dashboard/src/views/BusinessMetadata/BusinessMetadataAtrributeForm.tsx
@@ -37,7 +37,9 @@ import {
   tooltipClasses,
   TooltipProps,
   styled,
-  FilterOptionsState
+  FilterOptionsState,
+  ToggleButton,
+  ToggleButtonGroup
 } from "@mui/material";
 import { customSortBy, isEmpty, serverError } from "@utils/Utils";
 import { Controller, useForm } from "react-hook-form";
@@ -463,41 +465,82 @@ const BusinessMetadataAttributeForm = ({
               watched?.[index] &&
               watched?.[index]?.multiValueSelect) ||
               isEmpty(editbmAttribute)) && (
-              <Controller
-                control={control}
-                name={`attributeDefs.${index}.multiValueSelect` as const}
-                key={`attributeDefs.${index}.multiValueSelect`}
-                data-cy={`attributeDefs.${index}.multiValueSelect`}
-                defaultValue={field?.multiValueSelect}
-                render={({ field: { value, onChange } }) => (
-                  <>
-                    <Grid
-                      container
-                      columnSpacing={{ xs: 1, sm: 2, md: 2 }}
-                      marginBottom="1rem"
-                      alignItems="center"
-                    >
-                      <Grid item md={3} textAlign="right">
-                        <InputLabel>Enable Multivalues</InputLabel>
-                      </Grid>
-                      <Grid item md={7}>
-                        {" "}
-                        <FormControlLabel
-                          control={
-                            <Checkbox
-                              disabled={isEmpty(editbmAttribute) ? false : 
true}
-                              size="small"
-                              checked={value}
-                              onChange={onChange}
+              <>
+                <Controller
+                  control={control}
+                  name={`attributeDefs.${index}.multiValueSelect` as const}
+                  key={`attributeDefs.${index}.multiValueSelect`}
+                  data-cy={`attributeDefs.${index}.multiValueSelect`}
+                  defaultValue={field?.multiValueSelect}
+                  render={({ field: { value, onChange } }) => (
+                    <>
+                      <Grid
+                        container
+                        columnSpacing={{ xs: 1, sm: 2, md: 2 }}
+                        marginBottom="1rem"
+                        alignItems="center"
+                      >
+                        <Grid item md={3} textAlign="right">
+                          <InputLabel>Enable Multivalues</InputLabel>
+                        </Grid>
+                        <Grid item md={7}>
+                          <Stack direction="row" spacing={2} 
alignItems="center">
+                            <FormControlLabel
+                              control={
+                                <Checkbox
+                                  disabled={isEmpty(editbmAttribute) ? false : 
true}
+                                  size="small"
+                                  checked={value}
+                                  onChange={(e) => {
+                                    onChange(e.target.checked);
+                                    // Reset cardinality toggle when 
multivalues is unchecked
+                                    if (!e.target.checked) {
+                                      attributeDefsSetValue(
+                                        
`attributeDefs.${index}.cardinalityToggle`,
+                                        "SET"
+                                      );
+                                    }
+                                  }}
+                                />
+                              }
+                              label={undefined}
                             />
-                          }
-                          label={undefined}
-                        />
-                      </Grid>{" "}
-                    </Grid>
-                  </>
-                )}
-              />
+                            {value && (
+                              <Controller
+                                control={control}
+                                
name={`attributeDefs.${index}.cardinalityToggle` as const}
+                                
key={`attributeDefs.${index}.cardinalityToggle`}
+                                defaultValue={field?.cardinalityToggle || 
"SET"}
+                                render={({ field: { value: toggleValue, 
onChange: toggleOnChange } }) => (
+                                  <ToggleButtonGroup
+                                    size="small"
+                                    value={toggleValue || "SET"}
+                                    exclusive
+                                    disabled={!isEmpty(editbmAttribute)}
+                                    onChange={(e, newValue) => {
+                                      if (newValue !== null) {
+                                        toggleOnChange(newValue);
+                                      }
+                                    }}
+                                    aria-label="cardinality toggle"
+                                  >
+                                    <ToggleButton value="SET" aria-label="SET">
+                                      SET
+                                    </ToggleButton>
+                                    <ToggleButton value="LIST" 
aria-label="LIST">
+                                      LIST
+                                    </ToggleButton>
+                                  </ToggleButtonGroup>
+                                )}
+                              />
+                            )}
+                          </Stack>
+                        </Grid>
+                      </Grid>
+                    </>
+                  )}
+                />
+              </>
             )}
             {watched?.[index] && watched?.[index]?.typeName == "string" && (
               <Controller
diff --git a/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx 
b/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx
index 5fcbb22ed..4a3ed8a18 100644
--- a/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx
+++ b/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx
@@ -115,7 +115,9 @@ const BusinessMetaDataForm = ({
       ...(currentTypeName == "enumeration" && {
         enumValues: enumTypeOptions
       }),
-      multiValueSelect: str.indexOf("<") != -1 ? true : false
+      multiValueSelect: str.indexOf("<") != -1 ? true : false,
+      // Set cardinalityToggle based on existing cardinality (SET or LIST), 
default to SET
+      cardinalityToggle: editbmAttribute?.cardinality === "LIST" ? "LIST" : 
"SET"
     }
   ];
   const dispatchState = useAppDispatch();
@@ -142,6 +144,7 @@ const BusinessMetaDataForm = ({
                 },
                 isOptional: true,
                 cardinality: "SINGLE",
+                cardinalityToggle: "SET", // Default toggle value when 
multivalues is enabled
                 valuesMinCount: 0,
                 valuesMaxCount: 1,
                 isUnique: false,
@@ -157,7 +160,7 @@ const BusinessMetaDataForm = ({
     control,
     name: "attributeDefs",
     rules: {
-      required: true
+      required: false // Allow creating BM without attributes
     }
   });
 
@@ -203,7 +206,9 @@ const BusinessMetaDataForm = ({
         ...(currentTypeName == "enumeration" && {
           enumValues: enumTypeOptions
         }),
-        multiValueSelect: str.indexOf("<") != -1 ? true : false
+        multiValueSelect: str.indexOf("<") != -1 ? true : false,
+        // Set cardinalityToggle based on existing cardinality (SET or LIST), 
default to SET
+        cardinalityToggle: editbmAttribute?.cardinality === "LIST" ? "LIST" : 
"SET"
       }
     ];
     if (!isEmpty(editbmAttribute)) {
@@ -255,10 +260,19 @@ const BusinessMetaDataForm = ({
 
     let attributes = !isEmpty(attributeDefsData)
       ? attributeDefsData.map((item) => {
-          const { multiValueSelect, enumType, enumValues, ...rest } = item;
+          const { multiValueSelect, enumType, enumValues, cardinality, 
cardinalityToggle, ...rest } = item;
+
+          // Determine cardinality based on multiValueSelect and cardinality 
toggle
+          let finalCardinality = "SINGLE";
+          if (multiValueSelect) {
+            // If multivalues is enabled, use the cardinalityToggle (SET or 
LIST)
+            // Default to SET if not specified
+            finalCardinality = cardinalityToggle === "LIST" ? "LIST" : 
(cardinality === "LIST" ? "LIST" : "SET");
+          }
 
           const baseObj = {
             ...rest,
+            cardinality: finalCardinality,
             options: {
               applicableEntityTypes: JSON.stringify(
                 rest.options.applicableEntityTypes
@@ -508,6 +522,7 @@ const BusinessMetaDataForm = ({
                               },
                               isOptional: true,
                               cardinality: "SINGLE",
+                              cardinalityToggle: "SET", // Default toggle 
value when multivalues is enabled
                               valuesMinCount: 0,
                               valuesMaxCount: 1,
                               isUnique: false,
diff --git 
a/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataAtrribute.tsx
 
b/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataAtrribute.tsx
index 8ed291d90..af9c1a355 100644
--- 
a/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataAtrribute.tsx
+++ 
b/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataAtrribute.tsx
@@ -84,6 +84,18 @@ const BusinessMetadataAtrribute = ({ componentProps, row }: 
any) => {
         header: "Enable Multivalues",
         enableSorting: false
       },
+      {
+        accessorKey: "cardinality",
+        cell: (info: any) =>
+          !isEmpty(info.getValue()) ? (
+            <Typography>{info.getValue()}</Typography>
+          ) : (
+            <span>N/A</span>
+          ),
+        header: "Cardinality",
+        enableSorting: true,
+        show: true
+      },
       {
         accessorKey: "maxStrLength",
         cell: (info: any) => {
@@ -217,7 +229,9 @@ const BusinessMetadataAtrribute = ({ componentProps, row }: 
any) => {
                     ...(currentTypeName == "enumeration" && {
                       enumValues: enumTypeOptions
                     }),
-                    multiValueSelect: str.indexOf("<") != -1 ? true : false
+                    multiValueSelect: str.indexOf("<") != -1 ? true : false,
+                    // Set cardinalityToggle based on existing cardinality 
(SET or LIST), default to SET
+                    cardinalityToggle: original?.cardinality === "LIST" ? 
"LIST" : "SET"
                   }
                 ];
                 setForm(true);
diff --git 
a/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataDetailsLayout.tsx
 
b/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataDetailsLayout.tsx
index 1638565ff..b3d419246 100644
--- 
a/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataDetailsLayout.tsx
+++ 
b/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataDetailsLayout.tsx
@@ -179,10 +179,19 @@ const BusinessMetadataDetailsLayout = () => {
     let attributeDefsData = [...formAttributes];
 
     let attributes = attributeDefsData.map((item) => {
-      const { multiValueSelect, enumValues, enumType, ...rest } = item;
+      const { multiValueSelect, enumValues, enumType, cardinality, 
cardinalityToggle, ...rest } = item;
+
+      // Determine cardinality based on multiValueSelect and cardinality toggle
+      let finalCardinality = "SINGLE";
+      if (multiValueSelect) {
+        // If multivalues is enabled, use the cardinalityToggle (SET or LIST)
+        // Default to SET if not specified
+        finalCardinality = cardinalityToggle === "LIST" ? "LIST" : 
(cardinality === "LIST" ? "LIST" : "SET");
+      }
 
       return {
         ...rest,
+        cardinality: finalCardinality,
         ...{
           options: {
             applicableEntityTypes: JSON.stringify(
diff --git a/dashboardv2/public/css/scss/business-metadata.scss 
b/dashboardv2/public/css/scss/business-metadata.scss
index 5bcbb5df9..2cadd14bf 100644
--- a/dashboardv2/public/css/scss/business-metadata.scss
+++ b/dashboardv2/public/css/scss/business-metadata.scss
@@ -240,4 +240,12 @@
     .business-metadata-detail-attr-key {
         width: 30%;
     }
+}
+
+// Disabled state for cardinality toggle buttons
+.cardinality-btn.disabled,
+.cardinality-btn[disabled] {
+    opacity: 0.6;
+    cursor: not-allowed;
+    pointer-events: none;
 }
\ No newline at end of file
diff --git 
a/dashboardv2/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
 
b/dashboardv2/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
index aedb6dabe..8ffb689b4 100644
--- 
a/dashboardv2/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
+++ 
b/dashboardv2/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
@@ -68,7 +68,17 @@
     <div class="form-group" data-id="multiValueSelect">
         <label class="control-label col-sm-3" for="multiValSelect">Enable 
Multivalues</label>
         <div class="col-sm-8">
-            <input type="checkbox" class="form-check-input multi-value-select" 
data-id="multiValueSelectStatus">
+            <div class="row">
+                <div class="col-sm-6">
+                    <input type="checkbox" class="form-check-input 
multi-value-select" data-id="multiValueSelectStatus">
+                </div>
+                <div class="col-sm-6" data-id="cardinalityToggleContainer" 
style="display: none;">
+                    <div class="btn-group" role="group" 
data-id="cardinalityToggle">
+                        <button type="button" class="btn btn-sm btn-default 
cardinality-btn" data-value="SET" data-id="cardinalitySET">SET</button>
+                        <button type="button" class="btn btn-sm btn-default 
cardinality-btn active" data-value="LIST" 
data-id="cardinalityLIST">LIST</button>
+                    </div>
+                </div>
+            </div>
         </div>
     </div>
     <div class="form-group enumtype-container" 
data-id="enumTypeSelectorContainer">
diff --git 
a/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
 
b/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
index 37058bbe2..37e6e7a83 100644
--- 
a/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
+++ 
b/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
@@ -108,6 +108,15 @@ define(['require',
                                 attrDetails.typeName = 
attrObj.typeName.replace("array<", "").replace(">", "");
                                 attrDetails.multiValued = true;
                             }
+                            // Set cardinalityToggle based on existing 
cardinality (SET or LIST), default to SET
+                            if (attrObj.cardinality) {
+                                attrDetails.cardinalityToggle = 
(attrObj.cardinality === "LIST") ? "LIST" : "SET";
+                            } else {
+                                // If cardinality is not set but it's an array 
type, default to SET
+                                if (attrObj.typeName.includes('array')) {
+                                    attrDetails.cardinalityToggle = "SET";
+                                }
+                            }
                         }
                     });
                     this.showDetails = false;
@@ -197,6 +206,27 @@ define(['require',
                             }
                         })
                     },
+                    cardinality: {
+                        label: "Cardinality",
+                        cell: "html",
+                        editable: false,
+                        sortable: true,
+                        formatter: _.extend({}, 
Backgrid.CellFormatter.prototype, {
+                            fromRaw: function(rawValue, model) {
+                                var cardinality = model.get('cardinality');
+                                if (!cardinality) {
+                                    // If cardinality is not set, determine 
from typeName
+                                    if 
(model.get('typeName').indexOf('array<') > -1) {
+                                        // Default to SET for array types if 
cardinality is not specified
+                                        cardinality = "SET";
+                                    } else {
+                                        cardinality = "SINGLE";
+                                    }
+                                }
+                                return _.escape(cardinality);
+                            }
+                        })
+                    },
                     maxStrLength: {
                         label: "Max Length",
                         cell: "html",
diff --git 
a/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
 
b/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
index 5190db031..6dddb8f65 100644
--- 
a/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
+++ 
b/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
@@ -48,6 +48,10 @@ define(['require',
                 enumValueSelector: "[data-id='enumValueSelector']",
                 multiValueSelect: "[data-id='multiValueSelect']",
                 multiValueSelectStatus: "[data-id='multiValueSelectStatus']",
+                cardinalityToggleContainer: 
"[data-id='cardinalityToggleContainer']",
+                cardinalityToggle: "[data-id='cardinalityToggle']",
+                cardinalitySET: "[data-id='cardinalitySET']",
+                cardinalityLIST: "[data-id='cardinalityLIST']",
                 stringLengthContainer: "[data-id='stringLengthContainer']",
                 stringLengthValue: "[data-id='stringLength']",
                 createNewEnum: "[data-id='createNewEnum']"
@@ -122,15 +126,72 @@ define(['require',
                     this.model.set({ "enumValues": 
this.ui.enumValueSelector.val() });
                 };
                 events["change " + this.ui.multiValueSelectStatus] = 
function(e) {
+                    var that = this;
                     this.model.set({ "multiValueSelect": e.target.checked });
                     var typename = this.model.get('typeName');
                     if (e.target.checked) {
                         typename = "array<" + typename + ">";
+                        // Show cardinality toggle when multivalues is enabled
+                        that.ui.cardinalityToggleContainer.show();
+                        // Disable cardinality toggle buttons in edit mode
+                        if (that.isAttrEdit) {
+                            that.ui.cardinalitySET.attr('disabled', 
'disabled');
+                            that.ui.cardinalityLIST.attr('disabled', 
'disabled');
+                            that.ui.cardinalitySET.addClass('disabled');
+                            that.ui.cardinalityLIST.addClass('disabled');
+                        } else {
+                            that.ui.cardinalitySET.removeAttr('disabled');
+                            that.ui.cardinalityLIST.removeAttr('disabled');
+                            that.ui.cardinalitySET.removeClass('disabled');
+                            that.ui.cardinalityLIST.removeClass('disabled');
+                        }
+                        // Set default cardinality to SET if not already set
+                        if (!that.model.get('cardinalityToggle')) {
+                            that.model.set({ "cardinalityToggle": "SET" });
+                            that.ui.cardinalitySET.addClass('active');
+                            that.ui.cardinalityLIST.removeClass('active');
+                        } else {
+                            // Set active button based on current value
+                            var currentCardinality = 
that.model.get('cardinalityToggle');
+                            if (currentCardinality === "LIST") {
+                                that.ui.cardinalityLIST.addClass('active');
+                                that.ui.cardinalitySET.removeClass('active');
+                            } else {
+                                that.ui.cardinalitySET.addClass('active');
+                                that.ui.cardinalityLIST.removeClass('active');
+                            }
+                        }
                     } else {
                         typename = typename.replace('array<', '').replace('>', 
'');
+                        // Hide cardinality toggle when multivalues is disabled
+                        that.ui.cardinalityToggleContainer.hide();
+                        // Reset cardinality toggle
+                        that.model.set({ "cardinalityToggle": "SET" });
                     }
                     this.model.set({ "typeName": typename });
                 };
+                events["click " + this.ui.cardinalitySET] = function(e) {
+                    e.preventDefault();
+                    var that = this;
+                    // Disable cardinality toggle in edit mode
+                    if (that.isAttrEdit) {
+                        return;
+                    }
+                    that.model.set({ "cardinalityToggle": "SET" });
+                    that.ui.cardinalitySET.addClass('active');
+                    that.ui.cardinalityLIST.removeClass('active');
+                };
+                events["click " + this.ui.cardinalityLIST] = function(e) {
+                    e.preventDefault();
+                    var that = this;
+                    // Disable cardinality toggle in edit mode
+                    if (that.isAttrEdit) {
+                        return;
+                    }
+                    that.model.set({ "cardinalityToggle": "LIST" });
+                    that.ui.cardinalityLIST.addClass('active');
+                    that.ui.cardinalitySET.removeClass('active');
+                };
                 events["change " + this.ui.entityTypeSelector] = function(e) {
                     var options = this.model.get('options') || {};
                     options.applicableEntityTypes = 
JSON.stringify(this.ui.entityTypeSelector.val());
@@ -223,6 +284,24 @@ define(['require',
                         this.ui.multiValueSelect.show();
                         $(this.ui.multiValueSelectStatus).prop('checked', 
true).trigger('change');
                         this.ui.multiValueSelectStatus.attr("disabled", 
"false");
+                        // Set cardinality toggle based on existing 
cardinality (SET or LIST), default to SET
+                        var existingCardinality = 
this.model.get("cardinality");
+                        var cardinalityToggle = (existingCardinality === 
"LIST") ? "LIST" : "SET";
+                        this.model.set({ "cardinalityToggle": 
cardinalityToggle });
+                        if (cardinalityToggle === "LIST") {
+                            this.ui.cardinalityLIST.addClass('active');
+                            this.ui.cardinalitySET.removeClass('active');
+                        } else {
+                            this.ui.cardinalitySET.addClass('active');
+                            this.ui.cardinalityLIST.removeClass('active');
+                        }
+                        // Disable cardinality toggle buttons in edit mode
+                        if (this.isAttrEdit) {
+                            this.ui.cardinalitySET.attr('disabled', 
'disabled');
+                            this.ui.cardinalityLIST.attr('disabled', 
'disabled');
+                            this.ui.cardinalitySET.addClass('disabled');
+                            this.ui.cardinalityLIST.addClass('disabled');
+                        }
                     }
                 }
             },
diff --git 
a/dashboardv2/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
 
b/dashboardv2/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
index c02236843..53e14b9c4 100644
--- 
a/dashboardv2/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
+++ 
b/dashboardv2/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
@@ -244,11 +244,11 @@ define(['require',
                         accordion: false,
                         alwaysVisible: true,
                         expand: function(el, model) {
-                            el.attr('colspan', '8');
+                            el.attr('colspan', '9');
                             var attrValues = '',
                                 attrTable = $('table'),
                                 attrTableBody = $('tbody'),
-                                attrTableHeading = "<thead><td 
style='display:table-cell'><b>Attribute</b></td><td 
style='display:table-cell'><b>Type</b></td><td 
style='display:table-cell'><b>Search Weight</b></td><td 
style='display:table-cell'><b>Enable Multivalues</b></td><td 
style='display:table-cell'><b>Max Length</b></td><td 
style='display:table-cell'><b>Applicable Type(s)</b></td><td 
style='display:table-cell'><b>Action</b></td></thead>",
+                                attrTableHeading = "<thead><td 
style='display:table-cell'><b>Attribute</b></td><td 
style='display:table-cell'><b>Type</b></td><td 
style='display:table-cell'><b>Search Weight</b></td><td 
style='display:table-cell'><b>Enable Multivalues</b></td><td 
style='display:table-cell'><b>Cardinality</b></td><td 
style='display:table-cell'><b>Max Length</b></td><td 
style='display:table-cell'><b>Applicable Type(s)</b></td><td 
style='display:table-cell'><b>Action</b></td> [...]
                                 attrRow = '',
                                 attrTableDetails = '';
                             if (model.attributes && 
model.attributes.attributeDefs.length) {
@@ -256,7 +256,8 @@ define(['require',
                                     var applicableEntityTypes = '',
                                         typeName = attrObj.typeName,
                                         multiSelect = '',
-                                        maxString = 'NA';
+                                        maxString = 'NA',
+                                        cardinality = 'SINGLE';
                                     if (attrObj.options && 
attrObj.options.applicableEntityTypes) {
                                         var entityTypes = 
JSON.parse(attrObj.options.applicableEntityTypes);
                                         _.each(entityTypes, function(values) {
@@ -266,12 +267,21 @@ define(['require',
                                     if (typeName.includes('array')) {
                                         typeName = _.escape(typeName);
                                         multiSelect = 'checked';
+                                        // Determine cardinality - use 
existing cardinality value or default to SET
+                                        if (attrObj.cardinality) {
+                                            cardinality = attrObj.cardinality;
+                                        } else {
+                                            cardinality = 'SET';
+                                        }
+                                    } else {
+                                        // For non-array types, cardinality is 
SINGLE
+                                        cardinality = 'SINGLE';
                                     }
                                     if (typeName.includes('string') && 
attrObj.options && attrObj.options.maxStrLength) {
                                         maxString = 
attrObj.options.maxStrLength;
                                     }
 
-                                    attrRow += "<tr> <td 
style='display:table-cell'>" + _.escape(attrObj.name) + "</td><td 
style='display:table-cell'>" + typeName + "</td><td 
style='display:table-cell'>" + _.escape(attrObj.searchWeight) + "</td><td 
style='display:table-cell'><input type='checkbox' class='form-check-input 
multi-value-select' " + multiSelect + " disabled='disabled'> </td><td 
style='display:table-cell'>" + maxString + "</td><td 
style='display:table-cell'>" + applicableEntit [...]
+                                    attrRow += "<tr> <td 
style='display:table-cell'>" + _.escape(attrObj.name) + "</td><td 
style='display:table-cell'>" + typeName + "</td><td 
style='display:table-cell'>" + _.escape(attrObj.searchWeight) + "</td><td 
style='display:table-cell'><input type='checkbox' class='form-check-input 
multi-value-select' " + multiSelect + " disabled='disabled'> </td><td 
style='display:table-cell'>" + _.escape(cardinality) + "</td><td 
style='display:table-cell'>" + max [...]
                                 });
                                 var adminText = '<div class="row"><div 
class="col-sm-12 attr-details"><table style="padding: 50px;">' + 
attrTableHeading + attrRow + '</table></div></div>';
                                 $(el).append($('<div>').html(adminText));
diff --git 
a/dashboardv2/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
 
b/dashboardv2/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
index 165b99500..200e4ad16 100644
--- 
a/dashboardv2/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
+++ 
b/dashboardv2/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
@@ -216,6 +216,27 @@ define(['require',
                 }
 
             },
+            processAttributes: function(attributes) {
+                var that = this;
+                return attributes.map(function(item) {
+                    var multiValueSelect = item.multiValueSelect || false;
+                    var cardinalityToggle = item.cardinalityToggle || "SET";
+                    
+                    // Determine cardinality based on multiValueSelect and 
cardinality toggle
+                    var finalCardinality = "SINGLE";
+                    if (multiValueSelect) {
+                        // If multivalues is enabled, use the 
cardinalityToggle (SET or LIST)
+                        // Default to SET if not specified
+                        finalCardinality = cardinalityToggle === "LIST" ? 
"LIST" : (item.cardinality === "LIST" ? "LIST" : "SET");
+                    }
+                    
+                    // Remove cardinalityToggle from the final object as it's 
not part of API
+                    var processedItem = _.omit(item, 'cardinalityToggle');
+                    processedItem.cardinality = finalCardinality;
+                    
+                    return processedItem;
+                });
+            },
             onCreateBusinessMetadata: function() {
                 var that = this;
                 if (this.validateValues()) {
@@ -227,6 +248,9 @@ define(['require',
                 var attributeObj = this.collection.toJSON();
                 if (this.collection.length === 1 && 
this.collection.first().get("name") === "") {
                     attributeObj = [];
+                } else {
+                    // Process attributes to include cardinality
+                    attributeObj = this.processAttributes(attributeObj);
                 }
                 this.json = {
                     "enumDefs": [],
@@ -279,7 +303,9 @@ define(['require',
                     if (selectedBusinessMetadataClone.attributeDefs === 
undefined) {
                         selectedBusinessMetadataClone.attributeDefs = [];
                     }
-                    selectedBusinessMetadataClone.attributeDefs = 
selectedBusinessMetadataClone.attributeDefs.concat(this.collection.toJSON());
+                    // Process new attributes to include cardinality
+                    var newAttributes = 
this.processAttributes(this.collection.toJSON());
+                    selectedBusinessMetadataClone.attributeDefs = 
selectedBusinessMetadataClone.attributeDefs.concat(newAttributes);
                     this.json = {
                         "enumDefs": [],
                         "structDefs": [],
diff --git a/dashboardv3/public/css/scss/business-metadata.scss 
b/dashboardv3/public/css/scss/business-metadata.scss
index efed616e1..2a593bee8 100644
--- a/dashboardv3/public/css/scss/business-metadata.scss
+++ b/dashboardv3/public/css/scss/business-metadata.scss
@@ -240,4 +240,12 @@
     .business-metadata-detail-attr-key {
         width: 30%;
     }
+}
+
+// Disabled state for cardinality toggle buttons
+.cardinality-btn.disabled,
+.cardinality-btn[disabled] {
+    opacity: 0.6;
+    cursor: not-allowed;
+    pointer-events: none;
 }
\ No newline at end of file
diff --git 
a/dashboardv3/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
 
b/dashboardv3/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
index aedb6dabe..8ffb689b4 100644
--- 
a/dashboardv3/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
+++ 
b/dashboardv3/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
@@ -68,7 +68,17 @@
     <div class="form-group" data-id="multiValueSelect">
         <label class="control-label col-sm-3" for="multiValSelect">Enable 
Multivalues</label>
         <div class="col-sm-8">
-            <input type="checkbox" class="form-check-input multi-value-select" 
data-id="multiValueSelectStatus">
+            <div class="row">
+                <div class="col-sm-6">
+                    <input type="checkbox" class="form-check-input 
multi-value-select" data-id="multiValueSelectStatus">
+                </div>
+                <div class="col-sm-6" data-id="cardinalityToggleContainer" 
style="display: none;">
+                    <div class="btn-group" role="group" 
data-id="cardinalityToggle">
+                        <button type="button" class="btn btn-sm btn-default 
cardinality-btn" data-value="SET" data-id="cardinalitySET">SET</button>
+                        <button type="button" class="btn btn-sm btn-default 
cardinality-btn active" data-value="LIST" 
data-id="cardinalityLIST">LIST</button>
+                    </div>
+                </div>
+            </div>
         </div>
     </div>
     <div class="form-group enumtype-container" 
data-id="enumTypeSelectorContainer">
diff --git 
a/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
 
b/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
index dd1e470e2..4646abbb1 100644
--- 
a/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
+++ 
b/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
@@ -108,6 +108,15 @@ define(['require',
                                 attrDetails.typeName = 
attrObj.typeName.replace("array<", "").replace(">", "");
                                 attrDetails.multiValued = true;
                             }
+                            // Set cardinalityToggle based on existing 
cardinality (SET or LIST), default to SET
+                            if (attrObj.cardinality) {
+                                attrDetails.cardinalityToggle = 
(attrObj.cardinality === "LIST") ? "LIST" : "SET";
+                            } else {
+                                // If cardinality is not set but it's an array 
type, default to SET
+                                if (attrObj.typeName.includes('array')) {
+                                    attrDetails.cardinalityToggle = "SET";
+                                }
+                            }
                         }
                     });
                     this.showDetails = false;
@@ -197,6 +206,27 @@ define(['require',
                             }
                         })
                     },
+                    cardinality: {
+                        label: "Cardinality",
+                        cell: "html",
+                        editable: false,
+                        sortable: true,
+                        formatter: _.extend({}, 
Backgrid.CellFormatter.prototype, {
+                            fromRaw: function(rawValue, model) {
+                                var cardinality = model.get('cardinality');
+                                if (!cardinality) {
+                                    // If cardinality is not set, determine 
from typeName
+                                    if 
(model.get('typeName').indexOf('array<') > -1) {
+                                        // Default to SET for array types if 
cardinality is not specified
+                                        cardinality = "SET";
+                                    } else {
+                                        cardinality = "SINGLE";
+                                    }
+                                }
+                                return _.escape(cardinality);
+                            }
+                        })
+                    },
                     maxStrLength: {
                         label: "Max Length",
                         cell: "html",
diff --git 
a/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
 
b/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
index 5190db031..6dddb8f65 100644
--- 
a/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
+++ 
b/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
@@ -48,6 +48,10 @@ define(['require',
                 enumValueSelector: "[data-id='enumValueSelector']",
                 multiValueSelect: "[data-id='multiValueSelect']",
                 multiValueSelectStatus: "[data-id='multiValueSelectStatus']",
+                cardinalityToggleContainer: 
"[data-id='cardinalityToggleContainer']",
+                cardinalityToggle: "[data-id='cardinalityToggle']",
+                cardinalitySET: "[data-id='cardinalitySET']",
+                cardinalityLIST: "[data-id='cardinalityLIST']",
                 stringLengthContainer: "[data-id='stringLengthContainer']",
                 stringLengthValue: "[data-id='stringLength']",
                 createNewEnum: "[data-id='createNewEnum']"
@@ -122,15 +126,72 @@ define(['require',
                     this.model.set({ "enumValues": 
this.ui.enumValueSelector.val() });
                 };
                 events["change " + this.ui.multiValueSelectStatus] = 
function(e) {
+                    var that = this;
                     this.model.set({ "multiValueSelect": e.target.checked });
                     var typename = this.model.get('typeName');
                     if (e.target.checked) {
                         typename = "array<" + typename + ">";
+                        // Show cardinality toggle when multivalues is enabled
+                        that.ui.cardinalityToggleContainer.show();
+                        // Disable cardinality toggle buttons in edit mode
+                        if (that.isAttrEdit) {
+                            that.ui.cardinalitySET.attr('disabled', 
'disabled');
+                            that.ui.cardinalityLIST.attr('disabled', 
'disabled');
+                            that.ui.cardinalitySET.addClass('disabled');
+                            that.ui.cardinalityLIST.addClass('disabled');
+                        } else {
+                            that.ui.cardinalitySET.removeAttr('disabled');
+                            that.ui.cardinalityLIST.removeAttr('disabled');
+                            that.ui.cardinalitySET.removeClass('disabled');
+                            that.ui.cardinalityLIST.removeClass('disabled');
+                        }
+                        // Set default cardinality to SET if not already set
+                        if (!that.model.get('cardinalityToggle')) {
+                            that.model.set({ "cardinalityToggle": "SET" });
+                            that.ui.cardinalitySET.addClass('active');
+                            that.ui.cardinalityLIST.removeClass('active');
+                        } else {
+                            // Set active button based on current value
+                            var currentCardinality = 
that.model.get('cardinalityToggle');
+                            if (currentCardinality === "LIST") {
+                                that.ui.cardinalityLIST.addClass('active');
+                                that.ui.cardinalitySET.removeClass('active');
+                            } else {
+                                that.ui.cardinalitySET.addClass('active');
+                                that.ui.cardinalityLIST.removeClass('active');
+                            }
+                        }
                     } else {
                         typename = typename.replace('array<', '').replace('>', 
'');
+                        // Hide cardinality toggle when multivalues is disabled
+                        that.ui.cardinalityToggleContainer.hide();
+                        // Reset cardinality toggle
+                        that.model.set({ "cardinalityToggle": "SET" });
                     }
                     this.model.set({ "typeName": typename });
                 };
+                events["click " + this.ui.cardinalitySET] = function(e) {
+                    e.preventDefault();
+                    var that = this;
+                    // Disable cardinality toggle in edit mode
+                    if (that.isAttrEdit) {
+                        return;
+                    }
+                    that.model.set({ "cardinalityToggle": "SET" });
+                    that.ui.cardinalitySET.addClass('active');
+                    that.ui.cardinalityLIST.removeClass('active');
+                };
+                events["click " + this.ui.cardinalityLIST] = function(e) {
+                    e.preventDefault();
+                    var that = this;
+                    // Disable cardinality toggle in edit mode
+                    if (that.isAttrEdit) {
+                        return;
+                    }
+                    that.model.set({ "cardinalityToggle": "LIST" });
+                    that.ui.cardinalityLIST.addClass('active');
+                    that.ui.cardinalitySET.removeClass('active');
+                };
                 events["change " + this.ui.entityTypeSelector] = function(e) {
                     var options = this.model.get('options') || {};
                     options.applicableEntityTypes = 
JSON.stringify(this.ui.entityTypeSelector.val());
@@ -223,6 +284,24 @@ define(['require',
                         this.ui.multiValueSelect.show();
                         $(this.ui.multiValueSelectStatus).prop('checked', 
true).trigger('change');
                         this.ui.multiValueSelectStatus.attr("disabled", 
"false");
+                        // Set cardinality toggle based on existing 
cardinality (SET or LIST), default to SET
+                        var existingCardinality = 
this.model.get("cardinality");
+                        var cardinalityToggle = (existingCardinality === 
"LIST") ? "LIST" : "SET";
+                        this.model.set({ "cardinalityToggle": 
cardinalityToggle });
+                        if (cardinalityToggle === "LIST") {
+                            this.ui.cardinalityLIST.addClass('active');
+                            this.ui.cardinalitySET.removeClass('active');
+                        } else {
+                            this.ui.cardinalitySET.addClass('active');
+                            this.ui.cardinalityLIST.removeClass('active');
+                        }
+                        // Disable cardinality toggle buttons in edit mode
+                        if (this.isAttrEdit) {
+                            this.ui.cardinalitySET.attr('disabled', 
'disabled');
+                            this.ui.cardinalityLIST.attr('disabled', 
'disabled');
+                            this.ui.cardinalitySET.addClass('disabled');
+                            this.ui.cardinalityLIST.addClass('disabled');
+                        }
                     }
                 }
             },
diff --git 
a/dashboardv3/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
 
b/dashboardv3/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
index 274630656..099a220af 100644
--- 
a/dashboardv3/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
+++ 
b/dashboardv3/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
@@ -244,11 +244,11 @@ define(['require',
                         accordion: false,
                         alwaysVisible: true,
                         expand: function(el, model) {
-                            el.attr('colspan', '8');
+                            el.attr('colspan', '9');
                             var attrValues = '',
                                 attrTable = $('table'),
                                 attrTableBody = $('tbody'),
-                                attrTableHeading = "<thead><td 
style='display:table-cell'><b>Attribute</b></td><td 
style='display:table-cell'><b>Type</b></td><td 
style='display:table-cell'><b>Search Weight</b></td><td 
style='display:table-cell'><b>Enable Multivalues</b></td><td 
style='display:table-cell'><b>Max Length</b></td><td 
style='display:table-cell'><b>Applicable Type(s)</b></td><td 
style='display:table-cell'><b>Action</b></td></thead>",
+                                attrTableHeading = "<thead><td 
style='display:table-cell'><b>Attribute</b></td><td 
style='display:table-cell'><b>Type</b></td><td 
style='display:table-cell'><b>Search Weight</b></td><td 
style='display:table-cell'><b>Enable Multivalues</b></td><td 
style='display:table-cell'><b>Cardinality</b></td><td 
style='display:table-cell'><b>Max Length</b></td><td 
style='display:table-cell'><b>Applicable Type(s)</b></td><td 
style='display:table-cell'><b>Action</b></td> [...]
                                 attrRow = '',
                                 attrTableDetails = '';
                             if (model.attributes && 
model.attributes.attributeDefs.length) {
@@ -256,7 +256,8 @@ define(['require',
                                     var applicableEntityTypes = '',
                                         typeName = attrObj.typeName,
                                         multiSelect = '',
-                                        maxString = 'NA';
+                                        maxString = 'NA',
+                                        cardinality = 'SINGLE';
                                     if (attrObj.options && 
attrObj.options.applicableEntityTypes) {
                                         var entityTypes = 
JSON.parse(attrObj.options.applicableEntityTypes);
                                         _.each(entityTypes, function(values) {
@@ -266,12 +267,21 @@ define(['require',
                                     if (typeName.includes('array')) {
                                         typeName = _.escape(typeName);
                                         multiSelect = 'checked';
+                                        // Determine cardinality - use 
existing cardinality value or default to SET
+                                        if (attrObj.cardinality) {
+                                            cardinality = attrObj.cardinality;
+                                        } else {
+                                            cardinality = 'SET';
+                                        }
+                                    } else {
+                                        // For non-array types, cardinality is 
SINGLE
+                                        cardinality = 'SINGLE';
                                     }
                                     if (typeName.includes('string') && 
attrObj.options && attrObj.options.maxStrLength) {
                                         maxString = 
attrObj.options.maxStrLength;
                                     }
 
-                                    attrRow += "<tr> <td 
style='display:table-cell'>" + _.escape(attrObj.name) + "</td><td 
style='display:table-cell'>" + typeName + "</td><td 
style='display:table-cell'>" + _.escape(attrObj.searchWeight) + "</td><td 
style='display:table-cell'><input type='checkbox' class='form-check-input 
multi-value-select' " + multiSelect + " disabled='disabled'> </td><td 
style='display:table-cell'>" + maxString + "</td><td 
style='display:table-cell'>" + applicableEntit [...]
+                                    attrRow += "<tr> <td 
style='display:table-cell'>" + _.escape(attrObj.name) + "</td><td 
style='display:table-cell'>" + typeName + "</td><td 
style='display:table-cell'>" + _.escape(attrObj.searchWeight) + "</td><td 
style='display:table-cell'><input type='checkbox' class='form-check-input 
multi-value-select' " + multiSelect + " disabled='disabled'> </td><td 
style='display:table-cell'>" + _.escape(cardinality) + "</td><td 
style='display:table-cell'>" + max [...]
                                 });
                                 var adminText = '<div class="row"><div 
class="col-sm-12 attr-details"><table style="padding: 50px;">' + 
attrTableHeading + attrRow + '</table></div></div>';
                                 $(el).append($('<div>').html(adminText));
diff --git 
a/dashboardv3/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
 
b/dashboardv3/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
index 165b99500..200e4ad16 100644
--- 
a/dashboardv3/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
+++ 
b/dashboardv3/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
@@ -216,6 +216,27 @@ define(['require',
                 }
 
             },
+            processAttributes: function(attributes) {
+                var that = this;
+                return attributes.map(function(item) {
+                    var multiValueSelect = item.multiValueSelect || false;
+                    var cardinalityToggle = item.cardinalityToggle || "SET";
+                    
+                    // Determine cardinality based on multiValueSelect and 
cardinality toggle
+                    var finalCardinality = "SINGLE";
+                    if (multiValueSelect) {
+                        // If multivalues is enabled, use the 
cardinalityToggle (SET or LIST)
+                        // Default to SET if not specified
+                        finalCardinality = cardinalityToggle === "LIST" ? 
"LIST" : (item.cardinality === "LIST" ? "LIST" : "SET");
+                    }
+                    
+                    // Remove cardinalityToggle from the final object as it's 
not part of API
+                    var processedItem = _.omit(item, 'cardinalityToggle');
+                    processedItem.cardinality = finalCardinality;
+                    
+                    return processedItem;
+                });
+            },
             onCreateBusinessMetadata: function() {
                 var that = this;
                 if (this.validateValues()) {
@@ -227,6 +248,9 @@ define(['require',
                 var attributeObj = this.collection.toJSON();
                 if (this.collection.length === 1 && 
this.collection.first().get("name") === "") {
                     attributeObj = [];
+                } else {
+                    // Process attributes to include cardinality
+                    attributeObj = this.processAttributes(attributeObj);
                 }
                 this.json = {
                     "enumDefs": [],
@@ -279,7 +303,9 @@ define(['require',
                     if (selectedBusinessMetadataClone.attributeDefs === 
undefined) {
                         selectedBusinessMetadataClone.attributeDefs = [];
                     }
-                    selectedBusinessMetadataClone.attributeDefs = 
selectedBusinessMetadataClone.attributeDefs.concat(this.collection.toJSON());
+                    // Process new attributes to include cardinality
+                    var newAttributes = 
this.processAttributes(this.collection.toJSON());
+                    selectedBusinessMetadataClone.attributeDefs = 
selectedBusinessMetadataClone.attributeDefs.concat(newAttributes);
                     this.json = {
                         "enumDefs": [],
                         "structDefs": [],

Reply via email to