public inbox for [email protected]  
help / color / mirror / Atom feed
[pgAdmin][RM6081] Advanced table fields in ERD
4+ messages / 2 participants
[nested] [flat]

* [pgAdmin][RM6081] Advanced table fields in ERD
@ 2021-10-11 11:43  Aditya Toshniwal <[email protected]>
  0 siblings, 1 reply; 4+ messages in thread

From: Aditya Toshniwal @ 2021-10-11 11:43 UTC (permalink / raw)
  To: pgadmin-hackers

Hi Hackers,

Attached patch will port Backbone based dialogs in ERD to React based using
the schema view UI framework. This allows us to use the existing table UI,
remove repeated code and enable advanced fields. I have hidden fields from
the existing table UI for ERD.
Please note, this also fixes #6529.

Please review.

-- 
Thanks,
Aditya Toshniwal
pgAdmin Hacker | Software Architect | *edbpostgres.com*
<http://edbpostgres.com;
"Don't Complain about Heat, Plant a TREE"


Attachments:

  [application/octet-stream] RM6081_6529.patch (130.8K, 3-RM6081_6529.patch)
  download | inline diff:
diff --git a/web/package.json b/web/package.json
index 046126c66..d7bb3d505 100644
--- a/web/package.json
+++ b/web/package.json
@@ -83,7 +83,7 @@
     "@material-ui/icons": "^4.11.2",
     "@material-ui/lab": "4.0.0-alpha.58",
     "@material-ui/pickers": "^3.2.10",
-    "@projectstorm/react-diagrams": "^6.4.2",
+    "@projectstorm/react-diagrams": "^6.6.1",
     "@simonwep/pickr": "^1.5.1",
     "@tippyjs/react": "^4.2.0",
     "@types/classnames": "^2.2.6",
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js
index ee6edcd1b..f57d540f0 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui.js
@@ -19,7 +19,7 @@ export function getNodeColumnSchema(treeNodeInfo, itemNodeData, pgBrowser) {
 }
 
 export default class ColumnSchema extends BaseUISchema {
-  constructor(getPrivilegeRoleSchema, nodeInfo, cltypeOptions, collspcnameOptions) {
+  constructor(getPrivilegeRoleSchema, nodeInfo, cltypeOptions, collspcnameOptions, inErd=false) {
     super({
       name: undefined,
       attowner: undefined,
@@ -57,6 +57,7 @@ export default class ColumnSchema extends BaseUISchema {
     this.nodeInfo = nodeInfo;
     this.cltypeOptions = cltypeOptions;
     this.collspcnameOptions = collspcnameOptions;
+    this.inErd = inErd;
 
     this.datatypes = [];
   }
@@ -71,8 +72,7 @@ export default class ColumnSchema extends BaseUISchema {
       return true;
     }
 
-    if('schema' in this.nodeInfo)
-    {
+    if(this.nodeInfo &&  ('schema' in this.nodeInfo)) {
       // We will disable control if it's system columns
       // inheritedfrom check is useful when we use this schema in table node
       // inheritedfrom has value then we should disable it
@@ -86,7 +86,7 @@ export default class ColumnSchema extends BaseUISchema {
       // ie: it's position is less than 1
       return !(!_.isUndefined(state.attnum) && state.attnum > 0);
     }
-    return true;
+    return false;
   }
 
   editableCheckForTable(state) {
@@ -179,7 +179,7 @@ export default class ColumnSchema extends BaseUISchema {
           ) || (
             'is_partitioned' in obj.top.origData
             && obj.top.origData['is_partitioned']
-            && obj.nodeInfo.server && obj.nodeInfo.server.version < 11000
+            && obj.getServerVersion() < 11000
           ))
         ) {
           return true;
@@ -239,7 +239,7 @@ export default class ColumnSchema extends BaseUISchema {
             filter: (options)=>{
               let result = options;
               let edit_types = state?.edit_types || [];
-              if(!obj.isNew(state)) {
+              if(!obj.isNew(state) && !this.inErd) {
                 result = _.filter(options, (o)=>edit_types.indexOf(o.value) > -1);
               }
               return result;
@@ -256,7 +256,7 @@ export default class ColumnSchema extends BaseUISchema {
             filter: (options)=>{
               let result = options;
               let edit_types = row?.edit_types || [];
-              if(!obj.isNew(row)) {
+              if(!obj.isNew(row) && !this.inErd) {
                 result = _.filter(options, (o)=>edit_types.indexOf(o.value) > -1);
               }
               return result;
@@ -272,7 +272,10 @@ export default class ColumnSchema extends BaseUISchema {
       id: 'inheritedfrom', label: gettext('Inherited from table'),
       type: 'text', readonly: true, editable: false,
       visible: function() {
-        return _.isUndefined(this.nodeInfo['table'] || this.nodeInfo['view'] || this.nodeInfo['mview']);
+        if(this.nodeInfo) {
+          return _.isUndefined(this.nodeInfo['table'] || this.nodeInfo['view'] || this.nodeInfo['mview']);
+        }
+        return false;
       },
     },{
       id: 'attlen', label: gettext('Length/Precision'),
@@ -417,8 +420,7 @@ export default class ColumnSchema extends BaseUISchema {
           {'label': gettext('IDENTITY'), 'value': 'i'},
         ];
 
-        if (this.nodeInfo && this.nodeInfo.server &&
-            this.nodeInfo.server.version >= 120000) {
+        if (this.getServerVersion() >= 120000) {
           // You can't change the existing column to Generated column.
           if (this.isNew(state)) {
             options.push({
@@ -529,15 +531,18 @@ export default class ColumnSchema extends BaseUISchema {
       ], null, null, ['name', 'value']),
       uniqueCol : ['name'], mode: ['edit', 'create'],
       canAdd: true, canEdit: false, canDelete: true,
-    }, {
+    },{
+      id: 'security', label: gettext('Security'), type: 'group',
+      visible: !this.inErd,
+    },{
       id: 'attacl', label: gettext('Privileges'), type: 'collection',
-      group: gettext('Security'),
+      group: 'security',
       schema: this.getPrivilegeRoleSchema(['a','r','w','x']),
       mode: ['edit'], canAdd: true, canDelete: true,
       uniqueCol : ['grantee'],
     },{
       id: 'seclabels', label: gettext('Security labels'), canAdd: true,
-      schema: new SecLabelSchema(), group: gettext('Security'),
+      schema: new SecLabelSchema(), group: 'security',
       mode: ['edit', 'create'], editable: false, type: 'collection',
       min_version: 90100, canEdit: false, canDelete: true,
       uniqueCol : ['provider'],
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.ui.js
index 5903f5394..2dffa5f02 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.ui.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.ui.js
@@ -86,7 +86,7 @@ class ForeignKeyHeaderSchema extends BaseUISchema {
   }
 }
 
-class ForeignKeyColumnSchema extends BaseUISchema {
+export class ForeignKeyColumnSchema extends BaseUISchema {
   constructor() {
     super({
       local_column: undefined,
@@ -111,7 +111,7 @@ class ForeignKeyColumnSchema extends BaseUISchema {
 }
 
 export default class ForeignKeySchema extends BaseUISchema {
-  constructor(fieldOptions={}, nodeInfo, getColumns, initData={}) {
+  constructor(fieldOptions={}, nodeInfo, getColumns, initValues={}, inErd=false) {
     super({
       name: undefined,
       reftab: undefined,
@@ -128,7 +128,7 @@ export default class ForeignKeySchema extends BaseUISchema {
       autoindex: true,
       coveringindex: undefined,
       hasindex:undefined,
-      ...initData,
+      ...initValues,
     });
 
     this.nodeInfo = nodeInfo;
@@ -136,7 +136,7 @@ export default class ForeignKeySchema extends BaseUISchema {
     this.fkHeaderSchema = new ForeignKeyHeaderSchema(fieldOptions, getColumns);
     this.fkHeaderSchema.fkObj = this;
     this.fkColumnSchema = new ForeignKeyColumnSchema();
-
+    this.inErd = inErd;
   }
 
   get idAttribute() {
@@ -240,9 +240,7 @@ export default class ForeignKeySchema extends BaseUISchema {
           return true;
         }
         // If we are in table edit mode then
-        if(obj.inTable) {
-          return true;
-        } else if(state.hasindex) {
+        if(state.hasindex) {
           return true;
         }
         return false;
@@ -252,7 +250,7 @@ export default class ForeignKeySchema extends BaseUISchema {
           return {};
         }
         // If we are in table edit mode
-        if(obj.inTable) {
+        if(obj.inTable && !this.inErd) {
           if(obj.isNew(state) && obj.top.isNew()) {
             return {autoindex: false, coveringindex: ''};
           }
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.ui.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.ui.js
index 94622e2b3..1290e6ce3 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.ui.js
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/table.ui.js
@@ -70,7 +70,7 @@ export function getNodeTableSchema(treeNodeInfo, itemNodeData, pgBrowser) {
 }
 
 export class ConstraintsSchema extends BaseUISchema {
-  constructor(nodeInfo, getFkObj, getExConsObj, otherOptions) {
+  constructor(nodeInfo, getFkObj, getExConsObj, otherOptions, inErd=false) {
     super();
     this.nodeInfo = nodeInfo;
     this.primaryKeyObj = new PrimaryKeySchema({
@@ -81,13 +81,16 @@ export class ConstraintsSchema extends BaseUISchema {
       spcname: otherOptions.spcname,
     }, nodeInfo);
     this.exConsObj = getExConsObj();
+    this.inErd = inErd;
   }
 
   changeColumnOptions(colOptions) {
     this.primaryKeyObj.changeColumnOptions(colOptions);
     this.fkObj.changeColumnOptions(colOptions);
-    this.uniqueConsObj.changeColumnOptions(colOptions);
-    this.exConsObj.changeColumnOptions(colOptions);
+    if(!this.inErd) {
+      this.uniqueConsObj.changeColumnOptions(colOptions);
+      this.exConsObj.changeColumnOptions(colOptions);
+    }
   }
 
   anyColumnAdded(state) {
@@ -147,20 +150,24 @@ export class ConstraintsSchema extends BaseUISchema {
           return {foreign_key: []};
         }
       }
+    },{
+      id: 'check_group', type: 'group', label: gettext('Check'), visible: !this.inErd,
     },{
       id: 'check_constraint', label: '',
       schema: new CheckConstraintSchema(),
       editable: false, type: 'collection',
-      group: gettext('Check'), mode: ['edit', 'create'],
+      group: 'check_group', mode: ['edit', 'create'],
       canEdit: true, canDelete: true, deps:['is_partitioned'],
       canAdd: true,
       columns : ['name', 'consrc'],
       disabled: this.inCatalog,
+    },{
+      id: 'unique_group', type: 'group', label: gettext('Unique'), visible: !this.inErd,
     },{
       id: 'unique_constraint', label: '',
       schema: this.uniqueConsObj,
       editable: false, type: 'collection',
-      group: gettext('Unique'), mode: ['edit', 'create'],
+      group: 'unique_group', mode: ['edit', 'create'],
       canEdit: true, canDelete: true, deps:['is_partitioned', 'typname'],
       columns : ['name', 'columns'],
       disabled: this.inCatalog,
@@ -176,11 +183,13 @@ export class ConstraintsSchema extends BaseUISchema {
           return {unique_constraint: []};
         }
       }
+    },{
+      id: 'exclude_group', type: 'group', label: gettext('Exclude'), visible: !this.inErd,
     },{
       id: 'exclude_constraint', label: '',
       schema: this.exConsObj,
       editable: false, type: 'collection',
-      group: gettext('Exclude'), mode: ['edit', 'create'],
+      group: 'exclude_group', mode: ['edit', 'create'],
       canEdit: true, canDelete: true, deps:['is_partitioned'],
       columns : ['name', 'columns', 'constraint'],
       disabled: this.inCatalog,
@@ -276,7 +285,7 @@ export class LikeSchema extends BaseUISchema {
 
 export default class TableSchema extends BaseUISchema {
   constructor(fieldOptions={}, nodeInfo, schemas={}, getPrivilegeRoleSchema=()=>{}, getColumns=()=>[],
-    getCollations=()=>[], getOperatorClass=()=>[], getAttachTables=()=>[], initValues={}) {
+    getCollations=()=>[], getOperatorClass=()=>[], getAttachTables=()=>[], initValues={}, inErd=false) {
     super({
       name: undefined,
       oid: undefined,
@@ -307,6 +316,7 @@ export default class TableSchema extends BaseUISchema {
       autovacuum_enabled: 'x',
       primary_key: [],
       foreign_key: [],
+      partition_keys: [],
       partitions: [],
       partition_type: 'range',
       is_partitioned: false,
@@ -325,6 +335,24 @@ export default class TableSchema extends BaseUISchema {
     this.columnsSchema = this.schemas.columns && this.schemas.columns() || {};
     this.vacuumSettingsSchema = this.schemas.vacuum_settings && this.schemas.vacuum_settings() || {};
     this.partitionKeysObj = new PartitionKeysSchema([], getCollations, getOperatorClass);
+    this.inErd = inErd;
+  }
+
+  static getErdSupportedData(data) {
+    let newData = {...data};
+    const SUPPORTED_KEYS = [
+      'name', 'schema', 'description', 'rlspolicy', 'forcerlspolicy', 'fillfactor',
+      'toast_tuple_target', 'parallel_workers', 'relhasoids', 'relpersistence',
+      'columns', 'primary_key', 'foreign_key',
+    ];
+    newData = _.pick(newData, SUPPORTED_KEYS);
+
+    /* Remove inherited references */
+    newData.columns = newData.columns.map((c)=>{
+      delete c.inheritedfromtable;
+      return c;
+    });
+    return newData;
   }
 
   get idAttribute() {
@@ -397,9 +425,9 @@ export default class TableSchema extends BaseUISchema {
       id: 'oid', label: gettext('OID'), type: 'text', mode: ['properties'],
     },{
       id: 'relowner', label: gettext('Owner'), type: 'select',
-      options: this.fieldOptions.relowner, noEmpty: true,
+      options: this.fieldOptions.relowner, noEmpty: this.inErd ? false : true,
       mode: ['properties', 'create', 'edit'], controlProps: {allowClear: false},
-      readonly: this.inCatalog,
+      readonly: this.inCatalog, visible: !this.inErd,
     },{
       id: 'schema', label: gettext('Schema'), type: 'select',
       options: this.fieldOptions.schema, noEmpty: true,
@@ -407,7 +435,7 @@ export default class TableSchema extends BaseUISchema {
       readonly: this.inCatalog,
     },{
       id: 'spcname', label: gettext('Tablespace'),
-
+      visible: !this.inErd,
       mode: ['properties', 'create', 'edit'], deps: ['is_partitioned'],
       readonly: this.inCatalog, type: (state)=>{
         return {
@@ -421,6 +449,9 @@ export default class TableSchema extends BaseUISchema {
       id: 'partition', type: 'group', label: gettext('Partitions'),
       mode: ['edit', 'create'], min_version: 100000,
       visible: function(state) {
+        if(this.inErd) {
+          return false;
+        }
         // Always show in case of create mode
         if (obj.isNew(state) || state.is_partitioned)
           return true;
@@ -429,7 +460,7 @@ export default class TableSchema extends BaseUISchema {
     },{
       id: 'is_partitioned', label:gettext('Partitioned table?'), cell: 'switch',
       type: 'switch', mode: ['properties', 'create', 'edit'],
-      min_version: 100000,
+      min_version: 100000, visible: !this.inErd,
       readonly: function(state) {
         if (!obj.isNew(state))
           return true;
@@ -447,7 +478,7 @@ export default class TableSchema extends BaseUISchema {
       type: 'select', group: gettext('Columns'),
       deps: ['typname', 'is_partitioned'], mode: ['create', 'edit'],
       controlProps: { multiple: true, allowClear: false, placeholder: gettext('Select to inherit from...')},
-      options: this.fieldOptions.coll_inherits,
+      options: this.fieldOptions.coll_inherits, visible: !this.inErd,
       optionsLoaded: (res)=>obj.inheritedTableList=res,
       disabled: (state)=>{
         if(state.adding_inherit_cols || state.is_partitioned){
@@ -581,7 +612,18 @@ export default class TableSchema extends BaseUISchema {
       deps: ['typname', 'is_partitioned'],
       depChange: (state, source, topState, actionObj)=>{
         if(source[0] === 'columns') {
-          obj.changeColumnOptions(state.columns);
+          /* In ERD, attnum is an imp var for setting the links
+          Here, attnum is set to max avail value.
+          */
+          let columns = state.columns;
+          if(actionObj.type === SCHEMA_STATE_ACTIONS.ADD_ROW && this.inErd) {
+            let lastAttnum = _.maxBy(columns, (c)=>c.attnum)?.attnum;
+            if(_.isUndefined(lastAttnum) || _.isNull(lastAttnum)) {
+              lastAttnum = -1;
+            }
+            columns[columns.length-1].attnum = lastAttnum + 1;
+          }
+          obj.changeColumnOptions(columns);
           /* If primary key switch changes, primary key collection need to change */
           if(actionObj.path.indexOf('is_primary_key') > -1) {
             let tabColPath = _.slice(actionObj.path, 0, -1);
@@ -631,6 +673,7 @@ export default class TableSchema extends BaseUISchema {
     },{
       id: 'typname', label: gettext('Of type'), type: 'select',
       mode: ['properties', 'create', 'edit'], group: 'advanced', deps: ['coll_inherits'],
+      visible: !this.inErd,
       disabled: (state)=>{
         if(!obj.inSchemaWithModelCheck(state) && isEmptyString(state.coll_inherits)) {
           return false;
@@ -743,6 +786,7 @@ export default class TableSchema extends BaseUISchema {
       type: 'nested-fieldset', label: gettext('Like'),
       group: 'advanced', mode: ['create'],
       schema: new LikeSchema(this.fieldOptions.like_relation),
+      visible: !this.inErd,
     },{
       id: 'partition_type', label:gettext('Partition Type'),
       editable: false, type: 'select', controlProps: {allowClear: false},
@@ -774,6 +818,7 @@ export default class TableSchema extends BaseUISchema {
       id: 'partition_keys', label:gettext('Partition Keys'),
       schema: obj.partitionKeysObj,
       editable: true, type: 'collection',
+      columns: ['key_type', 'pt_column', 'expression'].concat(!this.inErd ? ['collationame', 'op_class'] : []),
       group: 'partition', mode: ['create'],
       deps: ['is_partitioned', 'partition_type', 'typname'],
       canEdit: false, canDelete: true,
@@ -885,28 +930,32 @@ export default class TableSchema extends BaseUISchema {
         '</li></ul>',
       ].join(''),
       min_version: 100000,
-    },
-    {
+    },{
+      type: 'group', id: 'parameters', label: gettext('Parameters'),
+      visible: !this.inErd,
+    },{
       // Here - we will create tab control for storage parameters
       // (auto vacuum).
-      type: 'nested-tab', group: gettext('Parameters'),
+      type: 'nested-tab', group: 'parameters',
       mode: ['edit', 'create'], deps: ['is_partitioned'],
-      schema: this.vacuumSettingsSchema,
+      schema: this.vacuumSettingsSchema, visible: !this.inErd,
+    },{
+      id: 'security_group', type: 'group', label: gettext('Security'), visible: !this.inErd,
     },
     {
       id: 'relacl_str', label: gettext('Privileges'), disabled: this.inCatalog,
-      type: 'text', mode: ['properties'], group: gettext('Security'),
+      type: 'text', mode: ['properties'], group: 'security_group',
     },
     {
       id: 'relacl', label: gettext('Privileges'), type: 'collection',
-      group: gettext('Security'), schema: this.getPrivilegeRoleSchema(['a','r','w','d','D','x','t']),
+      group: 'security_group', schema: this.getPrivilegeRoleSchema(['a','r','w','d','D','x','t']),
       mode: ['edit', 'create'], canAdd: true, canDelete: true,
       uniqueCol : ['grantee'],
     },{
       id: 'seclabels', label: gettext('Security labels'), canEdit: false,
       schema: new SecLabelSchema(), editable: false, canAdd: true,
       type: 'collection', min_version: 90100, mode: ['edit', 'create'],
-      group: gettext('Security'), canDelete: true, control: 'unique-col-collection',
+      group: 'security_group', canDelete: true, control: 'unique-col-collection',
     },{
       id: 'vacuum_settings_str', label: gettext('Storage settings'),
       type: 'multiline', group: 'advanced', mode: ['properties'],
diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/utils.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/utils.py
index e0099396e..9f370a951 100644
--- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/utils.py
+++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/utils.py
@@ -1125,7 +1125,7 @@ class BaseTableView(PGChildNodeView, BasePartitionTable, VacuumSettings):
         elif part_type in data and data[part_type] == 'hash':
             partition_scheme = 'HASH ('
 
-        for row in data[part_keys]:
+        for row in data.get(part_keys, []):
             if row['key_type'] == 'column':
                 partition_scheme += self.qtIdent(
                     self.conn, row['pt_column'])
@@ -1141,7 +1141,7 @@ class BaseTableView(PGChildNodeView, BasePartitionTable, VacuumSettings):
                 partition_scheme += row['expression'] + ', '
 
         # Remove extra space and comma
-        if len(data[part_keys]) > 0:
+        if len(data.get(part_keys, [])) > 0:
             partition_scheme = partition_scheme[:-2]
         partition_scheme += ')'
 
diff --git a/web/pgadmin/static/js/SchemaView/FormView.jsx b/web/pgadmin/static/js/SchemaView/FormView.jsx
index 17371202e..74fa2bb2a 100644
--- a/web/pgadmin/static/js/SchemaView/FormView.jsx
+++ b/web/pgadmin/static/js/SchemaView/FormView.jsx
@@ -205,6 +205,13 @@ export default function FormView({
 
     if(modeSupported) {
       let {group, CustomControl} = field;
+      if(field.type === 'group') {
+        groupLabels[field.id] = field.label;
+        if(!visible) {
+          schemaRef.current.filterGroups.push(field.label);
+        }
+        return;
+      }
       group = groupLabels[group] || group || defaultTab;
 
       if(!tabs[group]) tabs[group] = [];
@@ -261,11 +268,6 @@ export default function FormView({
         } else {
           tabs[group].push(<DataGridView {...props}/>);
         }
-      } else if(field.type === 'group') {
-        groupLabels[field.id] = field.label;
-        if(!visible) {
-          schemaRef.current.filterGroups.push(field.label);
-        }
       } else {
         /* Its a form control */
         const hasError = _.isEqual(accessPath.concat(field.id), stateUtils.formErr.name);
diff --git a/web/pgadmin/static/js/SchemaView/index.jsx b/web/pgadmin/static/js/SchemaView/index.jsx
index 0f3073e9e..d78ba7a7b 100644
--- a/web/pgadmin/static/js/SchemaView/index.jsx
+++ b/web/pgadmin/static/js/SchemaView/index.jsx
@@ -89,7 +89,8 @@ function isValueEqual(val1, val2) {
   /* If the orig value and new value are of different datatype but of same value(numeric) "no change" */
   /* If the orig value is undefined or null and new value is boolean false "no change" */
   if ((_.isEqual(val1, val2)
-    || ((val1 === null || _.isUndefined(val1)) && !val2)
+    || ((val1 === null || _.isUndefined(val1)) && val2 === '')
+    || ((val1 === null || _.isUndefined(val1)) && typeof(val2) === 'boolean' && !val2)
     || (attrDefined ? _.isEqual(val1.toString(), val2.toString()) : false
     ))) {
     return true;
@@ -98,22 +99,6 @@ function isValueEqual(val1, val2) {
   }
 }
 
-function objectComparator(obj1, obj2) {
-  for(const key of _.union(Object.keys(obj1), Object.keys(obj2))) {
-    let equal = isValueEqual(obj1[key], obj2[key]);
-    if(equal) {
-      continue;
-    } else {
-      return false;
-    }
-  }
-  return true;
-}
-
-const diffArrayOptions = {
-  compareFunction: objectComparator,
-};
-
 function getChangedData(topSchema, viewHelperProps, sessData, stringify=false, includeSkipChange=true) {
   let changedData = {};
   let isEdit = viewHelperProps.mode === 'edit';
@@ -173,13 +158,13 @@ function getChangedData(topSchema, viewHelperProps, sessData, stringify=false, i
             );
             change = {};
             if(changeDiff.added.length > 0) {
-              change['added'] = cleanCid(changeDiff.added);
+              change['added'] = cleanCid(changeDiff.added, viewHelperProps.keepCid);
             }
             if(changeDiff.removed.length > 0) {
               change['deleted'] = cleanCid(changeDiff.removed.map((row)=>{
                 /* Deleted records should be original, not the changed */
                 return _.find(_.get(origVal, field.id), ['cid', row.cid]);
-              }));
+              }), viewHelperProps.keepCid);
             }
             if(changeDiff.updated.length > 0) {
               /* There is change in collection. Parse further to go deep */
@@ -204,7 +189,7 @@ function getChangedData(topSchema, viewHelperProps, sessData, stringify=false, i
                   });
                 }
               }
-              change['changed'] = cleanCid(change['changed']);
+              change['changed'] = cleanCid(change['changed'], viewHelperProps.keepCid);
             }
             if(Object.keys(change).length > 0) {
               attrChanged(field.id, change, true);
@@ -214,21 +199,18 @@ function getChangedData(topSchema, viewHelperProps, sessData, stringify=false, i
           }
         } else if(!isEdit) {
           if(field.type === 'collection') {
-            /* For fixed rows, check the updated changes */
-            if(!_.isUndefined(field.fixedRows)) {
-              const changeDiff = diffArray(
-                _.get(origVal, field.id) || [],
-                _.get(sessVal, field.id) || [],
-                'cid',
-                diffArrayOptions
-              );
-              if(changeDiff.updated.length > 0) {
-                let change = cleanCid(_.get(sessVal, field.id));
-                attrChanged(field.id, change, true);
-              }
-            } else {
-              let change = cleanCid(_.get(sessVal, field.id));
-              attrChanged(field.id, change);
+            const changeDiff = diffArray(
+              _.get(origVal, field.id) || [],
+              _.get(sessVal, field.id) || [],
+              'cid',
+            );
+            /* For fixed rows, check only the updated changes */
+            if((!_.isUndefined(field.fixedRows) && changeDiff.updated.length > 0)
+              || (_.isUndefined(field.fixedRows) && (
+                changeDiff.added.length > 0 || changeDiff.removed.length > 0 || changeDiff.updated.length > 0
+              ))) {
+              let change = cleanCid(_.get(sessVal, field.id), viewHelperProps.keepCid);
+              attrChanged(field.id, change, true);
             }
           } else {
             attrChanged(field.id);
@@ -409,18 +391,18 @@ const sessDataReducer = (state, action)=>{
 };
 
 /* Remove cid key added by prepareData */
-function cleanCid(coll) {
-  if(!coll) {
+function cleanCid(coll, keepCid=false) {
+  if(!coll || keepCid) {
     return coll;
   }
   return coll.map((o)=>_.pickBy(o, (v, k)=>k!='cid'));
 }
 
-function prepareData(val) {
+function prepareData(val, createMode=false) {
   if(_.isPlainObject(val)) {
     _.forIn(val, function (el) {
       if (_.isObject(el)) {
-        prepareData(el);
+        prepareData(el, createMode);
       }
     });
   } else if(_.isArray(val)) {
@@ -432,8 +414,8 @@ function prepareData(val) {
         So to decide whether row is new or not set, the cid starts with
         nn (not new) for existing rows. Newly added will start with 'c' (created)
         */
-        el['cid'] = _.uniqueId('nn');
-        prepareData(el);
+        el['cid'] = createMode ? _.uniqueId('c') : _.uniqueId('nn');
+        prepareData(el, createMode);
       }
     });
   }
@@ -532,7 +514,7 @@ function SchemaDialogView({
       });
     } else {
       /* Use the defaults as the initital data */
-      schema.origData = prepareData(schema.defaults);
+      schema.origData = prepareData(schema.defaults, true);
       schema.initialise(schema.origData);
       sessDispatch({
         type: SCHEMA_STATE_ACTIONS.INIT,
@@ -624,6 +606,7 @@ function SchemaDialogView({
           );
         }
       }).catch((err)=>{
+        console.error(err);
         setFormErr({
           name: 'apierror',
           message: parseApiError(err),
diff --git a/web/pgadmin/static/js/Theme/index.jsx b/web/pgadmin/static/js/Theme/index.jsx
index b27692875..3a0118802 100644
--- a/web/pgadmin/static/js/Theme/index.jsx
+++ b/web/pgadmin/static/js/Theme/index.jsx
@@ -106,23 +106,6 @@ basicSettings = createMuiTheme(basicSettings, {
         paddingRight: basicSettings.spacing(1.5),
       }
     },
-    MuiToggleButton: {
-      root: {
-        textTransform: 'none,',
-        padding: basicSettings.spacing(0.5, 2.5, 0.5, 0.5),
-        color: 'abc',
-        '&:hover':{
-          backgroundColor: 'abc',
-        },
-        '&$selected': {
-          color: 'abc',
-          backgroundColor: 'abc',
-          '&:hover':{
-            backgroundColor: 'abc',
-          }
-        }
-      }
-    },
     MuiAccordion: {
       root: {
         boxShadow: 'none',
@@ -172,6 +155,17 @@ basicSettings = createMuiTheme(basicSettings, {
       body1: {
         fontSize: '1em',
       }
+    },
+    MuiDialog: {
+      paper: {
+        margin: 0,
+      }
+    },
+    MuiTooltip: {
+      popper: {
+        top: 0,
+        zIndex: 9999,
+      }
     }
   },
   transitions: {
@@ -374,6 +368,7 @@ function getFinalTheme(baseTheme) {
       },
       MuiToggleButton: {
         root: {
+          padding: 'abc',
           paddingRight: baseTheme.spacing(2.5),
           paddingLeft: baseTheme.spacing(0.5),
           color: 'abc',
diff --git a/web/pgadmin/static/scss/_alertify.overrides.scss b/web/pgadmin/static/scss/_alertify.overrides.scss
index 40f6ac383..ddaaee18d 100644
--- a/web/pgadmin/static/scss/_alertify.overrides.scss
+++ b/web/pgadmin/static/scss/_alertify.overrides.scss
@@ -42,7 +42,7 @@
     }
 
     & .ajs-handle {
-      z-index: 5;
+      z-index: 1020;
     }
   }
 
diff --git a/web/pgadmin/tools/erd/__init__.py b/web/pgadmin/tools/erd/__init__.py
index 6d16cbd79..6978d5db1 100644
--- a/web/pgadmin/tools/erd/__init__.py
+++ b/web/pgadmin/tools/erd/__init__.py
@@ -532,6 +532,35 @@ def prequisite(trans_id, sgid, sid, did):
     )
 
 
+def translate_foreign_keys(tab_fks, tab_data, all_nodes):
+    """
+    This function will take the from table foreign keys and translate
+    it into non oid based format. It will allow creating FK sql even
+    if table is not already created.
+    :param tab_fks: Table foreign keyss
+    :param tab_data: Table data
+    :param all_nodes: All the nodes info from ERD
+    :return: Translated foreign key data
+    """
+    for tab_fk in tab_fks:
+        if 'columns' not in tab_fk:
+            continue
+        print(tab_data)
+        remote_table = all_nodes[tab_fk['columns'][0]['references']]
+        tab_fk['schema'] = tab_data['schema']
+        tab_fk['table'] = tab_data['name']
+        tab_fk['remote_schema'] = remote_table['schema']
+        tab_fk['remote_table'] = remote_table['name']
+
+        new_column = {
+            'local_column': tab_fk['columns'][0]['local_column'],
+            'referenced': tab_fk['columns'][0]['referenced']
+        }
+        tab_fk['columns'][0] = new_column
+
+    return tab_fks
+
+
 @blueprint.route('/sql/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
                  methods=["POST"],
                  endpoint='sql')
@@ -542,12 +571,16 @@ def sql(trans_id, sgid, sid, did):
     conn = _get_connection(sid, did, trans_id)
 
     sql = ''
-    for tab_key, tab_data in data.get('nodes', {}).items():
+    tab_foreign_keys = []
+    all_nodes = data.get('nodes', {})
+    for tab_key, tab_data in all_nodes.items():
+        tab_fks = tab_data.pop('foreign_key', [])
+        tab_foreign_keys.extend(translate_foreign_keys(tab_fks, tab_data, all_nodes))
         sql += '\n\n' + helper.get_table_sql(tab_data)
 
-    for link_key, link_data in data.get('links', {}).items():
-        link_sql, name = fkey_utils.get_sql(conn, link_data, None)
-        sql += '\n\n' + link_sql
+    for tab_fk in tab_foreign_keys:
+        fk_sql, name = fkey_utils.get_sql(conn, tab_fk, None)
+        sql += '\n\n' + fk_sql
 
     return make_json_response(
         data=sql,
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js b/web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js
index 23b592149..b63b1786a 100644
--- a/web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js
@@ -13,11 +13,16 @@
 import createEngine from '@projectstorm/react-diagrams';
 import {DagreEngine, PathFindingLinkFactory, PortModelAlignment} from '@projectstorm/react-diagrams';
 import { ZoomCanvasAction } from '@projectstorm/react-canvas-core';
+import _ from 'lodash';
 
 import {TableNodeFactory, TableNodeModel } from './nodes/TableNode';
 import {OneToManyLinkFactory, OneToManyLinkModel } from './links/OneToManyLink';
 import { OneToManyPortFactory } from './ports/OneToManyPort';
 import ERDModel from './ERDModel';
+import ForeignKeySchema from '../../../../../browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.ui';
+import diffArray from 'diff-arrays-of-objects';
+import TableSchema from '../../../../../browser/server_groups/servers/databases/schemas/tables/static/js/table.ui';
+import ColumnSchema from '../../../../../browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui';
 
 export default class ERDCore {
   constructor() {
@@ -66,8 +71,8 @@ export default class ERDCore {
           else if(e.function === 'showNote') {
             this.fireEvent({node: e.entity}, 'showNote', true);
           }
-          else if(e.function === 'editNode') {
-            this.fireEvent({node: e.entity}, 'editNode', true);
+          else if(e.function === 'editTable') {
+            this.fireEvent({node: e.entity}, 'editTable', true);
           }
           else if(e.function === 'nodeUpdated') {
             this.fireEvent({}, 'nodesUpdated', true);
@@ -232,6 +237,188 @@ export default class ERDCore {
     return newLink;
   }
 
+  removePortLinks(port) {
+    let links = port.getLinks();
+    Object.values(links).forEach((link)=>{
+      link.getTargetPort().remove();
+      link.getSourcePort().remove();
+      link.setSelected(false);
+      link.remove();
+    });
+  }
+
+  syncTableLinks(tableNode, oldTableData) {
+    let tableData = tableNode.getData();
+    let tableNodesDict = this.getModel().getNodesDict();
+
+    const addLink = (theFk)=>{
+      let newData = {
+        local_table_uid: tableNode.getID(),
+        local_column_attnum: undefined,
+        referenced_table_uid: theFk.references,
+        referenced_column_attnum: undefined,
+      };
+      let sourceNode = tableNodesDict[newData.referenced_table_uid];
+
+      newData.local_column_attnum = _.find(tableNode.getColumns(), (col)=>col.name==theFk.local_column).attnum;
+      newData.referenced_column_attnum = _.find(sourceNode.getColumns(), (col)=>col.name==theFk.referenced).attnum;
+
+      this.addLink(newData, 'onetomany');
+    };
+
+    const removeLink = (theFk)=>{
+      let attnum = _.find(tableNode.getColumns(), (col)=>col.name==theFk.local_column).attnum;
+      let existPort = tableNode.getPort(tableNode.getPortName(attnum));
+      if(existPort && existPort.getSubtype() == 'many') {
+        existPort.removeAllLinks();
+        tableNode.removePort(existPort);
+      }
+    };
+
+    const changeDiff = diffArray(
+      oldTableData?.foreign_key || [],
+      tableData?.foreign_key || [],
+      'cid'
+    );
+
+    changeDiff.added.forEach((theFk)=>{
+      addLink(theFk.columns[0]);
+    });
+    changeDiff.removed.forEach((theFk)=>{
+      removeLink(theFk.columns[0]);
+    });
+
+    if(changeDiff.updated.length > 0) {
+      for(const changedRow of changeDiff.updated) {
+        let rowIndx = _.findIndex(tableData.foreign_key, (f)=>f.cid==changedRow.cid);
+        const changeDiffCols = diffArray(
+          oldTableData.foreign_key[rowIndx].columns,
+          tableData.foreign_key[rowIndx].columns,
+          'cid'
+        );
+        if(changeDiffCols.removed.length > 0 || changeDiffCols.added.length > 0) {
+          removeLink(changeDiffCols.removed[0]);
+          addLink(changeDiffCols.added[0]);
+        }
+      }
+    }
+  }
+
+  addOneToManyLink(onetomanyData) {
+    let newFk = new ForeignKeySchema({}, {}, ()=>{}, {autoindex: false});
+    let tableNodesDict = this.getModel().getNodesDict();
+    let fkColumn = {};
+    let sourceNode = tableNodesDict[onetomanyData.referenced_table_uid];
+    let targetNode = tableNodesDict[onetomanyData.local_table_uid];
+
+    fkColumn.local_column = _.find(targetNode.getColumns(), (col)=>col.attnum==onetomanyData.local_column_attnum).name;
+    fkColumn.referenced = _.find(sourceNode.getColumns(), (col)=>col.attnum==onetomanyData.referenced_column_attnum).name;
+    fkColumn.references = onetomanyData.referenced_table_uid;
+    fkColumn.references_table_name = sourceNode.getData().name;
+
+    let tableData = targetNode.getData();
+    tableData.foreign_key = tableData.foreign_key || [];
+
+    let col = newFk.fkColumnSchema.getNewData(fkColumn);
+    tableData.foreign_key.push(
+      newFk.getNewData({
+        columns: [col],
+      })
+    );
+    targetNode.setData(tableData);
+    let newLink = this.addLink(onetomanyData, 'onetomany');
+    this.clearSelection();
+    newLink.setSelected(true);
+    this.repaint();
+  }
+
+  removeOneToManyLink(link) {
+    let linkData = link.getData();
+    let tableNode = this.getModel().getNodesDict()[linkData.local_table_uid];
+    let tableData = tableNode.getData();
+
+    let newForeingKeys = [];
+    tableData.foreign_key?.forEach((theFkRow)=>{
+      let theFk = theFkRow.columns[0];
+      let attnum = _.find(tableNode.getColumns(), (col)=>col.name==theFk.local_column).attnum;
+      /* Skip all those whose attnum matches to the link */
+      if(linkData.local_column_attnum != attnum) {
+        newForeingKeys.push(theFkRow);
+      }
+    });
+    tableData.foreign_key = newForeingKeys;
+    tableNode.setData(tableData);
+    link.getTargetPort().remove();
+    link.getSourcePort().remove();
+    link.setSelected(false);
+    link.remove();
+  }
+
+  addManyToManyLink(manytomanyData) {
+    let nodes = this.getModel().getNodesDict();
+    let leftNode = nodes[manytomanyData.left_table_uid];
+    let rightNode = nodes[manytomanyData.right_table_uid];
+
+    let tableObj = new TableSchema({}, {}, {
+      constraints:()=>{},
+      columns:()=>new ColumnSchema(()=>{}, {}, {}, {}),
+      vacuum_settings:()=>{},
+    }, ()=>{}, ()=>{}, ()=>{}, ()=>{});
+
+    let tableData = tableObj.getNewData({
+      name: `${leftNode.getData().name}_${rightNode.getData().name}`,
+      schema: leftNode.getData().schema,
+      columns: [tableObj.columnsSchema.getNewData({
+        ...leftNode.getColumnAt(manytomanyData.left_table_column_attnum),
+        'name': `${leftNode.getData().name}_${leftNode.getColumnAt(manytomanyData.left_table_column_attnum).name}`,
+        'attnum': 0,
+        'is_primary_key': false,
+      }),tableObj.columnsSchema.getNewData({
+        ...rightNode.getColumnAt(manytomanyData.right_table_column_attnum),
+        'name': `${rightNode.getData().name}_${rightNode.getColumnAt(manytomanyData.right_table_column_attnum).name}`,
+        'attnum': 1,
+        'is_primary_key': false,
+      })],
+    });
+
+    // let tableData = {
+    //   name: `${leftNode.getData().name}_${rightNode.getData().name}`,
+    //   schema: leftNode.getData().schema,
+    //   columns: [{
+    //     ...leftNode.getColumnAt(manytomanyData.left_table_column_attnum),
+    //     'name': `${leftNode.getData().name}_${leftNode.getColumnAt(manytomanyData.left_table_column_attnum).name}`,
+    //     'is_primary_key': false,
+    //     'attnum': 0,
+    //   },{
+    //     ...rightNode.getColumnAt(manytomanyData.right_table_column_attnum),
+    //     'name': `${rightNode.getData().name}_${rightNode.getColumnAt(manytomanyData.right_table_column_attnum).name}`,
+    //     'is_primary_key': false,
+    //     'attnum': 1,
+    //   }],
+    // };
+    let newNode = this.addNode(tableData);
+    this.clearSelection();
+    newNode.setSelected(true);
+
+    let linkData = {
+      local_table_uid: newNode.getID(),
+      local_column_attnum: newNode.getColumns()[0].attnum,
+      referenced_table_uid: manytomanyData.left_table_uid,
+      referenced_column_attnum : manytomanyData.left_table_column_attnum,
+    };
+    this.addOneToManyLink(linkData);
+
+    linkData = {
+      local_table_uid: newNode.getID(),
+      local_column_attnum: newNode.getColumns()[1].attnum,
+      referenced_table_uid: manytomanyData.right_table_uid,
+      referenced_column_attnum : manytomanyData.right_table_column_attnum,
+    };
+    this.addOneToManyLink(linkData);
+
+    this.repaint();
+  }
+
   serialize(version) {
     return {
       version: version||0,
@@ -246,65 +433,53 @@ export default class ERDCore {
   }
 
   serializeData() {
-    let nodes = {}, links = {};
+    let nodes = {};
     let nodesDict = this.getModel().getNodesDict();
 
     Object.keys(nodesDict).forEach((id)=>{
       nodes[id] = nodesDict[id].serializeData();
     });
 
-    this.getModel().getLinks().map((link)=>{
-      links[link.getID()] = link.serializeData(nodesDict);
-    });
-
-    /* Separate the links from nodes so that we don't have any dependancy issues */
     return {
       'nodes': nodes,
-      'links': links,
     };
   }
 
   deserializeData(data){
     let oidUidMap = {};
-    let uidFks = [];
-    data.forEach((node)=>{
-      let newData = {
-        name: node.name,
-        schema: node.schema,
-        description: node.description,
-        columns: node.columns,
-        primary_key: node.primary_key,
-      };
-      let newNode = this.addNode(newData);
-      oidUidMap[node.oid] = newNode.getID();
-      if(node.foreign_key) {
-        node.foreign_key.forEach((a_fk)=>{
-          uidFks.push({
-            uid: newNode.getID(),
-            data: a_fk.columns[0],
-          });
-        });
-      }
+
+    /* Add the nodes */
+    data.forEach((nodeData)=>{
+      let newNode = this.addNode(TableSchema.getErdSupportedData(nodeData));
+      oidUidMap[nodeData.oid] = newNode.getID();
     });
 
     /* Lets use the oidUidMap for creating the links */
-    uidFks.forEach((fkData)=>{
-      let tableNodesDict = this.getModel().getNodesDict();
-      let newData = {
-        local_table_uid: fkData.uid,
-        local_column_attnum: undefined,
-        referenced_table_uid: oidUidMap[fkData.data.references],
-        referenced_column_attnum: undefined,
-      };
-
-      let sourceNode = tableNodesDict[newData.referenced_table_uid];
-      let targetNode = tableNodesDict[newData.local_table_uid];
-
-      newData.local_column_attnum = _.find(targetNode.getColumns(), (col)=>col.name==fkData.data.local_column).attnum;
-      newData.referenced_column_attnum = _.find(sourceNode.getColumns(), (col)=>col.name==fkData.data.referenced).attnum;
-
-      this.addLink(newData, 'onetomany');
+    let tableNodesDict = this.getModel().getNodesDict();
+    _.forIn(tableNodesDict, (node, uid)=>{
+      let nodeData = node.getData();
+      if(nodeData.foreign_key) {
+        nodeData.foreign_key.forEach((theFk)=>{
+          delete theFk.oid;
+          theFk = theFk.columns[0];
+          theFk.references = oidUidMap[theFk.references];
+          let newData = {
+            local_table_uid: uid,
+            local_column_attnum: undefined,
+            referenced_table_uid: theFk.references,
+            referenced_column_attnum: undefined,
+          };
+          let sourceNode = tableNodesDict[newData.referenced_table_uid];
+          let targetNode = tableNodesDict[newData.local_table_uid];
+
+          newData.local_column_attnum = _.find(targetNode.getColumns(), (col)=>col.name==theFk.local_column).attnum;
+          newData.referenced_column_attnum = _.find(sourceNode.getColumns(), (col)=>col.name==theFk.referenced).attnum;
+
+          this.addLink(newData, 'onetomany');
+        });
+      }
     });
+
     setTimeout(this.dagreDistributeNodes.bind(this), 250);
   }
 
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/DialogWrapper.js b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/DialogWrapper.js
index cb0b1ff52..0ed761629 100644
--- a/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/DialogWrapper.js
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/DialogWrapper.js
@@ -7,152 +7,98 @@
 //
 //////////////////////////////////////////////////////////////
 
-import gettext from 'sources/gettext';
-import * as commonUtils from 'sources/utils';
+import React from 'react';
+import ReactDOM from 'react-dom';
+import SchemaView from '../../../../../../static/js/SchemaView';
 
 export default class DialogWrapper {
-  constructor(dialogContainerSelector, dialogTitle, typeOfDialog,
-    jquery, pgBrowser, alertify, backform, backgrid) {
-
+  constructor(dialogContainerSelector, dialogTitle, typeOfDialog, alertify, serverInfo) {
     this.dialogContainerSelector = dialogContainerSelector;
     this.dialogTitle = dialogTitle;
-    this.jquery = jquery;
-    this.pgBrowser = pgBrowser;
     this.alertify = alertify;
-    this.backform = backform;
-    this.backgrid = backgrid;
     this.typeOfDialog = typeOfDialog;
+    this.serverInfo = serverInfo;
+
+    let self = this;
+    this.hooks = {
+      onshow: ()=>{
+        self.createDialog(self.elements.content);
+      },
+      onclose: ()=>{
+        self.cleanupDialog(self.elements.content);
+      }
+    };
   }
 
-  main(title, dialogModel, okCallback) {
+  main(title, dialogSchema, okCallback) {
     this.set('title', title);
-    this.dialogModel = dialogModel;
+    this.dialogSchema = dialogSchema;
     this.okCallback = okCallback;
   }
 
   build() {
-    this.alertify.pgDialogBuild.apply(this);
-  }
-
-  disableOKButton() {
-    this.__internal.buttons[1].element.disabled = true;
+    this.elements.dialog.classList.add('erd-dialog');
   }
 
-  enableOKButton() {
-    this.__internal.buttons[1].element.disabled = false;
-  }
-
-  focusOnDialog(alertifyDialog) {
-    let backform_tab = this.jquery(alertifyDialog.elements.body).find('.backform-tab');
-    backform_tab.attr('tabindex', -1);
-    this.pgBrowser.keyboardNavigation.getDialogTabNavigator(this.jquery(alertifyDialog.elements.dialog));
-    let container = backform_tab.find('.tab-content:first > .tab-pane.active:first');
-
-    if(container.length === 0 && alertifyDialog.elements.content.innerHTML) {
-      container = this.jquery(alertifyDialog.elements.content);
-    }
-    commonUtils.findAndSetFocus(container);
+  prepare() {
+    /* If tooltip is mounted after alertify in dom and button is click,
+    alertify re-positions itself on DOM to come in focus. This makes it lose
+    the button click events. Making it modal along with following fixes things. */
+    this.elements.modal.style.maxHeight=0;
+    this.elements.modal.style.maxWidth='none';
+    this.elements.modal.style.overflow='visible';
+    this.elements.dimmer.style.display='none';
   }
 
   setup() {
     return {
-      buttons: [{
-        text: gettext('Cancel'),
-        key: 27,
-        className: 'btn btn-secondary fa fa-lg fa-times pg-alertify-button',
-        'data-btn-name': 'cancel',
-      }, {
-        text: gettext('OK'),
-        key: 13,
-        className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
-        'data-btn-name': 'ok',
-      }],
+      buttons: [],
       // Set options for dialog
       options: {
         title: this.dialogTitle,
         //disable both padding and overflow control.
         padding: !1,
         overflow: !1,
-        model: 0,
         resizable: true,
         maximizable: true,
         pinnable: false,
         closableByDimmer: false,
-        modal: false,
+        modal: true,
+        autoReset: false,
       },
     };
   }
 
-  prepare() {
-    const $container = this.jquery(this.dialogContainerSelector);
-    const dialog = this.createDialog($container);
-    dialog.render();
-    this.elements.content.innerHTML = '';
-    this.elements.content.appendChild($container.get(0));
-    this.jquery(this.elements.body.childNodes[0]).addClass(
-      'alertify_tools_dialog_properties obj_properties'
-    );
-    const statusBar = this.jquery(
-      '<div class=\'pg-prop-status-bar pg-prop-status-bar-absolute pg-el-xs-12 d-none\'>' +
-      '  <div class="error-in-footer"> ' +
-      '    <div class="d-flex px-2 py-1"> ' +
-      '      <div class="pr-2"> ' +
-      '        <i class="fa fa-exclamation-triangle text-danger" aria-hidden="true"></i> ' +
-      '      </div> ' +
-      '      <div class="alert-text" role="alert"></div> ' +
-      '       <div class="ml-auto close-error-bar"> ' +
-      '          <a aria-label="' + gettext('Close error bar') + '" class="close-error fa fa-times text-danger"></a> ' +
-      '        </div> ' +
-      '    </div> ' +
-      '  </div> ' +
-      '</div>').appendTo($container);
-
-    statusBar.find('.close-error').on('click', ()=>{
-      statusBar.addClass('d-none');
+  onSaveClick(isNew, data) {
+    return new Promise((resolve)=>{
+      this.okCallback(data);
+      this.close();
+      resolve();
     });
-
-    var onSessionInvalid = (msg) => {
-      statusBar.find('.alert-text').text(msg);
-      statusBar.removeClass('d-none');
-      this.disableOKButton();
-      return true;
-    };
-
-    var onSessionValidated = () => {
-      statusBar.find('.alert-text').text('');
-      statusBar.addClass('d-none');
-      this.enableOKButton();
-      return true;
-    };
-
-    this.dialogModel.on('pgadmin-session:valid', onSessionValidated);
-    this.dialogModel.on('pgadmin-session:invalid', onSessionInvalid);
-    this.dialogModel.startNewSession();
-    this.disableOKButton();
-    this.focusOnDialog(this);
   }
 
-  callback(event) {
-    if (this.wasOkButtonPressed(event)) {
-      this.okCallback(this.view.model.toJSON(true));
-    }
-  }
-
-  createDialog($container) {
-    let fields = this.backform.generateViewSchema(
-      null, this.dialogModel, 'create', null, null, true, null
-    );
-
-    this.view = new this.backform.Dialog({
-      el: $container,
-      model: this.dialogModel,
-      schema: fields,
-    });
-
-    return this.view;
+  createDialog(container) {
+    let self = this;
+    ReactDOM.render(
+      <SchemaView
+        formType={'dialog'}
+        getInitData={()=>Promise.resolve({})}
+        schema={this.dialogSchema}
+        viewHelperProps={{
+          mode: 'create',
+          keepCid: true,
+          serverInfo: this.serverInfo,
+        }}
+        onSave={this.onSaveClick.bind(this)}
+        onClose={()=>self.close()}
+        onDataChange={()=>{}}
+        hasSQL={false}
+        disableSqlHelp={true}
+        disableDialogHelp={true}
+      />, container);
   }
 
-  wasOkButtonPressed(event) {
-    return event.button['data-btn-name'] === 'ok';
+  cleanupDialog(container) {
+    ReactDOM.unmountComponentAtNode(container);
   }
 }
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/ManyToManyDialog.js b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/ManyToManyDialog.js
index 5a35243ab..dc2421b7e 100644
--- a/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/ManyToManyDialog.js
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/ManyToManyDialog.js
@@ -8,13 +8,48 @@
 //////////////////////////////////////////////////////////////
 
 import gettext from 'sources/gettext';
-import Backform from 'sources/backform.pgadmin';
 import Alertify from 'pgadmin.alertifyjs';
-import $ from 'jquery';
+import BaseUISchema from 'sources/SchemaView/base_schema.ui';
 
 import DialogWrapper from './DialogWrapper';
 import _ from 'lodash';
 
+class ManyToManySchema extends BaseUISchema {
+  constructor(fieldOptions={}, initValues={}) {
+    super({
+      left_table_uid: undefined,
+      left_table_column_attnum: undefined,
+      right_table_uid: undefined,
+      right_table_column_attnum: undefined,
+      ...initValues,
+    });
+    this.fieldOptions = fieldOptions;
+  }
+  get baseFields() {
+    return [{
+      id: 'left_table_uid', label: gettext('Local Table'),
+      type: 'select', readonly: true, controlProps: {allowClear: false},
+      options: this.fieldOptions.left_table_uid,
+    }, {
+      id: 'left_table_column_attnum', label: gettext('Local Column'),
+      type: 'select', options: this.fieldOptions.left_table_column_attnum,
+      controlProps: {allowClear: false}, noEmpty: true,
+    },{
+      id: 'right_table_uid', label: gettext('Referenced Table'),
+      type: 'select', options: this.fieldOptions.right_table_uid,
+      controlProps: {allowClear: false}, noEmpty: true,
+    },{
+      id: 'right_table_column_attnum', label: gettext('Referenced Column'),
+      controlProps: {allowClear: false}, deps: ['right_table_uid'],
+      type: (state)=>({
+        type: 'select',
+        options: state.right_table_uid ? ()=>this.fieldOptions.getRefColumns(state.right_table_uid) : [],
+        optionsReloadBasis: state.right_table_uid,
+      }),
+    }];
+  }
+}
+
 export default class ManyToManyDialog {
   constructor(pgBrowser) {
     this.pgBrowser = pgBrowser;
@@ -24,93 +59,29 @@ export default class ManyToManyDialog {
     return 'manytomany_dialog';
   }
 
-  getDataModel(attributes, tableNodesDict) {
-    const parseColumns = (columns)=>{
-      return columns.map((col)=>{
+  getUISchema(attributes, tableNodesDict) {
+    let tablesData = [];
+    _.forEach(tableNodesDict, (node, uid)=>{
+      let [schema, name] = node.getSchemaTableName();
+      tablesData.push({value: uid, label: `(${schema}) ${name}`, image: 'icon-table'});
+    });
+
+    return new ManyToManySchema({
+      left_table_uid: tablesData,
+      left_table_column_attnum: tableNodesDict[attributes.left_table_uid].getColumns().map((col)=>{
         return {
-          value: col.attnum, label: col.name,
+          value: col.attnum, label: col.name, 'image': 'icon-column',
         };
-      });
-    };
-
-    let dialogModel = this.pgBrowser.DataModel.extend({
-      defaults: {
-        left_table_uid: undefined,
-        left_table_column_attnum: undefined,
-        right_table_uid: undefined,
-        right_table_column_attnum: undefined,
+      }),
+      right_table_uid: tablesData,
+      getRefColumns: (uid)=>{
+        return tableNodesDict[uid].getColumns().map((col)=>{
+          return {
+            value: col.attnum, label: col.name, 'image': 'icon-column',
+          };
+        });
       },
-      schema: [{
-        id: 'left_table_uid', label: gettext('Left Table'),
-        type: 'select2', readonly: true,
-        options: ()=>{
-          let retOpts = [];
-          _.forEach(tableNodesDict, (node, uid)=>{
-            let [schema, name] = node.getSchemaTableName();
-            retOpts.push({value: uid, label: `(${schema}) ${name}`});
-          });
-          return retOpts;
-        },
-      }, {
-        id: 'left_table_column_attnum', label: gettext('Left table Column'),
-        type: 'select2', disabled: false, first_empty: false,
-        editable: true, options: (view)=>{
-          return parseColumns(tableNodesDict[view.model.get('left_table_uid')].getColumns());
-        },
-      },{
-        id: 'right_table_uid', label: gettext('Right Table'),
-        type: 'select2', disabled: false,
-        editable: true, options: (view)=>{
-          let retOpts = [];
-          _.forEach(tableNodesDict, (node, uid)=>{
-            if(uid === view.model.get('left_table_uid')) {
-              return;
-            }
-            let [schema, name] = node.getSchemaTableName();
-            retOpts.push({value: uid, label: `(${schema}) ${name}`});
-          });
-          return retOpts;
-        },
-      },{
-        id: 'right_table_column_attnum', label: gettext('Right table Column'),
-        type: 'select2', disabled: false, deps: ['right_table_uid'],
-        editable: true, options: (view)=>{
-          if(view.model.get('right_table_uid')) {
-            return parseColumns(tableNodesDict[view.model.get('right_table_uid')].getColumns());
-          }
-          return [];
-        },
-      }],
-      validate: function(keys) {
-        var msg = undefined;
-
-        // Nothing to validate
-        if (keys && keys.length == 0) {
-          this.errorModel.clear();
-          return null;
-        } else {
-          this.errorModel.clear();
-        }
-
-        if (_.isUndefined(this.get('left_table_column_attnum')) || this.get('left_table_column_attnum') == '') {
-          msg = gettext('Select the left table column.');
-          this.errorModel.set('left_table_column_attnum', msg);
-          return msg;
-        }
-        if (_.isUndefined(this.get('right_table_uid')) || this.get('right_table_uid') == '') {
-          msg = gettext('Select the right table.');
-          this.errorModel.set('right_table_uid', msg);
-          return msg;
-        }
-        if (_.isUndefined(this.get('right_table_column_attnum')) || this.get('right_table_column_attnum') == '') {
-          msg = gettext('Select the right table column.');
-          this.errorModel.set('right_table_column_attnum', msg);
-          return msg;
-        }
-      },
-    });
-
-    return new dialogModel(attributes);
+    }, attributes);
   }
 
   createOrGetDialog(title) {
@@ -122,19 +93,16 @@ export default class ManyToManyDialog {
           `<div class="${dialogName}"></div>`,
           title,
           null,
-          $,
-          this.pgBrowser,
           Alertify,
-          Backform
         );
       });
     }
     return Alertify[dialogName];
   }
 
-  show(title, attributes, tablesData, sVersion, callback) {
+  show(title, attributes, tablesData, serverInfo, callback) {
     let dialogTitle = title || gettext('Unknown');
-    const dialog = this.createOrGetDialog('manytomany_dialog');
-    dialog(dialogTitle, this.getDataModel(attributes, tablesData), callback).resizeTo(this.pgBrowser.stdW.sm, this.pgBrowser.stdH.md);
+    const dialog = this.createOrGetDialog('manytomany_dialog', serverInfo);
+    dialog(dialogTitle, this.getUISchema(attributes, tablesData), callback).resizeTo(this.pgBrowser.stdW.sm, this.pgBrowser.stdH.md);
   }
 }
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/OneToManyDialog.js b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/OneToManyDialog.js
index efcc5e63b..7cb45810d 100644
--- a/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/OneToManyDialog.js
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/OneToManyDialog.js
@@ -8,13 +8,48 @@
 //////////////////////////////////////////////////////////////
 
 import gettext from 'sources/gettext';
-import Backform from 'sources/backform.pgadmin';
 import Alertify from 'pgadmin.alertifyjs';
-import $ from 'jquery';
+import BaseUISchema from 'sources/SchemaView/base_schema.ui';
 
 import DialogWrapper from './DialogWrapper';
 import _ from 'lodash';
 
+class OneToManySchema extends BaseUISchema {
+  constructor(fieldOptions={}, initValues={}) {
+    super({
+      local_table_uid: undefined,
+      local_column_attnum: undefined,
+      referenced_table_uid: undefined,
+      referenced_column_attnum: undefined,
+      ...initValues,
+    });
+    this.fieldOptions = fieldOptions;
+  }
+  get baseFields() {
+    return [{
+      id: 'local_table_uid', label: gettext('Local Table'),
+      type: 'select', readonly: true, controlProps: {allowClear: false},
+      options: this.fieldOptions.local_table_uid,
+    }, {
+      id: 'local_column_attnum', label: gettext('Local Column'),
+      type: 'select', options: this.fieldOptions.local_column_attnum,
+      controlProps: {allowClear: false}, noEmpty: true,
+    },{
+      id: 'referenced_table_uid', label: gettext('Referenced Table'),
+      type: 'select', options: this.fieldOptions.referenced_table_uid,
+      controlProps: {allowClear: false}, noEmpty: true,
+    },{
+      id: 'referenced_column_attnum', label: gettext('Referenced Column'),
+      controlProps: {allowClear: false}, deps: ['referenced_table_uid'], noEmpty: true,
+      type: (state)=>({
+        type: 'select',
+        options: state.referenced_table_uid ? ()=>this.fieldOptions.getRefColumns(state.referenced_table_uid) : [],
+        optionsReloadBasis: state.referenced_table_uid,
+      }),
+    }];
+  }
+}
+
 export default class OneToManyDialog {
   constructor(pgBrowser) {
     this.pgBrowser = pgBrowser;
@@ -24,93 +59,32 @@ export default class OneToManyDialog {
     return 'onetomany_dialog';
   }
 
-  getDataModel(attributes, tableNodesDict) {
-    const parseColumns = (columns)=>{
-      return columns.map((col)=>{
+  getUISchema(attributes, tableNodesDict) {
+    let tablesData = [];
+    _.forEach(tableNodesDict, (node, uid)=>{
+      let [schema, name] = node.getSchemaTableName();
+      tablesData.push({value: uid, label: `(${schema}) ${name}`, image: 'icon-table'});
+    });
+
+    return new OneToManySchema({
+      local_table_uid: tablesData,
+      local_column_attnum: tableNodesDict[attributes.local_table_uid].getColumns().map((col)=>{
         return {
-          value: col.attnum, label: col.name,
+          value: col.attnum, label: col.name, 'image': 'icon-column',
         };
-      });
-    };
-
-    let dialogModel = this.pgBrowser.DataModel.extend({
-      defaults: {
-        local_table_uid: undefined,
-        local_column_attnum: undefined,
-        referenced_table_uid: undefined,
-        referenced_column_attnum: undefined,
+      }),
+      referenced_table_uid: tablesData,
+      getRefColumns: (uid)=>{
+        return tableNodesDict[uid].getColumns().map((col)=>{
+          return {
+            value: col.attnum, label: col.name, 'image': 'icon-column',
+          };
+        });
       },
-      schema: [{
-        id: 'local_table_uid', label: gettext('Local Table'),
-        type: 'select2', readonly: true,
-        options: ()=>{
-          let retOpts = [];
-          _.forEach(tableNodesDict, (node, uid)=>{
-            let [schema, name] = node.getSchemaTableName();
-            retOpts.push({value: uid, label: `(${schema}) ${name}`});
-          });
-          return retOpts;
-        },
-      }, {
-        id: 'local_column_attnum', label: gettext('Local Column'),
-        type: 'select2', disabled: false, first_empty: false,
-        editable: true, options: (view)=>{
-          return parseColumns(tableNodesDict[view.model.get('local_table_uid')].getColumns());
-        },
-      },{
-        id: 'referenced_table_uid', label: gettext('Referenced Table'),
-        type: 'select2', disabled: false,
-        editable: true, options: ()=>{
-          let retOpts = [];
-          _.forEach(tableNodesDict, (node, uid)=>{
-            let [schema, name] = node.getSchemaTableName();
-            retOpts.push({value: uid, label: `(${schema}) ${name}`});
-          });
-          return retOpts;
-        },
-      },{
-        id: 'referenced_column_attnum', label: gettext('Referenced Column'),
-        type: 'select2', disabled: false, deps: ['referenced_table_uid'],
-        editable: true, options: (view)=>{
-          if(view.model.get('referenced_table_uid')) {
-            return parseColumns(tableNodesDict[view.model.get('referenced_table_uid')].getColumns());
-          }
-          return [];
-        },
-      }],
-      validate: function(keys) {
-        var msg = undefined;
-
-        // Nothing to validate
-        if (keys && keys.length == 0) {
-          this.errorModel.clear();
-          return null;
-        } else {
-          this.errorModel.clear();
-        }
-
-        if (_.isUndefined(this.get('local_column_attnum')) || this.get('local_column_attnum') == '') {
-          msg = gettext('Select the local column.');
-          this.errorModel.set('local_column_attnum', msg);
-          return msg;
-        }
-        if (_.isUndefined(this.get('referenced_table_uid')) || this.get('referenced_table_uid') == '') {
-          msg = gettext('Select the referenced table.');
-          this.errorModel.set('referenced_table_uid', msg);
-          return msg;
-        }
-        if (_.isUndefined(this.get('referenced_column_attnum')) || this.get('referenced_column_attnum') == '') {
-          msg = gettext('Select the referenced table column.');
-          this.errorModel.set('referenced_column_attnum', msg);
-          return msg;
-        }
-      },
-    });
-
-    return new dialogModel(attributes);
+    }, attributes);
   }
 
-  createOrGetDialog(title) {
+  createOrGetDialog(title, sVersion) {
     const dialogName = this.dialogName();
 
     if (!Alertify[dialogName]) {
@@ -119,19 +93,17 @@ export default class OneToManyDialog {
           `<div class="${dialogName}"></div>`,
           title,
           null,
-          $,
-          this.pgBrowser,
           Alertify,
-          Backform
+          sVersion
         );
       });
     }
     return Alertify[dialogName];
   }
 
-  show(title, attributes, tablesData, sVersion, callback) {
+  show(title, attributes, tablesData, serverInfo, callback) {
     let dialogTitle = title || gettext('Unknown');
-    const dialog = this.createOrGetDialog('onetomany_dialog');
-    dialog(dialogTitle, this.getDataModel(attributes, tablesData), callback).resizeTo(this.pgBrowser.stdW.sm, this.pgBrowser.stdH.md);
+    const dialog = this.createOrGetDialog('onetomany_dialog', serverInfo);
+    dialog(dialogTitle, this.getUISchema(attributes, tablesData), callback).resizeTo(this.pgBrowser.stdW.sm, this.pgBrowser.stdH.md);
   }
 }
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/TableDialog.js b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/TableDialog.js
index 3375fdc6f..eafc881e7 100644
--- a/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/TableDialog.js
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/TableDialog.js
@@ -8,37 +8,22 @@
 //////////////////////////////////////////////////////////////
 
 import gettext from 'sources/gettext';
-import Backgrid from 'sources/backgrid.pgadmin';
-import Backform from 'sources/backform.pgadmin';
 import Alertify from 'pgadmin.alertifyjs';
-import $ from 'jquery';
 import _ from 'lodash';
+import BaseUISchema from 'sources/SchemaView/base_schema.ui';
 
 import DialogWrapper from './DialogWrapper';
+import TableSchema, { ConstraintsSchema } from '../../../../../../browser/server_groups/servers/databases/schemas/tables/static/js/table.ui';
+import ColumnSchema from '../../../../../../browser/server_groups/servers/databases/schemas/tables/columns/static/js/column.ui';
+import ForeignKeySchema from '../../../../../../browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/js/foreign_key.ui';
 
-export function transformToSupported(data) {
-  /* Table fields */
-  data = _.pick(data, ['oid', 'name', 'schema', 'description', 'columns', 'primary_key', 'foreign_key']);
-
-  /* Columns */
-  data['columns'] = data['columns'].map((column)=>{
-    return _.pick(column,[
-      'name','description','attowner','attnum','cltype','min_val_attlen','min_val_attprecision','max_val_attlen',
-      'max_val_attprecision', 'is_primary_key','attnotnull','attlen','attprecision','attidentity','colconstype',
-      'seqincrement','seqstart','seqmin','seqmax','seqcache','seqcycle',
-    ]);
-  });
-
-  /* Primary key */
-  data['primary_key'] = data['primary_key'].map((primary_key)=>{
-    primary_key = _.pick(primary_key, ['columns']);
-    primary_key['columns'] = primary_key['columns'].map((column)=>{
-      return _.pick(column, ['column']);
-    });
-    return primary_key;
-  });
+class EmptySchema extends BaseUISchema {
+  get baseFields() {
+    return [];
+  }
+  changeColumnOptions() {
 
-  return data;
+  }
 }
 
 export default class TableDialog {
@@ -47,682 +32,81 @@ export default class TableDialog {
   }
 
   dialogName() {
-    return 'entity_dialog';
+    return 'table_dialog';
   }
 
-  getDataModel(attributes, isNew, allTables, colTypes, schemas, sVersion) {
-    let dialogObj = this;
-    let columnsModel = this.pgBrowser.DataModel.extend({
-      idAttribute: 'attnum',
-      defaults: {
-        name: undefined,
-        description: undefined,
-        attowner: undefined,
-        attnum: undefined,
-        cltype: undefined,
-        min_val_attlen: undefined,
-        min_val_attprecision: undefined,
-        max_val_attlen: undefined,
-        max_val_attprecision: undefined,
-        is_primary_key: false,
-        attnotnull: false,
-        attlen: null,
-        attprecision: null,
-        attidentity: 'a',
-        colconstype: 'n',
-        seqincrement: undefined,
-        seqstart: undefined,
-        seqmin: undefined,
-        seqmax: undefined,
-        seqcache: undefined,
-        seqcycle: undefined,
-      },
-      initialize: function(attrs) {
-        if (_.size(attrs) !== 0) {
-          this.set({
-            'old_attidentity': this.get('attidentity'),
-          }, {silent: true});
-        }
-        dialogObj.pgBrowser.DataModel.prototype.initialize.apply(this, arguments);
-
-        if(!this.get('cltype') && colTypes.length > 0) {
-          this.set({
-            'cltype': colTypes[0]['value'],
-          }, {silent: true});
-        }
-      },
-      schema: [{
-        id: 'name', label: gettext('Name'), cell: 'string',
-        type: 'text', disabled: false,
-        cellHeaderClasses: 'width_percent_30',
-        editable: true,
-      }, {
-        // Need to show this field only when creating new table
-        // [in SubNode control]
-        id: 'is_primary_key', label: gettext('Primary key?'),
-        cell: Backgrid.Extension.TableChildSwitchCell, type: 'switch',
-        deps: ['name'], cellHeaderClasses: 'width_percent_5',
-        options: {
-          onText: gettext('Yes'), offText: gettext('No'),
-          onColor: 'success', offColor: 'ternary',
-        },
-        visible: function () {
-          return true;
-        },
-        disabled: false,
-        editable: true,
-      }, {
-        id: 'description', label: gettext('Comment'), cell: 'string', type: 'multiline',
-      }, {
-        id: 'cltype', label: gettext('Data type'),
-        cell: 'select2',
-        type: 'select2', disabled: false,
-        control: 'select2',
-        cellHeaderClasses: 'width_percent_30',
-        select2: { allowClear: false, first_empty: false }, group: gettext('Definition'),
-        options: function () {
-          return colTypes;
-        },
-      }, {
-        id: 'attlen', label: gettext('Length/Precision'), cell: Backgrid.Extension.IntegerDepCell,
-        deps: ['cltype'], type: 'int', group: gettext('Definition'), cellHeaderClasses: 'width_percent_20',
-        disabled: function (m) {
-          var of_type = m.get('cltype'),
-            flag = true;
-          _.each(colTypes, function (o) {
-            if (of_type == o.value) {
-              if (o.length) {
-                m.set('min_val_attlen', o.min_val, { silent: true });
-                m.set('max_val_attlen', o.max_val, { silent: true });
-                flag = false;
-              }
-            }
-          });
-
-          flag && setTimeout(function () {
-            if (m.get('attlen')) {
-              m.set('attlen', null);
-            }
-          }, 10);
-
-          return flag;
-        },
-        editable: function (m) {
-          var of_type = m.get('cltype'),
-            flag = false;
-          _.each(colTypes, function (o) {
-            if (of_type == o.value) {
-              if (o.length) {
-                m.set('min_val_attlen', o.min_val, { silent: true });
-                m.set('max_val_attlen', o.max_val, { silent: true });
-                flag = true;
-              }
-            }
-          });
-
-          !flag && setTimeout(function () {
-            if (m.get('attlen')) {
-              m.set('attlen', null);
-            }
-          }, 10);
-
-          return flag;
-        },
-      }, {
-        id: 'attprecision', label: gettext('Scale'), cell: Backgrid.Extension.IntegerDepCell,
-        deps: ['cltype'], type: 'int', group: gettext('Definition'), cellHeaderClasses: 'width_percent_20',
-        disabled: function (m) {
-          var of_type = m.get('cltype'),
-            flag = true;
-          _.each(colTypes, function (o) {
-            if (of_type == o.value) {
-              if (o.precision) {
-                m.set('min_val_attprecision', 0, { silent: true });
-                m.set('max_val_attprecision', o.max_val, { silent: true });
-                flag = false;
-              }
-            }
-          });
-
-          flag && setTimeout(function () {
-            if (m.get('attprecision')) {
-              m.set('attprecision', null);
-            }
-          }, 10);
-          return flag;
-        },
-        editable: function (m) {
-          if (!colTypes) {
-            // datatypes not loaded yet, may be this call is from CallByNeed from backgrid cell initialize.
-            return true;
-          }
-
-          var of_type = m.get('cltype'),
-            flag = false;
-          _.each(colTypes, function (o) {
-            if (of_type == o.value) {
-              if (o.precision) {
-                m.set('min_val_attprecision', 0, { silent: true });
-                m.set('max_val_attprecision', o.max_val, { silent: true });
-                flag = true;
-              }
-            }
-          });
-
-          !flag && setTimeout(function () {
-            if (m.get('attprecision')) {
-              m.set('attprecision', null);
-            }
-          }, 10);
-
-          return flag;
-        },
-      }, {
-        id: 'attnotnull', label: gettext('Not NULL?'), cell: 'switch',
-        type: 'switch', cellHeaderClasses: 'width_percent_20',
-        group: gettext('Constraints'),
-        options: { onText: gettext('Yes'), offText: gettext('No'), onColor: 'success', offColor: 'ternary' },
-        disabled: function(m) {
-          if (m.get('colconstype') == 'i') {
-            setTimeout(function () {
-              m.set('attnotnull', true);
-            }, 10);
-          }
-          return false;
-        },
-      }, {
-        id: 'colconstype',
-        label: gettext('Type'),
-        cell: 'string',
-        type: 'radioModern',
-        controlsClassName: 'pgadmin-controls col-12 col-sm-9',
-        controlLabelClassName: 'control-label col-sm-3 col-12',
-        group: gettext('Constraints'),
-        options: function() {
-          var opt_array = [
-            {'label': gettext('NONE'), 'value': 'n'},
-            {'label': gettext('IDENTITY'), 'value': 'i'},
-          ];
-
-          if (sVersion >= 120000) {
-            opt_array.push({
-              'label': gettext('GENERATED'),
-              'value': 'g',
-            });
-          }
-
-          return opt_array;
-        },
-        disabled: false,
-        visible: function() {
-          if (sVersion >= 100000) {
-            return true;
-          }
-          return false;
-        },
-      }, {
-        id: 'attidentity', label: gettext('Identity'), control: 'select2',
-        cell: 'select2',
-        select2: {placeholder: 'Select identity', allowClear: false, width: '100%'},
-        group: gettext('Constraints'),
-        'options': [
-          {label: gettext('ALWAYS'), value: 'a'},
-          {label: gettext('BY DEFAULT'), value: 'd'},
-        ],
-        deps: ['colconstype'],
-        visible: function(m) {
-          if (sVersion >= 100000 && m.isTypeIdentity(m)) {
-            return true;
-          }
-          return false;
-        },
-        disabled: function() {
-          return false;
-        },
-      }, {
-        id: 'seqincrement', label: gettext('Increment'), type: 'int',
-        mode: ['properties', 'create', 'edit'], group: gettext('Constraints'),
-        min: 1, deps: ['attidentity', 'colconstype'], disabled: 'isIdentityColumn',
-        visible: 'isTypeIdentity',
-      },{
-        id: 'seqstart', label: gettext('Start'), type: 'int',
-        mode: ['properties', 'create', 'edit'], group: gettext('Constraints'),
-        disabled: function(m) {
-          let isIdentity = m.get('attidentity');
-          if(!_.isUndefined(isIdentity) && !_.isNull(isIdentity) && !_.isEmpty(isIdentity))
-            return false;
-          return true;
-        }, deps: ['attidentity', 'colconstype'],
-        visible: 'isTypeIdentity',
-      },{
-        id: 'seqmin', label: gettext('Minimum'), type: 'int',
-        mode: ['properties', 'create', 'edit'], group: gettext('Constraints'),
-        deps: ['attidentity', 'colconstype'], disabled: 'isIdentityColumn',
-        visible: 'isTypeIdentity',
-      },{
-        id: 'seqmax', label: gettext('Maximum'), type: 'int',
-        mode: ['properties', 'create', 'edit'], group: gettext('Constraints'),
-        deps: ['attidentity', 'colconstype'], disabled: 'isIdentityColumn',
-        visible: 'isTypeIdentity',
-      },{
-        id: 'seqcache', label: gettext('Cache'), type: 'int',
-        mode: ['properties', 'create', 'edit'], group: gettext('Constraints'),
-        min: 1, deps: ['attidentity', 'colconstype'], disabled: 'isIdentityColumn',
-        visible: 'isTypeIdentity',
-      },{
-        id: 'seqcycle', label: gettext('Cycled'), type: 'switch',
-        mode: ['properties', 'create', 'edit'], group: gettext('Constraints'),
-        deps: ['attidentity', 'colconstype'], disabled: 'isIdentityColumn',
-        visible: 'isTypeIdentity',
-      }],
-      validate: function(keys) {
-        var msg = undefined;
-
-        // Nothing to validate
-        if (keys && keys.length == 0) {
-          this.errorModel.clear();
-          return null;
-        } else {
-          this.errorModel.clear();
-        }
-
-        if (_.isUndefined(this.get('name'))
-            || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
-          msg = gettext('Column name cannot be empty.');
-          this.errorModel.set('name', msg);
-          return msg;
-        }
-
-        if (_.isUndefined(this.get('cltype'))
-            || String(this.get('cltype')).replace(/^\s+|\s+$/g, '') == '') {
-          msg = gettext('Column type cannot be empty.');
-          this.errorModel.set('cltype', msg);
-          return msg;
-        }
-
-        if (!_.isUndefined(this.get('cltype'))
-              && !_.isUndefined(this.get('attlen'))
-              && !_.isNull(this.get('attlen'))
-              && this.get('attlen') !== '') {
-          // Validation for Length field
-          if (this.get('attlen') < this.get('min_val_attlen'))
-            msg = gettext('Length/Precision should not be less than: ') + this.get('min_val_attlen');
-          if (this.get('attlen') > this.get('max_val_attlen'))
-            msg = gettext('Length/Precision should not be greater than: ') + this.get('max_val_attlen');
-          // If we have any error set then throw it to user
-          if(msg) {
-            this.errorModel.set('attlen', msg);
-            return msg;
-          }
-        }
-
-        if (!_.isUndefined(this.get('cltype'))
-              && !_.isUndefined(this.get('attprecision'))
-              && !_.isNull(this.get('attprecision'))
-              && this.get('attprecision') !== '') {
-          // Validation for precision field
-          if (this.get('attprecision') < this.get('min_val_attprecision'))
-            msg = gettext('Scale should not be less than: ') + this.get('min_val_attprecision');
-          if (this.get('attprecision') > this.get('max_val_attprecision'))
-            msg = gettext('Scale should not be greater than: ') + this.get('max_val_attprecision');
-          // If we have any error set then throw it to user
-          if(msg) {
-            this.errorModel.set('attprecision', msg);
-            return msg;
-          }
-        }
-
-        var minimum = this.get('seqmin'),
-          maximum = this.get('seqmax'),
-          start = this.get('seqstart');
-
-        if (!this.isNew() && this.get('colconstype') == 'i' &&
-          (this.get('old_attidentity') == 'a' || this.get('old_attidentity') == 'd') &&
-          (this.get('attidentity') == 'a' || this.get('attidentity') == 'd')) {
-          if (_.isUndefined(this.get('seqincrement'))
-            || String(this.get('seqincrement')).replace(/^\s+|\s+$/g, '') == '') {
-            msg = gettext('Increment value cannot be empty.');
-            this.errorModel.set('seqincrement', msg);
-            return msg;
-          } else {
-            this.errorModel.unset('seqincrement');
-          }
-
-          if (_.isUndefined(this.get('seqmin'))
-            || String(this.get('seqmin')).replace(/^\s+|\s+$/g, '') == '') {
-            msg = gettext('Minimum value cannot be empty.');
-            this.errorModel.set('seqmin', msg);
-            return msg;
-          } else {
-            this.errorModel.unset('seqmin');
-          }
-
-          if (_.isUndefined(this.get('seqmax'))
-            || String(this.get('seqmax')).replace(/^\s+|\s+$/g, '') == '') {
-            msg = gettext('Maximum value cannot be empty.');
-            this.errorModel.set('seqmax', msg);
-            return msg;
-          } else {
-            this.errorModel.unset('seqmax');
-          }
-
-          if (_.isUndefined(this.get('seqcache'))
-            || String(this.get('seqcache')).replace(/^\s+|\s+$/g, '') == '') {
-            msg = gettext('Cache value cannot be empty.');
-            this.errorModel.set('seqcache', msg);
-            return msg;
-          } else {
-            this.errorModel.unset('seqcache');
-          }
-        }
-        var min_lt = gettext('Minimum value must be less than maximum value.'),
-          start_lt = gettext('Start value cannot be less than minimum value.'),
-          start_gt = gettext('Start value cannot be greater than maximum value.');
-
-        if (_.isEmpty(minimum) || _.isEmpty(maximum))
-          return null;
-
-        if ((minimum == 0 && maximum == 0) ||
-            (parseInt(minimum, 10) >= parseInt(maximum, 10))) {
-          this.errorModel.set('seqmin', min_lt);
-          return min_lt;
-        } else {
-          this.errorModel.unset('seqmin');
-        }
-
-        if (start && minimum && parseInt(start) < parseInt(minimum)) {
-          this.errorModel.set('seqstart', start_lt);
-          return start_lt;
-        } else {
-          this.errorModel.unset('seqstart');
-        }
-
-        if (start && maximum && parseInt(start) > parseInt(maximum)) {
-          this.errorModel.set('seqstart', start_gt);
-          return start_gt;
-        } else {
-          this.errorModel.unset('seqstart');
-        }
-
-        return null;
-      },
-      // Check whether the column is identity column or not
-      isIdentityColumn: function(m) {
-        let isIdentity = m.get('attidentity');
-        if(!_.isUndefined(isIdentity) && !_.isNull(isIdentity) && !_.isEmpty(isIdentity))
-          return false;
-        return true;
-      },
-      // Check whether the column is a identity column
-      isTypeIdentity: function(m) {
-        let colconstype = m.get('colconstype');
-        if (!_.isUndefined(colconstype) && !_.isNull(colconstype) && colconstype == 'i') {
-          return true;
-        }
-        return false;
-      },
-      // Check whether the column is a generated column
-      isTypeGenerated: function(m) {
-        let colconstype = m.get('colconstype');
-        if (!_.isUndefined(colconstype) && !_.isNull(colconstype) && colconstype == 'g') {
-          return true;
-        }
-        return false;
-      },
-    });
-
-    const formatSchemaItem = function(opt) {
-      if (!opt.id) {
-        return opt.text;
-      }
-
-      var optimage = $(opt.element).data('image');
-
-      if (!optimage) {
-        return opt.text;
-      } else {
-        return $('<span></span>').append(
-          $('<span></span>', {
-            class: 'wcTabIcon ' + optimage,
-          })
-        ).append($('<span></span>').text(opt.text));
-      }
-    };
-
-    let dialogModel = this.pgBrowser.DataModel.extend({
-      defaults: {
-        name: undefined,
-        schema: undefined,
-        description: undefined,
-        columns: [],
-        primary_key: [],
-      },
-      initialize: function() {
-        dialogObj.pgBrowser.DataModel.prototype.initialize.apply(this, arguments);
-
-        if(!this.get('schema') && schemas.length > 0) {
-          this.set({
-            'schema': schemas[0]['name'],
-          }, {silent: true});
-        }
+  getUISchema(attributes, isNew, tableNodesDict, colTypes, schemas) {
+    let treeNodeInfo = undefined;
+
+    let columnSchema = new ColumnSchema(
+      ()=>{},
+      treeNodeInfo,
+      ()=>colTypes,
+      ()=>[],
+      ()=>[],
+      true,
+    );
+
+    return new TableSchema(
+      {
+        relowner: [],
+        schema: schemas.map((schema)=>{
+          return {
+            'value': schema['name'],
+            'image': 'icon-schema',
+            'label': schema['name'],
+          };
+        }),
+        spcname: [],
+        coll_inherits: [],
+        typname: [],
+        like_relation: [],
       },
-      schema: [{
-        id: 'name', label: gettext('Name'), type: 'text', disabled: false,
-      },{
-        id: 'schema', label: gettext('Schema'), type: 'text',
-        control: 'select2', select2: {
-          allowClear: false, first_empty: false,
-          templateResult: formatSchemaItem,
-          templateSelection: formatSchemaItem,
-        },
-        options: function () {
-          return schemas.map((schema)=>{
-            return {
-              'value': schema['name'],
-              'image': 'icon-schema',
-              'label': schema['name'],
-            };
-          });
-        },
-        filter: function(d) {
-          // If schema name start with pg_* then we need to exclude them
-          if(d && d.label.match(/^pg_/))
-          {
-            return false;
-          }
-          return true;
-        },
-      },{
-        id: 'description', label: gettext('Comment'), type: 'multiline',
-      },{
-        id: 'columns', label: gettext('Columns'), type: 'collection', mode: ['create'],
-        group: gettext('Columns'),
-        model: columnsModel,
-        subnode: columnsModel,
-        disabled: false,
-        uniqueCol : ['name'],
-        columns : ['name' , 'cltype', 'attlen', 'attprecision', 'attnotnull', 'is_primary_key'],
-        control: Backform.UniqueColCollectionControl.extend({
-          initialize: function() {
-
-            Backform.UniqueColCollectionControl.prototype.initialize.apply(this, arguments);
-            var self = this,
-              collection = self.model.get(self.field.get('name'));
-
-            if(collection.isEmpty()) {
-              self.last_attnum = -1;
-            } else {
-              var lastCol = collection.max(function(col) {
-                return col.get('attnum');
+      treeNodeInfo,
+      {
+        columns: ()=>columnSchema,
+        vacuum_settings: ()=>new EmptySchema(),
+        constraints: ()=>new ConstraintsSchema(
+          treeNodeInfo,
+          ()=>new ForeignKeySchema({
+            local_column: [],
+            references: ()=>{
+              let retOpts = [];
+              _.forEach(tableNodesDict, (node, uid)=>{
+                let [schema, name] = node.getSchemaTableName();
+                retOpts.push({value: uid, label: `(${schema}) ${name}`});
               });
-              self.last_attnum = lastCol.get('attnum');
+              return retOpts;
             }
-
-            collection.on('change:is_primary_key', function(m) {
-              var primary_key_coll = self.model.get('primary_key'),
-                column_name = m.get('name'),
-                primary_key, primary_key_column_coll;
-
-              if(m.get('is_primary_key')) {
-                // Add column to primary key.
-                if (primary_key_coll.length < 1) {
-                  primary_key = new (primary_key_coll.model)({}, {
-                    top: self.model,
-                    collection: primary_key_coll,
-                    handler: primary_key_coll,
-                  });
-                  primary_key_coll.add(primary_key);
-                } else {
-                  primary_key = primary_key_coll.first();
-                }
-
-                primary_key_column_coll = primary_key.get('columns');
-                var primary_key_column_exist = primary_key_column_coll.where({column:column_name});
-
-                if (primary_key_column_exist.length == 0) {
-                  var primary_key_column = new (
-                    primary_key_column_coll.model
-                  )({column: column_name}, {
-                    silent: true,
-                    top: self.model,
-                    collection: primary_key_coll,
-                    handler: primary_key_coll,
-                  });
-
-                  primary_key_column_coll.add(primary_key_column);
-                }
-
-                primary_key_column_coll.trigger(
-                  'pgadmin:multicolumn:updated', primary_key_column_coll
-                );
-              } else {
-                // remove column from primary key.
-                if (primary_key_coll.length > 0) {
-                  primary_key = primary_key_coll.first();
-                  // Do not alter existing primary key columns.
-                  if (!_.isUndefined(primary_key.get('oid'))) {
-                    return;
-                  }
-
-                  primary_key_column_coll = primary_key.get('columns');
-                  var removedCols = primary_key_column_coll.where({column:column_name});
-                  if (removedCols.length > 0) {
-                    primary_key_column_coll.remove(removedCols);
-                    _.each(removedCols, function(local_model) {
-                      local_model.destroy();
-                    });
-                    if (primary_key_column_coll.length == 0) {
-                      /* Ideally above line of code should be "primary_key_coll.reset()".
-                       * But our custom DataCollection (extended from Backbone collection in datamodel.js)
-                       * does not respond to reset event, it only supports add, remove, change events.
-                       * And hence no custom event listeners/validators get called for reset event.
-                       */
-                      primary_key_coll.remove(primary_key_coll.first());
-                    }
-                  }
-                  primary_key_column_coll.trigger('pgadmin:multicolumn:updated', primary_key_column_coll);
-                }
-              }
-            });
-
-            collection.on('change:name', function(m) {
-              let primary_key = self.model.get('primary_key').first();
-              if(primary_key) {
-                let updatedCols = primary_key.get('columns').where(
-                  {column: m.previous('name')}
-                );
-                if (updatedCols.length > 0) {
-                  /*
-                  * Table column name has changed so update
-                  * column name in primary key as well.
-                  */
-                  updatedCols[0].set(
-                    {'column': m.get('name')},
-                    {silent: true});
-                }
-              }
-            });
-
-            collection.on('remove', function(m) {
-              let primary_key = self.model.get('primary_key').first();
-              if(primary_key) {
-                let removedCols = primary_key.get('columns').where(
-                  {column: m.get('name')}
-                );
-
-                primary_key.get('columns').remove(removedCols);
-              }
-            });
           },
-        }),
-        canAdd: true,
-        canEdit: true, canDelete: true,
-        // For each row edit/delete button enable/disable
-        canEditRow: true,
-        canDeleteRow: true,
-        allowMultipleEmptyRow: false,
-        beforeAdd: function(newModel) {
-          this.last_attnum++;
-          newModel.set('attnum', this.last_attnum);
-          return newModel;
-        },
-      },{
-        // Here we will create tab control for constraints
-        // We will hide the tab for ERD
-        type: 'nested', control: 'tab', group: gettext('Constraints'), mode: ['properties'],
-        schema: [{
-          id: 'primary_key', label: '',
-          model: this.pgBrowser.Nodes['primary_key'].model.extend({
-            validate: ()=>{},
-          }),
-          subnode: this.pgBrowser.Nodes['primary_key'].model.extend({
-            validate: ()=>{},
-          }),
-          editable: false, type: 'collection',
-        },
-        ],
-      }],
-      validate: function() {
-        var msg,
-          name = this.get('name'),
-          schema = this.get('schema');
-
-        if (
-          _.isUndefined(name) || _.isNull(name) ||
-            String(name).replace(/^\s+|\s+$/g, '') == ''
-        ) {
-          msg = gettext('Table name cannot be empty.');
-          this.errorModel.set('name', msg);
-          return msg;
-        }
-
-        /* Check existing table names */
-        let sameNameCount = _.filter(allTables, (table)=>table[0]==schema&&table[1]==name).length;
-        if(isNew && this.sessAttrs['name'] && sameNameCount > 0 || isNew && sameNameCount > 0) {
-          msg = gettext('Table name already exists.');
-          this.errorModel.set('name', msg);
-          return msg;
-        }
-        this.errorModel.unset('name');
-        if (
-          _.isUndefined(schema) || _.isNull(schema) ||
-            String(schema).replace(/^\s+|\s+$/g, '') == ''
-        ) {
-          msg = gettext('Table schema cannot be empty.');
-          this.errorModel.set('schema', msg);
-          return msg;
-        }
-        this.errorModel.unset('schema');
-
-
-        return null;
+          treeNodeInfo,
+          (params)=>{
+            if(params.tid) {
+              return tableNodesDict[params.tid].getColumns().map((col)=>{
+                return {
+                  value: col.name, label: col.name, 'image': 'icon-column',
+                };
+              });
+            }
+          }, {autoindex: false}, true),
+          ()=>new EmptySchema(),
+          {spcname: []},
+          true
+        ),
       },
-    });
-
-    return new dialogModel(attributes);
+      ()=>new EmptySchema(),
+      ()=>[],
+      ()=>[],
+      ()=>[],
+      ()=>[],
+      isNew ? {
+        schema: schemas[0]?.name,
+      } : attributes,
+      true
+    );
   }
 
-  createOrGetDialog(type) {
+  createOrGetDialog(type, sVersion) {
     const dialogName = this.dialogName();
 
     if (!Alertify[dialogName]) {
@@ -731,19 +115,17 @@ export default class TableDialog {
           `<div class="${dialogName}"></div>`,
           null,
           type,
-          $,
-          this.pgBrowser,
           Alertify,
-          Backform
+          sVersion
         );
       });
     }
     return Alertify[dialogName];
   }
 
-  show(title, attributes, isNew, allTables, colTypes, schemas, sVersion, callback) {
+  show(title, attributes, isNew, tableNodesDict, colTypes, schemas, serverInfo, callback) {
     let dialogTitle = title || gettext('Unknown');
-    const dialog = this.createOrGetDialog('table_dialog');
-    dialog(dialogTitle, this.getDataModel(attributes, isNew, allTables, colTypes, schemas, sVersion), callback).resizeTo(this.pgBrowser.stdW.md, this.pgBrowser.stdH.md);
+    const dialog = this.createOrGetDialog('table_dialog', serverInfo);
+    dialog(dialogTitle, this.getUISchema(attributes, isNew, tableNodesDict, colTypes, schemas, serverInfo), callback).resizeTo(this.pgBrowser.stdW.lg, this.pgBrowser.stdH.md);
   }
 }
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/index.js b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/index.js
index d58d3f9a6..359085cd2 100644
--- a/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/index.js
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/dialogs/index.js
@@ -7,7 +7,7 @@
 //
 //////////////////////////////////////////////////////////////
 
-import TableDialog, {transformToSupported as transformToSupportedTable} from './TableDialog';
+import TableDialog from './TableDialog';
 import OneToManyDialog from './OneToManyDialog';
 import ManyToManyDialog from './ManyToManyDialog';
 import pgBrowser from 'top/browser/static/js/browser';
@@ -15,7 +15,7 @@ import 'sources/backgrid.pgadmin';
 import 'sources/backform.pgadmin';
 
 export default function getDialog(dialogName) {
-  if(dialogName === 'entity_dialog') {
+  if(dialogName === 'table_dialog') {
     return new TableDialog(pgBrowser);
   } else if(dialogName === 'onetomany_dialog') {
     return new OneToManyDialog(pgBrowser);
@@ -23,10 +23,3 @@ export default function getDialog(dialogName) {
     return new ManyToManyDialog(pgBrowser);
   }
 }
-
-export function transformToSupported(type, data) {
-  if(type == 'table') {
-    return transformToSupportedTable(data);
-  }
-  return data;
-}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/index.js b/web/pgadmin/tools/erd/static/js/erd_tool/index.js
index e865c29bd..2b1906f14 100644
--- a/web/pgadmin/tools/erd/static/js/erd_tool/index.js
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/index.js
@@ -12,7 +12,7 @@ import ReactDOM from 'react-dom';
 import _ from 'lodash';
 
 import BodyWidget from './ui_components/BodyWidget';
-import getDialog, {transformToSupported} from './dialogs';
+import getDialog from './dialogs';
 import Alertify from 'pgadmin.alertifyjs';
 import pgWindow from 'sources/window';
 import pgAdmin from 'sources/pgadmin';
@@ -40,7 +40,6 @@ export default class ERDTool {
       <BodyWidget
         params={this.params}
         getDialog={getDialog}
-        transformToSupported={transformToSupported}
         pgWindow={pgWindow}
         pgAdmin={pgAdmin}
         panel={panel}
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/nodes/TableNode.jsx b/web/pgadmin/tools/erd/static/js/erd_tool/nodes/TableNode.jsx
index c4c62b4b4..db2ff3050 100644
--- a/web/pgadmin/tools/erd/static/js/erd_tool/nodes/TableNode.jsx
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/nodes/TableNode.jsx
@@ -188,7 +188,7 @@ export class TableNodeWidget extends React.Component {
   render() {
     let node_data = this.props.node.getData();
     return (
-      <div className={'table-node ' + (this.props.node.isSelected() ? 'selected': '') } onDoubleClick={()=>{this.props.node.fireEvent({}, 'editNode');}}>
+      <div className={'table-node ' + (this.props.node.isSelected() ? 'selected': '') } onDoubleClick={()=>{this.props.node.fireEvent({}, 'editTable');}}>
         <div className="table-toolbar">
           <DetailsToggleButton className='btn-xs' showDetails={this.state.show_details} onClick={this.toggleShowDetails} onDoubleClick={(e)=>{e.stopPropagation();}} />
           {this.props.node.getNote() &&
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/BodyWidget.jsx b/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/BodyWidget.jsx
index 0e69c0b9a..4ff8825c1 100644
--- a/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/BodyWidget.jsx
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/BodyWidget.jsx
@@ -25,6 +25,7 @@ import gettext from 'sources/gettext';
 import url_for from 'sources/url_for';
 import {showERDSqlTool} from 'tools/datagrid/static/js/show_query_tool';
 import 'wcdocker';
+import Theme from '../../../../../../static/js/Theme';
 
 /* Custom react-diagram action for keyboard events */
 export class KeyboardShortcutAction extends Action {
@@ -76,6 +77,9 @@ export default class BodyWidget extends React.Component {
       show_details: true,
       is_new_tab: false,
       preferences: {},
+      table_dialog_open: true,
+      oto_dialog_open: true,
+      otm_dialog_open: true,
     };
     this.diagram = new ERDCore();
     /* Flag for checking if user has opted for save before close */
@@ -88,7 +92,7 @@ export default class BodyWidget extends React.Component {
     this.keyboardActionObj = null;
 
     _.bindAll(this, ['onLoadDiagram', 'onSaveDiagram', 'onSaveAsDiagram', 'onSQLClick',
-      'onImageClick', 'onAddNewNode', 'onEditNode', 'onCloneNode', 'onDeleteNode', 'onNoteClick',
+      'onImageClick', 'onAddNewNode', 'onEditTable', 'onCloneNode', 'onDeleteNode', 'onNoteClick',
       'onNoteClose', 'onOneToManyClick', 'onManyToManyClick', 'onAutoDistribute', 'onDetailsToggle',
       'onDetailsToggle', 'onHelpClick'
     ]);
@@ -130,8 +134,8 @@ export default class BodyWidget extends React.Component {
       'showNote': (event)=>{
         this.showNote(event.node);
       },
-      'editNode': (event) => {
-        this.addEditNode(event.node);
+      'editTable': (event) => {
+        this.addEditTable(event.node);
       },
     };
     Object.keys(diagramEvents).forEach(eventName => {
@@ -150,7 +154,7 @@ export default class BodyWidget extends React.Component {
       [this.state.preferences.generate_sql, this.onSQLClick],
       [this.state.preferences.download_image, this.onImageClick],
       [this.state.preferences.add_table, this.onAddNewNode],
-      [this.state.preferences.edit_table, this.onEditNode],
+      [this.state.preferences.edit_table, this.onEditTable],
       [this.state.preferences.clone_table, this.onCloneNode],
       [this.state.preferences.drop_table, this.onDeleteNode],
       [this.state.preferences.add_edit_note, this.onNoteClick],
@@ -297,19 +301,20 @@ export default class BodyWidget extends React.Component {
   }
 
   getDialog(dialogName) {
-    if(dialogName === 'entity_dialog') {
-      let allTables = this.diagram.getModel().getNodes().map((node)=>{
-        return node.getSchemaTableName();
-      });
+    let serverInfo = {
+      type: this.props.params.server_type,
+      version: this.state.server_version,
+    };
+    if(dialogName === 'table_dialog') {
       return (title, attributes, isNew, callback)=>{
         this.props.getDialog(dialogName).show(
-          title, attributes, isNew, allTables, this.diagram.getCache('colTypes'), this.diagram.getCache('schemas'), this.state.server_version, callback
+          title, attributes, isNew, this.diagram.getModel().getNodesDict(), this.diagram.getCache('colTypes'), this.diagram.getCache('schemas'), serverInfo, callback
         );
       };
     } else if(dialogName === 'onetomany_dialog' || dialogName === 'manytomany_dialog') {
       return (title, attributes, callback)=>{
         this.props.getDialog(dialogName).show(
-          title, attributes, this.diagram.getModel().getNodesDict(), this.state.server_version, callback
+          title, attributes, this.diagram.getModel().getNodesDict(), serverInfo, callback
         );
       };
     }
@@ -328,17 +333,20 @@ export default class BodyWidget extends React.Component {
     }
   }
 
-  addEditNode(node) {
-    let dialog = this.getDialog('entity_dialog');
+  addEditTable(node) {
+    let dialog = this.getDialog('table_dialog');
     if(node) {
       let [schema, table] = node.getSchemaTableName();
       dialog(gettext('Table: %s (%s)', _.escape(table),_.escape(schema)), node.getData(), false, (newData)=>{
+        let oldData = node.getData();
         node.setData(newData);
+        this.diagram.syncTableLinks(node, oldData);
         this.diagram.repaint();
       });
     } else {
-      dialog(gettext('New table'), {name: this.diagram.getNextTableName()}, true, (newData)=>{
+      dialog(gettext('New table'), {}, true, (newData)=>{
         let newNode = this.diagram.addNode(newData);
+        this.diagram.syncTableLinks(newNode);
         newNode.setSelected(true);
       });
     }
@@ -353,15 +361,15 @@ export default class BodyWidget extends React.Component {
     }
   }
 
-  onEditNode() {
+  onEditTable() {
     const selected = this.diagram.getSelectedNodes();
     if(selected.length == 1) {
-      this.addEditNode(selected[0]);
+      this.addEditTable(selected[0]);
     }
   }
 
   onAddNewNode() {
-    this.addEditNode();
+    this.addEditTable();
   }
 
   onCloneNode() {
@@ -385,10 +393,7 @@ export default class BodyWidget extends React.Component {
           node.remove();
         });
         this.diagram.getSelectedLinks().forEach((link)=>{
-          link.getTargetPort().remove();
-          link.getSourcePort().remove();
-          link.setSelected(false);
-          link.remove();
+          this.diagram.removeOneToManyLink(link);
         });
         this.diagram.repaint();
       },
@@ -656,10 +661,7 @@ export default class BodyWidget extends React.Component {
     let dialog = this.getDialog('onetomany_dialog');
     let initData = {local_table_uid: this.diagram.getSelectedNodes()[0].getID()};
     dialog(gettext('One to many relation'), initData, (newData)=>{
-      let newLink = this.diagram.addLink(newData, 'onetomany');
-      this.diagram.clearSelection();
-      newLink.setSelected(true);
-      this.diagram.repaint();
+      this.diagram.addOneToManyLink(newData);
     });
   }
 
@@ -667,46 +669,7 @@ export default class BodyWidget extends React.Component {
     let dialog = this.getDialog('manytomany_dialog');
     let initData = {left_table_uid: this.diagram.getSelectedNodes()[0].getID()};
     dialog(gettext('Many to many relation'), initData, (newData)=>{
-      let nodes = this.diagram.getModel().getNodesDict();
-      let left_table = nodes[newData.left_table_uid];
-      let right_table = nodes[newData.right_table_uid];
-      let tableData = {
-        name: `${left_table.getData().name}_${right_table.getData().name}`,
-        schema: left_table.getData().schema,
-        columns: [{
-          ...left_table.getColumnAt(newData.left_table_column_attnum),
-          'name': `${left_table.getData().name}_${left_table.getColumnAt(newData.left_table_column_attnum).name}`,
-          'is_primary_key': false,
-          'attnum': 0,
-        },{
-          ...right_table.getColumnAt(newData.right_table_column_attnum),
-          'name': `${right_table.getData().name}_${right_table.getColumnAt(newData.right_table_column_attnum).name}`,
-          'is_primary_key': false,
-          'attnum': 1,
-        }],
-      };
-      let newNode = this.diagram.addNode(tableData);
-      this.diagram.clearSelection();
-      newNode.setSelected(true);
-
-      let linkData = {
-        local_table_uid: newNode.getID(),
-        local_column_attnum: newNode.getColumns()[0].attnum,
-        referenced_table_uid: newData.left_table_uid,
-        referenced_column_attnum : newData.left_table_column_attnum,
-      };
-      this.diagram.addLink(linkData, 'onetomany');
-
-      linkData = {
-        local_table_uid: newNode.getID(),
-        local_column_attnum: newNode.getColumns()[1].attnum,
-        referenced_table_uid: newData.right_table_uid,
-        referenced_column_attnum : newData.right_table_column_attnum,
-      };
-
-      this.diagram.addLink(linkData, 'onetomany');
-
-      this.diagram.repaint();
+      this.diagram.addManyToManyLink(newData);
     });
   }
 
@@ -794,10 +757,7 @@ export default class BodyWidget extends React.Component {
 
     try {
       let response = await axios.get(url);
-      let tables = response.data.data.map((table)=>{
-        return this.props.transformToSupported('table', table);
-      });
-      this.diagram.deserializeData(tables);
+      this.diagram.deserializeData(response.data.data);
       return true;
     } catch (error) {
       this.handleAxiosCatch(error);
@@ -809,7 +769,7 @@ export default class BodyWidget extends React.Component {
 
   render() {
     return (
-      <>
+      <Theme>
         <ToolBar id="btn-toolbar">
           <ButtonGroup>
             <IconButton id="open-file" icon="fa fa-folder-open" onClick={this.onLoadDiagram} title={gettext('Load from file')}
@@ -828,7 +788,7 @@ export default class BodyWidget extends React.Component {
           <ButtonGroup>
             <IconButton id="add-node" icon="fa fa-plus-square" onClick={this.onAddNewNode} title={gettext('Add table')}
               shortcut={this.state.preferences.add_table}/>
-            <IconButton id="edit-node" icon="fa fa-pencil-alt" onClick={this.onEditNode} title={gettext('Edit table')}
+            <IconButton id="edit-node" icon="fa fa-pencil-alt" onClick={this.onEditTable} title={gettext('Edit table')}
               shortcut={this.state.preferences.edit_table} disabled={!this.state.single_node_selected || this.state.single_link_selected}/>
             <IconButton id="clone-node" icon="fa fa-clone" onClick={this.onCloneNode} title={gettext('Clone table')}
               shortcut={this.state.preferences.clone_table} disabled={!this.state.single_node_selected || this.state.single_link_selected}/>
@@ -869,7 +829,7 @@ export default class BodyWidget extends React.Component {
           <Loader message={this.state.loading_msg} autoEllipsis={true}/>
           <CanvasWidget className="diagram-canvas flex-grow-1" ref={(ele)=>{this.canvasEle = ele?.ref?.current;}} engine={this.diagram.getEngine()} />
         </div>
-      </>
+      </Theme>
     );
   }
 }
@@ -888,7 +848,6 @@ BodyWidget.propTypes = {
     gen: PropTypes.bool.isRequired,
   }),
   getDialog: PropTypes.func.isRequired,
-  transformToSupported: PropTypes.func.isRequired,
   pgWindow: PropTypes.object.isRequired,
   pgAdmin: PropTypes.object.isRequired,
   alertify: PropTypes.object.isRequired,
diff --git a/web/pgadmin/tools/erd/static/scss/_erd.scss b/web/pgadmin/tools/erd/static/scss/_erd.scss
index f48d65b4c..150ef6778 100644
--- a/web/pgadmin/tools/erd/static/scss/_erd.scss
+++ b/web/pgadmin/tools/erd/static/scss/_erd.scss
@@ -205,3 +205,14 @@
     }
   }
 }
+
+.alertify {
+  .erd-dialog {
+    .ajs-body .ajs-content {
+      bottom: 0!important;
+    }
+    .ajs-footer {
+      display: none;
+    }
+  }
+}
diff --git a/web/regression/javascript/erd/erd_core_spec.js b/web/regression/javascript/erd/erd_core_spec.js
index b26a5fc05..635d488d4 100644
--- a/web/regression/javascript/erd/erd_core_spec.js
+++ b/web/regression/javascript/erd/erd_core_spec.js
@@ -9,6 +9,7 @@
 import ERDCore from 'pgadmin.tools.erd/erd_tool/ERDCore';
 import * as createEngineLib from '@projectstorm/react-diagrams';
 import TEST_TABLES_DATA from './test_tables';
+import { FakeLink, FakeNode } from './fake_item';
 
 describe('ERDCore', ()=>{
   let eleFactory = jasmine.createSpyObj('nodeFactories', {
@@ -132,7 +133,8 @@ describe('ERDCore', ()=>{
     });
 
     it('addNode', ()=>{
-      let newNode = jasmine.createSpyObj('newNode', ['setPosition']);
+      let newNode = new FakeNode({});
+      spyOn(newNode, 'setPosition');
       spyOn(erdCoreObj, 'getNewNode').and.returnValue(newNode);
       spyOn(erdCoreObj, 'clearSelection');
 
@@ -151,33 +153,17 @@ describe('ERDCore', ()=>{
 
 
     it('addLink', ()=>{
+      let node1 = new FakeNode({'name': 'table1'}, 'id1');
+      let node2 = new FakeNode({'name': 'table2'}, 'id2');
+      spyOn(node1, 'addPort').and.callThrough();
+      spyOn(node2, 'addPort').and.callThrough();
       let nodesDict = {
-        'id1': {
-          serializeData: function(){ return {
-            'name': 'table1',
-          };},
-          getPortName: function(attnum) {
-            return `port-${attnum}`;
-          },
-          getPort: function() {
-            return null;
-          },
-          addPort: jasmine.createSpy('addPort').and.callFake((obj)=>obj),
-        },
-        'id2': {
-          serializeData: function(){ return {
-            'name': 'table2',
-          };},
-          getPortName: function(attnum) {
-            return `port-${attnum}`;
-          },
-          getPort: function() {
-            return null;
-          },
-          addPort: jasmine.createSpy('addPort').and.callFake((obj)=>obj),
-        },
+        'id1': node1,
+        'id2': node2,
       };
-      let link = jasmine.createSpyObj('link', ['setSourcePort', 'setTargetPort']);
+      let link = new FakeLink();
+      spyOn(link, 'setSourcePort').and.callThrough();
+      spyOn(link, 'setTargetPort').and.callThrough();
       spyOn(erdEngine.getModel(), 'getNodesDict').and.returnValue(nodesDict);
       spyOn(erdCoreObj, 'getNewLink').and.callFake(function() {
         return link;
@@ -199,7 +185,6 @@ describe('ERDCore', ()=>{
       expect(nodesDict['id2'].addPort).toHaveBeenCalledWith({name: 'port-3'});
       expect(link.setSourcePort).toHaveBeenCalledWith({name: 'port-1'});
       expect(link.setTargetPort).toHaveBeenCalledWith({name: 'port-3'});
-
     });
 
     it('serialize', ()=>{
@@ -222,41 +207,26 @@ describe('ERDCore', ()=>{
     });
 
     it('serializeData', ()=>{
-      spyOn(erdEngine.getModel(), 'getNodesDict').and.returnValue({
-        'id1': {
-          serializeData: function(){ return {
-            'name': 'table1',
-          };},
-        },
-        'id2': {
-          serializeData: function(){ return {
-            'name': 'table2',
-          };},
-        },
-      });
+      let node1 = new FakeNode({'name': 'table1'}, 'id1');
+      let node2 = new FakeNode({'name': 'table2'}, 'id2');
+      let nodesDict = {
+        'id1': node1,
+        'id2': node2,
+      };
+      spyOn(erdEngine.getModel(), 'getNodesDict').and.returnValue(nodesDict);
       spyOn(erdEngine.getModel(), 'getLinks').and.returnValue([
-        {
-          serializeData:  function(){ return {
-            'name': 'link1',
-          };},
-          getID:  function(){ return 'lid1'; },
-        },
-        {
-          serializeData:  function(){ return {
-            'name': 'link2',
-          };},
-          getID:  function(){ return 'lid2'; },
-        },
+        new FakeLink({
+          'name': 'link1',
+        }, 'lid1'),
+        new FakeLink({
+          'name': 'link2',
+        }, 'lid2'),
       ]);
       expect(JSON.stringify(erdCoreObj.serializeData())).toEqual(JSON.stringify({
         nodes: {
           'id1': {'name': 'table1'},
           'id2': {'name': 'table2'},
         },
-        links: {
-          'lid1': {'name': 'link1'},
-          'lid2': {'name': 'link2'},
-        },
       }));
     });
 
@@ -276,6 +246,9 @@ describe('ERDCore', ()=>{
           addPort: function() {
 
           },
+          getData: function() {
+            return table;
+          }
         };
       });
       spyOn(erdEngine.getModel(), 'getNodesDict').and.returnValue(nodesDict);
@@ -288,11 +261,7 @@ describe('ERDCore', ()=>{
       });
       spyOn(erdCoreObj, 'getNewPort').and.returnValue({id: 'id'});
       spyOn(erdCoreObj, 'addNode').and.callFake(function(data) {
-        return {
-          getID: function() {
-            return `id-${data.name}`;
-          },
-        };
+        return new FakeNode({}, `id-${data.name}`);
       });
       spyOn(erdCoreObj, 'addLink');
       spyOn(erdCoreObj, 'dagreDistributeNodes');
@@ -319,8 +288,8 @@ describe('ERDCore', ()=>{
 
     it('getNodesData', ()=>{
       spyOn(erdEngine.getModel(), 'getNodes').and.returnValue([
-        {getData: function () {return {name:'node1'};}},
-        {getData: function () {return {name:'node2'};}},
+        new FakeNode({name:'node1'}),
+        new FakeNode({name:'node2'}),
       ]);
       expect(JSON.stringify(erdCoreObj.getNodesData())).toEqual(JSON.stringify([
         {name:'node1'}, {name:'node2'},
diff --git a/web/regression/javascript/erd/fake_item.js b/web/regression/javascript/erd/fake_item.js
new file mode 100644
index 000000000..498ef1db1
--- /dev/null
+++ b/web/regression/javascript/erd/fake_item.js
@@ -0,0 +1,42 @@
+import _ from 'lodash';
+
+export class FakeNode {
+  constructor(data, id='nid1') {
+    this.data = data || {};
+    this.id = id;
+  }
+  setSelected() {}
+  getColumns() {return this.data.columns;}
+  getID() {return this.id;}
+  setData(data) {this.data = data;}
+  getData() {return this.data;}
+  getPosition() {return {x: 30, y: 30};}
+  setPosition() {}
+  serializeData() {return this.getData();}
+  getPortName(attnum) {return `port-${attnum}`;}
+  getPort() {return null;}
+  addPort(obj) {return obj;}
+  getColumnAt(pos) {return _.find(this.getColumns()||[], (c)=>c.attnum==pos);}
+  remove() {}
+  getSchemaTableName() {return [this.data.schema, this.data.name];}
+  cloneData(tabName) {
+    let retVal = {...this.data};
+    retVal.name = tabName;
+    return retVal;
+  }
+}
+
+export class FakeLink {
+  constructor(data, id='lid1') {
+    this.data = data;
+    this.id = id;
+  }
+  setSelected() {}
+  getID() {return this.id;}
+  getData() {return this.data;}
+  getSourcePort() {return {remove: ()=>{}};}
+  setSourcePort() {}
+  getTargetPort() {return {remove: ()=>{}};}
+  setTargetPort() {}
+  remove() {}
+}
diff --git a/web/regression/javascript/erd/ui_components/body_widget_spec.js b/web/regression/javascript/erd/ui_components/body_widget_spec.js
index 2095ecd90..ca30f63ff 100644
--- a/web/regression/javascript/erd/ui_components/body_widget_spec.js
+++ b/web/regression/javascript/erd/ui_components/body_widget_spec.js
@@ -10,6 +10,7 @@ import * as erdModule from 'pgadmin.tools.erd/erd_module';
 import erdPref from './erd_preferences';
 import BodyWidget from 'pgadmin.tools.erd/erd_tool/ui_components/BodyWidget';
 import * as ERDSqlTool from 'tools/datagrid/static/js/show_query_tool';
+import { FakeLink, FakeNode } from '../fake_item';
 
 let pgAdmin = {
   Browser: {
@@ -60,7 +61,7 @@ let mtmDialog = jasmine.createSpyObj('mtmDialog', ['show']);
 
 let getDialog = (dialogName)=>{
   switch(dialogName) {
-  case 'entity_dialog': return tableDialog;
+  case 'table_dialog': return tableDialog;
   case 'onetomany_dialog': return otmDialog;
   case 'manytomany_dialog': return mtmDialog;
   }
@@ -93,19 +94,16 @@ describe('ERD BodyWidget', ()=>{
     title: 'postgres/postgres@PostgreSQL 12',
     trans_id: 110008,
   };
+  let newNode = new FakeNode({
+    columns: [{attnum: 0}, {attnum: 1}],
+  }, 'newid1');
 
   beforeAll(()=>{
     spyOn(erdModule, 'setPanelTitle');
     spyOn(ERDCore.prototype, 'repaint');
     spyOn(ERDCore.prototype, 'deserializeData');
-    spyOn(ERDCore.prototype, 'addNode').and.returnValue({
-      setSelected: ()=>{},
-      getColumns: ()=>([{attnum: 0}, {attnum: 1}]),
-      getID: ()=>'newid1',
-    });
-    spyOn(ERDCore.prototype, 'addLink').and.returnValue({
-      setSelected: ()=>{},
-    });
+    spyOn(ERDCore.prototype, 'addNode').and.returnValue(newNode);
+    spyOn(ERDCore.prototype, 'addLink').and.returnValue(new FakeLink());
     spyOn(alertify, 'confirm').and.callFake((arg1, arg2, okCallback)=>{
       okCallback();
     });
@@ -128,7 +126,7 @@ describe('ERD BodyWidget', ()=>{
 
   beforeEach(()=>{
     jasmineEnzyme();
-    body = mount(<BodyWidget params={params} pgAdmin={pgAdmin} pgWindow={pgWindow} getDialog={getDialog} transformToSupported={()=>{}} alertify={alertify}/>);
+    body = mount(<BodyWidget params={params} pgAdmin={pgAdmin} pgWindow={pgWindow} getDialog={getDialog} alertify={alertify}/>);
     bodyInstance = body.instance();
   });
 
@@ -225,18 +223,18 @@ describe('ERD BodyWidget', ()=>{
     });
   });
 
-  it('event editNode', (done)=>{
+  it('event editTable', (done)=>{
     let node = {key: 'value', getNote: ()=>'a note'};
-    spyOn(bodyInstance, 'addEditNode');
-    bodyInstance.diagram.fireEvent({node: node}, 'editNode', true);
+    spyOn(bodyInstance, 'addEditTable');
+    bodyInstance.diagram.fireEvent({node: node}, 'editTable', true);
     setTimeout(()=>{
-      expect(bodyInstance.addEditNode).toHaveBeenCalledWith(node);
+      expect(bodyInstance.addEditTable).toHaveBeenCalledWith(node);
       done();
     });
   });
 
   it('getDialog', ()=>{
-    bodyInstance.getDialog('entity_dialog')();
+    bodyInstance.getDialog('table_dialog')();
     expect(tableDialog.show).toHaveBeenCalled();
 
     bodyInstance.getDialog('onetomany_dialog')();
@@ -246,10 +244,20 @@ describe('ERD BodyWidget', ()=>{
     expect(mtmDialog.show).toHaveBeenCalled();
   });
 
-  it('addEditNode', ()=>{
+  it('addEditTable', ()=>{
+    let node1 = new FakeNode({'name': 'table1', schema: 'erd1', columns: [{name: 'col1', type: 'type1', attnum: 1}]}, 'id1');
+    let node2 = new FakeNode({'name': 'table2', schema: 'erd2', columns: [{name: 'col2', type: 'type2', attnum: 2}]}, 'id2');
+    let nodesDict = {
+      'id1': node1,
+      'id2': node2,
+    };
+    spyOn(bodyInstance.diagram, 'getModel').and.returnValue({
+      'getNodesDict': ()=>nodesDict,
+    });
+    spyOn(bodyInstance.diagram, 'addLink');
     /* New */
     tableDialog.show.calls.reset();
-    bodyInstance.addEditNode();
+    bodyInstance.addEditTable();
     expect(tableDialog.show).toHaveBeenCalled();
 
     let saveCallback = tableDialog.show.calls.mostRecent().args[7];
@@ -259,12 +267,9 @@ describe('ERD BodyWidget', ()=>{
 
     /* Existing */
     tableDialog.show.calls.reset();
-    let node = jasmine.createSpyObj('node',{
-      getSchemaTableName: ['erd1', 'table1'],
-      setData: null,
-      getData: null,
-    });
-    bodyInstance.addEditNode(node);
+    let node = new FakeNode({name: 'table1', schema: 'erd1'});
+    spyOn(node, 'setData');
+    bodyInstance.addEditTable(node);
     expect(tableDialog.show).toHaveBeenCalled();
 
     saveCallback = tableDialog.show.calls.mostRecent().args[7];
@@ -273,49 +278,44 @@ describe('ERD BodyWidget', ()=>{
     expect(node.setData).toHaveBeenCalledWith(newData);
   });
 
-  it('onEditNode', ()=>{
+  it('onEditTable', ()=>{
     let node = {key: 'value'};
-    spyOn(bodyInstance, 'addEditNode');
+    spyOn(bodyInstance, 'addEditTable');
     spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
-    bodyInstance.onEditNode();
-    expect(bodyInstance.addEditNode).toHaveBeenCalledWith(node);
+    bodyInstance.onEditTable();
+    expect(bodyInstance.addEditTable).toHaveBeenCalledWith(node);
   });
 
   it('onAddNewNode', ()=>{
-    spyOn(bodyInstance, 'addEditNode');
+    spyOn(bodyInstance, 'addEditTable');
     bodyInstance.onAddNewNode();
-    expect(bodyInstance.addEditNode).toHaveBeenCalled();
+    expect(bodyInstance.addEditTable).toHaveBeenCalled();
   });
 
   it('onCloneNode', ()=>{
-    let node = jasmine.createSpyObj('node',{
-      getSchemaTableName: ['erd1', 'table1'],
-      setData: null,
-      getData: null,
-      cloneData: {key: 'value'},
-      getPosition: {x: 30, y: 30},
-    });
+    let node = new FakeNode({name: 'table1', schema: 'erd1'});
     spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
     spyOn(bodyInstance.diagram, 'getNextTableName').and.returnValue('newtable1');
+    bodyInstance.diagram.addNode.calls.reset();
     bodyInstance.onCloneNode();
-    expect(bodyInstance.diagram.addNode).toHaveBeenCalledWith({key: 'value'}, [50, 50]);
+    let cloneArgs = bodyInstance.diagram.addNode.calls.argsFor(0);
+    expect(cloneArgs[0]).toEqual(jasmine.objectContaining({
+      name: 'newtable1',
+      schema: 'erd1',
+    }));
+    expect(cloneArgs[1]).toEqual([50, 50]);
   });
 
   it('onDeleteNode', (done)=>{
-    let node = jasmine.createSpyObj('node',{
-      getSchemaTableName: ['erd1', 'table1'],
-      setData: null,
-      getData: null,
-      cloneData: {key: 'value'},
-      getPosition: {x: 30, y: 30},
-      remove: null,
-      setSelected: null,
-    });
-    let link = jasmine.createSpyObj('link', {
-      remove: null,
-      setSelected: null,
-      getTargetPort: jasmine.createSpyObj('port', ['remove']),
-      getSourcePort: jasmine.createSpyObj('port', ['remove']),
+    let node = new FakeNode({name: 'table1', schema: 'erd1'});
+    spyOn(node, 'remove');
+    let link = new FakeLink({local_table_uid: 'tid1'});
+    spyOn(link, 'remove');
+    let nodesDict = {
+      'tid1': node
+    };
+    spyOn(bodyInstance.diagram, 'getModel').and.returnValue({
+      'getNodesDict': ()=>nodesDict,
     });
     spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
     spyOn(bodyInstance.diagram, 'getSelectedLinks').and.returnValue([link]);
@@ -413,9 +413,17 @@ describe('ERD BodyWidget', ()=>{
   });
 
   it('onOneToManyClick', ()=>{
-    let node = jasmine.createSpyObj('node',{
-      getID: 'id1',
+    let node = new FakeNode({}, 'id1');
+    let node1 = new FakeNode({'name': 'table1', schema: 'erd1', columns: [{name: 'col1', type: 'type1', attnum: 1}]}, 'id1');
+    let node2 = new FakeNode({'name': 'table2', schema: 'erd2', columns: [{name: 'col2', type: 'type2', attnum: 2}]}, 'id2');
+    let nodesDict = {
+      'id1': node1,
+      'id2': node2,
+    };
+    spyOn(bodyInstance.diagram, 'getModel').and.returnValue({
+      'getNodesDict': ()=>nodesDict,
     });
+    spyOn(bodyInstance.diagram, 'addLink');
     spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
 
     otmDialog.show.calls.reset();
@@ -423,15 +431,18 @@ describe('ERD BodyWidget', ()=>{
     expect(otmDialog.show).toHaveBeenCalled();
 
     let saveCallback = otmDialog.show.calls.mostRecent().args[4];
-    let newData = {key: 'value'};
+    let newData = {
+      local_table_uid: 'id1',
+      local_column_attnum: 1,
+      referenced_table_uid: 'id2',
+      referenced_column_attnum: 2,
+    };
     saveCallback(newData);
     expect(bodyInstance.diagram.addLink).toHaveBeenCalledWith(newData, 'onetomany');
   });
 
   it('onManyToManyClick', ()=>{
-    let node = jasmine.createSpyObj('node',{
-      getID: 'id1',
-    });
+    let node = new FakeNode({}, 'id1');
     spyOn(bodyInstance.diagram, 'getSelectedNodes').and.returnValue([node]);
 
     mtmDialog.show.calls.reset();
@@ -439,19 +450,12 @@ describe('ERD BodyWidget', ()=>{
     expect(mtmDialog.show).toHaveBeenCalled();
 
     /* onSave */
+    let node1 = new FakeNode({'name': 'table1', schema: 'erd1', columns: [{name: 'col1', type: 'type1', attnum: 1}]}, 'id1');
+    let node2 = new FakeNode({'name': 'table2', schema: 'erd2', columns: [{name: 'col2', type: 'type2', attnum: 2}]}, 'id2');
     let nodesDict = {
-      'id1': {
-        getID: ()=>'id1',
-        getData: ()=>({name: 'table1', schema: 'erd1'}),
-        getColumnAt: ()=>({name: 'col1', type: 'type1', attnum: 0}),
-        addPort: jasmine.createSpy('addPort').and.callFake((obj)=>obj),
-      },
-      'id2': {
-        getID: ()=>'id2',
-        getData: ()=>({name: 'table2', schema: 'erd2'}),
-        getColumnAt: ()=>({name: 'col2', type: 'type2', attnum: 1}),
-        addPort: jasmine.createSpy('addPort').and.callFake((obj)=>obj),
-      },
+      'id1': node1,
+      'id2': node2,
+      'newid1': newNode,
     };
     spyOn(bodyInstance.diagram, 'getModel').and.returnValue({
       'getNodesDict': ()=>nodesDict,
@@ -468,24 +472,21 @@ describe('ERD BodyWidget', ()=>{
     bodyInstance.diagram.addNode.calls.reset();
     bodyInstance.diagram.addLink.calls.reset();
     saveCallback(newData);
-    expect(bodyInstance.diagram.addNode).toHaveBeenCalledWith({
+    let tableData = bodyInstance.diagram.addNode.calls.argsFor(0)[0];
+    expect(tableData).toEqual(jasmine.objectContaining({
       name: 'table1_table2',
       schema: 'erd1',
-      columns: [
-        {
-          type: 'type1',
-          name: 'table1_col1',
-          is_primary_key: false,
-          attnum: 0,
-        },
-        {
-          type: 'type2',
-          name: 'table2_col2',
-          is_primary_key: false,
-          attnum: 1,
-        },
-      ],
-    });
+    }));
+    expect(tableData.columns[0]).toEqual(jasmine.objectContaining({
+      type: 'type1',
+      name: 'table1_col1',
+      attnum: 0,
+    }));
+    expect(tableData.columns[1]).toEqual(jasmine.objectContaining({
+      type: 'type2',
+      name: 'table2_col2',
+      attnum: 1,
+    }));
 
     let linkData = {
       local_table_uid: 'newid1',
diff --git a/web/regression/javascript/schema_ui_files/column.ui.spec.js b/web/regression/javascript/schema_ui_files/column.ui.spec.js
index a6528ab16..bde3aff27 100644
--- a/web/regression/javascript/schema_ui_files/column.ui.spec.js
+++ b/web/regression/javascript/schema_ui_files/column.ui.spec.js
@@ -202,13 +202,13 @@ describe('ColumnSchema', ()=>{
     expect(schemaObj.inSchemaWithColumnCheck(state)).toBe(true);
 
     schemaObj.nodeInfo = {};
-    expect(schemaObj.inSchemaWithColumnCheck(state)).toBe(true);
+    expect(schemaObj.inSchemaWithColumnCheck(state)).toBe(false);
   });
 
   it('editableCheckForTable', ()=>{
     let state = {};
     schemaObj.nodeInfo = {};
-    expect(schemaObj.editableCheckForTable(state)).toBe(false);
+    expect(schemaObj.editableCheckForTable(state)).toBe(true);
   });
 
   it('depChange', ()=>{
diff --git a/web/yarn.lock b/web/yarn.lock
index b92c49a2d..254a37a9d 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -1464,50 +1464,50 @@
   resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.0.tgz#32e63212293dd3efbb521cd35a5020ab66eaa546"
   integrity sha512-wjtKehFAIARq2OxK8j3JrggNlEslJfNuSm2ArteIbKyRMts2g0a7KzTxfRVNUM+O0gnBJ2hNV8nWPOYBgI1sew==
 
-"@projectstorm/geometry@^6.5.2":
-  version "6.5.2"
-  resolved "https://registry.yarnpkg.com/@projectstorm/geometry/-/geometry-6.5.2.tgz#76ccc7280a49c64953036aa96287f408981e5913"
-  integrity sha512-PGrcMXr6CkdH1DvcY+MDYzg6suWnOXehP8S69DhiEeWUFmQhKJSCAPEovdQOpohL6urlKaSNbDM8auUypFbTfw==
-
-"@projectstorm/react-canvas-core@^6.5.2":
-  version "6.5.2"
-  resolved "https://registry.yarnpkg.com/@projectstorm/react-canvas-core/-/react-canvas-core-6.5.2.tgz#65726df39c15487d9c6b23616cfa6b182d82fcbe"
-  integrity sha512-tFfWpaPZ71lMDFRL69jRFTjmYit6FKNpkAiI9sqYeH2I4Y7OjuxmCqYomlzh8ZSLVwKHtcI6Vd7hhoatEBupEQ==
-  dependencies:
-    "@projectstorm/geometry" "^6.5.2"
-
-"@projectstorm/react-diagrams-core@^6.5.2":
-  version "6.5.2"
-  resolved "https://registry.yarnpkg.com/@projectstorm/react-diagrams-core/-/react-diagrams-core-6.5.2.tgz#4c8c92ed288b2934de11c1a52fda72249b151932"
-  integrity sha512-7ganJGs7lcRB1sTvUs+MQ9RiSCGD+IHgISo45aS81+XFllO8u7uZRaPdIyObhz+khwZvMk2pWmXYPNJwGThOZQ==
-  dependencies:
-    "@projectstorm/geometry" "^6.5.2"
-    "@projectstorm/react-canvas-core" "^6.5.2"
-
-"@projectstorm/react-diagrams-defaults@^6.5.2":
-  version "6.5.2"
-  resolved "https://registry.yarnpkg.com/@projectstorm/react-diagrams-defaults/-/react-diagrams-defaults-6.5.2.tgz#084138e8b2ac2643bcfa115fb543f69c0eaf88f3"
-  integrity sha512-8+hAe88Lj7uBsugAT7f5SsjqxmjYfeBYwViaFpnIilmuYF/uXN0w/ns8Mcq2FE49wGOXtBoxFusoWmDWwgE+fQ==
-  dependencies:
-    "@projectstorm/react-diagrams-core" "^6.5.2"
-
-"@projectstorm/react-diagrams-routing@^6.5.2":
-  version "6.5.2"
-  resolved "https://registry.yarnpkg.com/@projectstorm/react-diagrams-routing/-/react-diagrams-routing-6.5.2.tgz#824dadbcd8c14c647829bdd4761afef69dec5622"
-  integrity sha512-pWsDmk/4hbx6Ii2Z3Uvd9dZ/xB9F7u66mGVGMWb3txxTT68Y6mwdVJRQFu/frm/Sk3sZv7gNf1EqadsilIh7vg==
-  dependencies:
-    "@projectstorm/geometry" "^6.5.2"
-    "@projectstorm/react-diagrams-core" "^6.5.2"
-    "@projectstorm/react-diagrams-defaults" "^6.5.2"
-
-"@projectstorm/react-diagrams@^6.4.2":
-  version "6.5.2"
-  resolved "https://registry.yarnpkg.com/@projectstorm/react-diagrams/-/react-diagrams-6.5.2.tgz#3f65a903b1853e51d68109a513d91d190d278864"
-  integrity sha512-1xlz1jVffWq5R7Oqj/BEY06Cf+2uLV5eEY6pGxuGFfp5SXeTNCSIS/khkbFou707Gor3D8MOqdBpZvF2NfoLmA==
-  dependencies:
-    "@projectstorm/react-diagrams-core" "^6.5.2"
-    "@projectstorm/react-diagrams-defaults" "^6.5.2"
-    "@projectstorm/react-diagrams-routing" "^6.5.2"
+"@projectstorm/geometry@^6.6.1":
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/@projectstorm/geometry/-/geometry-6.6.1.tgz#4a42f5c8fdfcc3d951e73f5db7fe9546514acc3d"
+  integrity sha512-gWRkv+fm+VIpoffHzDHPmGYlEqx8xWGfE/JR7TXAZweNdjEIxyhT++hVlCJiFJS+/cGqgN3z+eP7PNjwZUPGRg==
+
+"@projectstorm/react-canvas-core@^6.6.1":
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/@projectstorm/react-canvas-core/-/react-canvas-core-6.6.1.tgz#93cc6e70e986fe620b6fad6b597d3aa038075244"
+  integrity sha512-wAxEh4Wja2Au0QAuLqxJNaWpVxYIffTFUZhjH8wtW8MKCWS6W9RnP6upeC8QVQi29NS59UIX4wXNzb6e6ht5ww==
+  dependencies:
+    "@projectstorm/geometry" "^6.6.1"
+
+"@projectstorm/react-diagrams-core@^6.6.1":
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/@projectstorm/react-diagrams-core/-/react-diagrams-core-6.6.1.tgz#f626c6253dd6f4b04c3256976b55377ffac2b1bb"
+  integrity sha512-TiDwpcH+t2b2tG/UHDQvhlyx3gOQRJXxTyNDo7p+430ikTrvz1f8uNe5Rt3SrzyqxeUazLFXYBgbGpEanqOykQ==
+  dependencies:
+    "@projectstorm/geometry" "^6.6.1"
+    "@projectstorm/react-canvas-core" "^6.6.1"
+
+"@projectstorm/react-diagrams-defaults@^6.6.1":
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/@projectstorm/react-diagrams-defaults/-/react-diagrams-defaults-6.6.1.tgz#a97cdda56d7336b84775f76a925b90286ea2833d"
+  integrity sha512-FJu8BNBjvANVZ8N99WXS/f6Mu5/yMC4Pi55kTG3vq7o14tsVMcghosmxst5eoeL251O4I+ulNvQ/yCc1Mc5org==
+  dependencies:
+    "@projectstorm/react-diagrams-core" "^6.6.1"
+
+"@projectstorm/react-diagrams-routing@^6.6.1":
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/@projectstorm/react-diagrams-routing/-/react-diagrams-routing-6.6.1.tgz#06b77ab1f94f4567391099ffe5e1d91db34408ff"
+  integrity sha512-m8akJynhanxmpc/A2U7bcgFxIMxsjb3zmYBRGFltVJve87mir8ACaH2gmiHYcAfgEHxdh+x7mCuUlfNP242Ytw==
+  dependencies:
+    "@projectstorm/geometry" "^6.6.1"
+    "@projectstorm/react-diagrams-core" "^6.6.1"
+    "@projectstorm/react-diagrams-defaults" "^6.6.1"
+
+"@projectstorm/react-diagrams@^6.6.1":
+  version "6.6.1"
+  resolved "https://registry.yarnpkg.com/@projectstorm/react-diagrams/-/react-diagrams-6.6.1.tgz#3af4007613f487ba58801cdcded955683b3f01c5"
+  integrity sha512-tLSXfEf/dGFUN8JCCRMrYyIBhhn+eVw24xQodmtcReJxQpKa31EWh9CmJ6UEg7xUnabMG9f2plOPyJqyFssGTA==
+  dependencies:
+    "@projectstorm/react-diagrams-core" "^6.6.1"
+    "@projectstorm/react-diagrams-defaults" "^6.6.1"
+    "@projectstorm/react-diagrams-routing" "^6.6.1"
 
 "@simonwep/pickr@^1.5.1":
   version "1.8.1"


^ permalink  raw  reply  [nested|flat] 4+ messages in thread

* Re: [pgAdmin][RM6081] Advanced table fields in ERD
@ 2021-10-11 12:13  Akshay Joshi <[email protected]>
  parent: Aditya Toshniwal <[email protected]>
  0 siblings, 1 reply; 4+ messages in thread

From: Akshay Joshi @ 2021-10-11 12:13 UTC (permalink / raw)
  To: Aditya Toshniwal <[email protected]>; +Cc: pgadmin-hackers

Thanks, the patch applied.

On Mon, Oct 11, 2021 at 5:14 PM Aditya Toshniwal <
[email protected]> wrote:

> Hi Hackers,
>
> Attached patch will port Backbone based dialogs in ERD to React based
> using the schema view UI framework. This allows us to use the existing
> table UI, remove repeated code and enable advanced fields. I have hidden
> fields from the existing table UI for ERD.
> Please note, this also fixes #6529.
>
> Please review.
>
> --
> Thanks,
> Aditya Toshniwal
> pgAdmin Hacker | Software Architect | *edbpostgres.com*
> <http://edbpostgres.com;
> "Don't Complain about Heat, Plant a TREE"
>


-- 
*Thanks & Regards*
*Akshay Joshi*
*pgAdmin Hacker | Principal Software Architect*
*EDB Postgres <http://edbpostgres.com>*

*Mobile: +91 976-788-8246*


^ permalink  raw  reply  [nested|flat] 4+ messages in thread

* Re: [pgAdmin][RM6081] Advanced table fields in ERD
@ 2021-10-12 06:05  Aditya Toshniwal <[email protected]>
  parent: Akshay Joshi <[email protected]>
  0 siblings, 1 reply; 4+ messages in thread

From: Aditya Toshniwal @ 2021-10-12 06:05 UTC (permalink / raw)
  To: Akshay Joshi <[email protected]>; +Cc: pgadmin-hackers

Hi,

Please review the attached patch to fix failing API test case.

On Mon, Oct 11, 2021 at 5:44 PM Akshay Joshi <[email protected]>
wrote:

> Thanks, the patch applied.
>
> On Mon, Oct 11, 2021 at 5:14 PM Aditya Toshniwal <
> [email protected]> wrote:
>
>> Hi Hackers,
>>
>> Attached patch will port Backbone based dialogs in ERD to React based
>> using the schema view UI framework. This allows us to use the existing
>> table UI, remove repeated code and enable advanced fields. I have hidden
>> fields from the existing table UI for ERD.
>> Please note, this also fixes #6529.
>>
>> Please review.
>>
>> --
>> Thanks,
>> Aditya Toshniwal
>> pgAdmin Hacker | Software Architect | *edbpostgres.com*
>> <http://edbpostgres.com;
>> "Don't Complain about Heat, Plant a TREE"
>>
>
>
> --
> *Thanks & Regards*
> *Akshay Joshi*
> *pgAdmin Hacker | Principal Software Architect*
> *EDB Postgres <http://edbpostgres.com>*
>
> *Mobile: +91 976-788-8246*
>


-- 
Thanks,
Aditya Toshniwal
pgAdmin Hacker | Software Architect | *edbpostgres.com*
<http://edbpostgres.com;
"Don't Complain about Heat, Plant a TREE"


Attachments:

  [application/octet-stream] RM6081.api.patch (2.3K, 3-RM6081.api.patch)
  download | inline diff:
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js b/web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js
index b63b1786a..60dbf4e34 100644
--- a/web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/ERDCore.js
@@ -381,21 +381,6 @@ export default class ERDCore {
       })],
     });
 
-    // let tableData = {
-    //   name: `${leftNode.getData().name}_${rightNode.getData().name}`,
-    //   schema: leftNode.getData().schema,
-    //   columns: [{
-    //     ...leftNode.getColumnAt(manytomanyData.left_table_column_attnum),
-    //     'name': `${leftNode.getData().name}_${leftNode.getColumnAt(manytomanyData.left_table_column_attnum).name}`,
-    //     'is_primary_key': false,
-    //     'attnum': 0,
-    //   },{
-    //     ...rightNode.getColumnAt(manytomanyData.right_table_column_attnum),
-    //     'name': `${rightNode.getData().name}_${rightNode.getColumnAt(manytomanyData.right_table_column_attnum).name}`,
-    //     'is_primary_key': false,
-    //     'attnum': 1,
-    //   }],
-    // };
     let newNode = this.addNode(tableData);
     this.clearSelection();
     newNode.setSelected(true);
diff --git a/web/pgadmin/tools/erd/tests/test_sql_input_data.json b/web/pgadmin/tools/erd/tests/test_sql_input_data.json
index 3c8182e68..bd149a237 100644
--- a/web/pgadmin/tools/erd/tests/test_sql_input_data.json
+++ b/web/pgadmin/tools/erd/tests/test_sql_input_data.json
@@ -80,6 +80,21 @@
           ],
           "include": []
         }
+      ],
+      "foreign_key": [
+        {
+          "schema": "public",
+          "table": "newtable2",
+          "remote_schema": "public",
+          "remote_table": "newtable1",
+          "columns": [
+            {
+              "local_column": "table1_id",
+              "referenced": "id",
+              "references": "1d9dc56e-e4f9-48b9-889b-6084ec6446bf"
+            }
+          ]
+        }
       ]
     },
     "f001a770-d6fa-4572-b88b-11dd5e38d30c": {
@@ -88,19 +103,5 @@
       "schema": "public",
       "primary_key": []
     }
-  },
-  "links": {
-    "998de19a-caa0-431e-9cf7-97827f01022b": {
-      "schema": "public",
-      "table": "newtable2",
-      "remote_schema": "public",
-      "remote_table": "newtable1",
-      "columns": [
-        {
-          "local_column": "table1_id",
-          "referenced": "id"
-        }
-      ]
-    }
   }
 }


^ permalink  raw  reply  [nested|flat] 4+ messages in thread

* Re: [pgAdmin][RM6081] Advanced table fields in ERD
@ 2021-10-12 09:34  Akshay Joshi <[email protected]>
  parent: Aditya Toshniwal <[email protected]>
  0 siblings, 0 replies; 4+ messages in thread

From: Akshay Joshi @ 2021-10-12 09:34 UTC (permalink / raw)
  To: Aditya Toshniwal <[email protected]>; +Cc: pgadmin-hackers

Thanks, the patch applied.

On Tue, Oct 12, 2021 at 11:36 AM Aditya Toshniwal <
[email protected]> wrote:

> Hi,
>
> Please review the attached patch to fix failing API test case.
>
> On Mon, Oct 11, 2021 at 5:44 PM Akshay Joshi <
> [email protected]> wrote:
>
>> Thanks, the patch applied.
>>
>> On Mon, Oct 11, 2021 at 5:14 PM Aditya Toshniwal <
>> [email protected]> wrote:
>>
>>> Hi Hackers,
>>>
>>> Attached patch will port Backbone based dialogs in ERD to React based
>>> using the schema view UI framework. This allows us to use the existing
>>> table UI, remove repeated code and enable advanced fields. I have hidden
>>> fields from the existing table UI for ERD.
>>> Please note, this also fixes #6529.
>>>
>>> Please review.
>>>
>>> --
>>> Thanks,
>>> Aditya Toshniwal
>>> pgAdmin Hacker | Software Architect | *edbpostgres.com*
>>> <http://edbpostgres.com;
>>> "Don't Complain about Heat, Plant a TREE"
>>>
>>
>>
>> --
>> *Thanks & Regards*
>> *Akshay Joshi*
>> *pgAdmin Hacker | Principal Software Architect*
>> *EDB Postgres <http://edbpostgres.com>*
>>
>> *Mobile: +91 976-788-8246*
>>
>
>
> --
> Thanks,
> Aditya Toshniwal
> pgAdmin Hacker | Software Architect | *edbpostgres.com*
> <http://edbpostgres.com;
> "Don't Complain about Heat, Plant a TREE"
>


-- 
*Thanks & Regards*
*Akshay Joshi*
*pgAdmin Hacker | Principal Software Architect*
*EDB Postgres <http://edbpostgres.com>*

*Mobile: +91 976-788-8246*


^ permalink  raw  reply  [nested|flat] 4+ messages in thread


end of thread, other threads:[~2021-10-12 09:34 UTC | newest]

Thread overview: 4+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2021-10-11 11:43 [pgAdmin][RM6081] Advanced table fields in ERD Aditya Toshniwal <[email protected]>
2021-10-11 12:13 ` Akshay Joshi <[email protected]>
2021-10-12 06:05   ` Aditya Toshniwal <[email protected]>
2021-10-12 09:34     ` Akshay Joshi <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox