From 4db0ee945d9f4b409825c92551ab21c8a8277306 Mon Sep 17 00:00:00 2001
From: "Behrisch, M. (Michael)" <m.behrisch@uu.nl>
Date: Tue, 6 Feb 2024 17:13:42 +0000
Subject: [PATCH] fix(schema): update style and layout of schema

Reduces edge overlap, adds an autocomplete option for dragged relations to query, and updates certain styles.
---
 .gitignore                                    |   5 +-
 .../components/inputs/checkbox.stories.tsx    |   8 +-
 .../components/inputs/dropdown.stories.tsx    |   8 +-
 libs/shared/lib/components/inputs/index.tsx   |  45 ++---
 .../lib/components/inputs/radio.stories.tsx   |  12 +-
 .../lib/components/inputs/slider.stories.tsx  |   8 +-
 .../lib/components/inputs/text.stories.tsx    |  10 +-
 .../data-access/store/querybuilderSlice.ts    |   2 +
 .../lib/data-access/store/schemaSlice.ts      |   4 +-
 .../lib/graph-layout/cytoscape-layouts.ts     |   4 +
 .../lib/querybuilder/panel/querybuilder.tsx   |  70 ++++++--
 .../querysidepanel/querySettingsDialog.tsx    |  16 +-
 ...ies.tsx => schemaquerybuilder.stories.tsx} |   2 +-
 .../pills/customFlowLines/connection.tsx      |   7 +-
 .../customFlowPills/entitypill/entitypill.tsx |   6 +-
 .../relationpill/relation-handles.tsx         |  19 ++-
 .../relationpill/relationpill.tsx             | 155 +++++++++---------
 .../query-utils/query2backend.spec.ts         |   1 +
 libs/shared/lib/schema/model/reactflow.tsx    |   1 +
 libs/shared/lib/schema/panel/schema.tsx       |  30 ++--
 libs/shared/lib/schema/panel/schemaDialog.tsx |  46 ++----
 .../lib/schema/pills/edges/node-edge.tsx      |   2 +-
 .../pills/nodes/entity/SchemaEntityPopup.tsx  |  81 ++++++---
 .../nodes/entity/entity-node.stories.tsx      |  44 ++++-
 .../schema/pills/nodes/entity/entity-node.tsx |   6 +-
 .../relation/SchemaRelationshipPopup.tsx      |  89 +++++++---
 .../nodes/relation/relation-node.stories.tsx  |  45 ++++-
 .../pills/nodes/relation/relation-node.tsx    |  15 +-
 .../schema/schema-utils/schema-usecases.ts    |  91 +---------
 29 files changed, 478 insertions(+), 354 deletions(-)
 rename libs/shared/lib/querybuilder/panel/{shemaquerybuilder.stories.tsx => schemaquerybuilder.stories.tsx} (97%)

diff --git a/.gitignore b/.gitignore
index 6f9727aa4..cf4d02514 100644
--- a/.gitignore
+++ b/.gitignore
@@ -78,4 +78,7 @@ certs/*.pem
 node_modules
 
 tsconfig.tsbuildinfo
-vite.config.ts.*
\ No newline at end of file
+vite.config.ts.*
+
+# autogenerated scss type files 
+*.scss.d.ts
diff --git a/libs/shared/lib/components/inputs/checkbox.stories.tsx b/libs/shared/lib/components/inputs/checkbox.stories.tsx
index 4ef5a1b18..e28212597 100644
--- a/libs/shared/lib/components/inputs/checkbox.stories.tsx
+++ b/libs/shared/lib/components/inputs/checkbox.stories.tsx
@@ -1,10 +1,10 @@
 import React from 'react';
 import type { Meta, StoryObj } from '@storybook/react';
-import { Checkbox } from '.';
+import { CheckboxInput } from '.';
 
-const Component: Meta<typeof Checkbox> = {
+const Component: Meta<typeof CheckboxInput> = {
   title: 'Components/Inputs',
-  component: Checkbox,
+  component: CheckboxInput,
   argTypes: { onChange: {} },
   decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
 };
@@ -12,7 +12,7 @@ const Component: Meta<typeof Checkbox> = {
 export default Component;
 type Story = StoryObj<typeof Component>;
 
-export const CheckboxInput: Story = {
+export const CheckboxInputStory: Story = {
   args: {
     type: 'checkbox',
     label: 'Checkbox component',
diff --git a/libs/shared/lib/components/inputs/dropdown.stories.tsx b/libs/shared/lib/components/inputs/dropdown.stories.tsx
index 0c7aa3d5d..319132e5a 100644
--- a/libs/shared/lib/components/inputs/dropdown.stories.tsx
+++ b/libs/shared/lib/components/inputs/dropdown.stories.tsx
@@ -1,10 +1,10 @@
 import React from 'react';
 import type { Meta, StoryObj } from '@storybook/react';
-import { DropDown } from '.';
+import { DropDownInput } from '.';
 
-const Component: Meta<typeof DropDown> = {
+const Component: Meta<typeof DropDownInput> = {
   title: 'Components/Inputs',
-  component: DropDown,
+  component: DropDownInput,
   argTypes: { onChange: {} },
   decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
 };
@@ -12,7 +12,7 @@ const Component: Meta<typeof DropDown> = {
 export default Component;
 type Story = StoryObj<typeof Component>;
 
-export const DropdownInput: Story = {
+export const DropdownInputStory: Story = {
   args: {
     type: 'dropdown',
     label: 'Select component',
diff --git a/libs/shared/lib/components/inputs/index.tsx b/libs/shared/lib/components/inputs/index.tsx
index 4454d2e73..76b72f2ba 100644
--- a/libs/shared/lib/components/inputs/index.tsx
+++ b/libs/shared/lib/components/inputs/index.tsx
@@ -11,6 +11,7 @@ type SliderProps = {
   step: number;
   showValue?: boolean;
   unit?: string;
+  tooltip?: string;
   onChange?: (value: number) => void;
 };
 
@@ -23,6 +24,7 @@ type TextProps = {
   errorText?: string;
   visible?: boolean;
   disabled?: boolean;
+  tooltip?: string;
   validate?: (value: any) => boolean;
   onChange?: (value: string) => void;
 };
@@ -32,6 +34,7 @@ type CheckboxProps = {
   type: 'checkbox';
   options: Array<string>;
   value: Array<string>;
+  tooltip?: string;
   onChange?: (value: Array<string>) => void;
 };
 
@@ -40,6 +43,7 @@ type BooleanProps = {
   type: 'boolean';
   options?: any;
   value: boolean;
+  tooltip?: string;
   onChange?: (value: boolean) => void;
 };
 
@@ -48,6 +52,7 @@ type RadioProps = {
   type: 'radio';
   options: Array<string>;
   value: string;
+  tooltip?: string;
   onChange?: (value: string) => void;
 };
 
@@ -56,6 +61,7 @@ type DropdownProps = {
   value: string | number;
   type: 'dropdown';
   options: any;
+  tooltip?: string;
   onChange?: (value: string | number) => void;
   required?: boolean;
 };
@@ -65,25 +71,25 @@ export type InputProps = TextProps | SliderProps | CheckboxProps | DropdownProps
 const Input = (props: InputProps) => {
   switch (props.type) {
     case 'slider':
-      return <Slider {...(props as SliderProps)} />;
+      return <SliderInput {...(props as SliderProps)} />;
     case 'text' || 'password':
-      return <Text {...(props as TextProps)} />;
+      return <TextInput {...(props as TextProps)} />;
     case 'checkbox':
-      return <Checkbox {...(props as CheckboxProps)} />;
+      return <CheckboxInput {...(props as CheckboxProps)} />;
     case 'dropdown':
-      return <DropDown {...(props as DropdownProps)} />;
+      return <DropDownInput {...(props as DropdownProps)} />;
     case 'radio':
-      return <Radio {...(props as RadioProps)} />;
+      return <RadioInput {...(props as RadioProps)} />;
     case 'boolean':
-      return <Boolean {...(props as BooleanProps)} />;
+      return <BooleanInput {...(props as BooleanProps)} />;
     default:
       return null;
   }
 };
 
-export const Slider = ({ label, value, min, max, step, unit, showValue = true, onChange }: SliderProps) => {
+export const SliderInput = ({ label, value, min, max, step, unit, showValue = true, onChange, tooltip }: SliderProps) => {
   return (
-    <div className={styles['slider']}>
+    <div data-tip={tooltip} className={'tooltip ' + styles['slider']}>
       <label className="label flex flex-row justify-between items-end">
         <span className="label-text">{label}</span>
         {showValue ? (
@@ -111,7 +117,7 @@ export const Slider = ({ label, value, min, max, step, unit, showValue = true, o
   );
 };
 
-export const Text = ({
+export const TextInput = ({
   label,
   placeholder,
   value = '',
@@ -121,11 +127,12 @@ export const Text = ({
   validate,
   disabled = false,
   onChange,
+  tooltip,
 }: TextProps) => {
   const [isValid, setIsValid] = React.useState<boolean>(true);
 
   return (
-    <div className="form-control w-full">
+    <div data-tip={tooltip || null} className="tooltip form-control w-full">
       <label className="label">
         <span className={`text-sm font-medium text-secondary-700 ${required && "after:content-['*'] after:ml-0.5 after:text-danger-500"}`}>
           {label}
@@ -154,9 +161,9 @@ export const Text = ({
   );
 };
 
-export const Radio = ({ label, value, options, onChange }: RadioProps) => {
+export const RadioInput = ({ label, value, options, onChange, tooltip }: RadioProps) => {
   return (
-    <div>
+    <div data-tip={tooltip || null} className="tooltip">
       <label className="label">
         <span className="label-text">{label}</span>
       </label>
@@ -180,9 +187,9 @@ export const Radio = ({ label, value, options, onChange }: RadioProps) => {
   );
 };
 
-export const Checkbox = ({ label, value, options, onChange }: CheckboxProps) => {
+export const CheckboxInput = ({ label, value, options, onChange, tooltip }: CheckboxProps) => {
   return (
-    <div>
+    <div data-tip={tooltip || null} className="tooltip">
       {label && (
         <label className="label">
           <span className="label-text">{label}</span>
@@ -209,10 +216,10 @@ export const Checkbox = ({ label, value, options, onChange }: CheckboxProps) =>
   );
 };
 
-export const Boolean = ({ label, value, onChange }: BooleanProps) => {
+export const BooleanInput = ({ label, value, onChange, tooltip }: BooleanProps) => {
   return (
-    <div>
-      <label className="label cursor-pointer w-fit gap-2 px-0 py-1">
+    <div data-tip={tooltip || null} className="tooltip">
+      <label className={`label cursor-pointer w-fit gap-2 px-0 py-1`}>
         <span className="label-text">{label}</span>
         <input
           type="checkbox"
@@ -229,7 +236,7 @@ export const Boolean = ({ label, value, onChange }: BooleanProps) => {
   );
 };
 
-export const DropDown = ({ label, value, options, onChange, required = false }: DropdownProps) => {
+export const DropDownInput = ({ label, value, options, onChange, required = false, tooltip }: DropdownProps) => {
   const dropdownRef = React.useRef<HTMLDivElement>(null);
   const [isDropdownOpen, setIsDropdownOpen] = React.useState<boolean>(false);
 
@@ -246,7 +253,7 @@ export const DropDown = ({ label, value, options, onChange, required = false }:
   }, [isDropdownOpen]);
 
   return (
-    <div className="w-full">
+    <div data-tip={tooltip || null} className="tooltip w-full">
       {label && (
         <label className="label">
           <span
diff --git a/libs/shared/lib/components/inputs/radio.stories.tsx b/libs/shared/lib/components/inputs/radio.stories.tsx
index e1d3be1c3..563e01816 100644
--- a/libs/shared/lib/components/inputs/radio.stories.tsx
+++ b/libs/shared/lib/components/inputs/radio.stories.tsx
@@ -1,10 +1,10 @@
 import React, { useState } from 'react';
 import type { Meta, StoryObj } from '@storybook/react';
-import { Radio } from '.';
+import { RadioInput } from '.';
 
-const Component: Meta<typeof Radio> = {
+const Component: Meta<typeof RadioInput> = {
   title: 'Components/Inputs',
-  component: Radio,
+  component: RadioInput,
   argTypes: { onChange: { action: 'changed' } },
   decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
 };
@@ -12,12 +12,12 @@ const Component: Meta<typeof Radio> = {
 export default Component;
 type Story = StoryObj<typeof Component>;
 
-export const RadioInput: Story = (args: any) => {
+export const RadioInputStory: Story = (args: any) => {
   const [value, setValue] = useState<string>('');
-  return <Radio {...args} value={value} onChange={setValue} />;
+  return <RadioInput {...args} value={value} onChange={setValue} />;
 };
 
-RadioInput.args = {
+RadioInputStory.args = {
   type: 'radio',
   label: 'Radio component',
   options: ['Option 1', 'Option 2'],
diff --git a/libs/shared/lib/components/inputs/slider.stories.tsx b/libs/shared/lib/components/inputs/slider.stories.tsx
index 12c65ffb8..6598690f4 100644
--- a/libs/shared/lib/components/inputs/slider.stories.tsx
+++ b/libs/shared/lib/components/inputs/slider.stories.tsx
@@ -1,10 +1,10 @@
 import React from 'react';
 import type { Meta, StoryObj } from '@storybook/react';
-import { Slider } from '.';
+import { SliderInput } from '.';
 
-const Component: Meta<typeof Slider> = {
+const Component: Meta<typeof SliderInput> = {
   title: 'Components/Inputs',
-  component: Slider,
+  component: SliderInput,
   argTypes: { onChange: {}, unit: {}, showValue: { control: 'boolean' } },
   decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
 };
@@ -12,7 +12,7 @@ const Component: Meta<typeof Slider> = {
 export default Component;
 type Story = StoryObj<typeof Component>;
 
-export const SliderInput: Story = {
+export const SliderInputStory: Story = {
   args: {
     type: 'slider',
     label: 'Slider component',
diff --git a/libs/shared/lib/components/inputs/text.stories.tsx b/libs/shared/lib/components/inputs/text.stories.tsx
index c76d075cd..f383435de 100644
--- a/libs/shared/lib/components/inputs/text.stories.tsx
+++ b/libs/shared/lib/components/inputs/text.stories.tsx
@@ -1,10 +1,10 @@
 import React from 'react';
 import type { Meta, StoryObj } from '@storybook/react';
-import { Text } from '.';
+import { TextInput } from '.';
 
-const Component: Meta<typeof Text> = {
+const Component: Meta<typeof TextInput> = {
   title: 'Components/Inputs',
-  component: Text,
+  component: TextInput,
   argTypes: { onChange: {} },
   decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
 };
@@ -12,7 +12,7 @@ const Component: Meta<typeof Text> = {
 export default Component;
 type Story = StoryObj<typeof Component>;
 
-export const TextInput: Story = {
+export const TextInputStory: Story = {
   args: {
     type: 'text',
     label: 'Text input',
@@ -22,7 +22,7 @@ export const TextInput: Story = {
   },
 };
 
-export const RequiredTextInput: Story = {
+export const RequiredTextInputStory: Story = {
   args: {
     type: 'text',
     label: 'Text input',
diff --git a/libs/shared/lib/data-access/store/querybuilderSlice.ts b/libs/shared/lib/data-access/store/querybuilderSlice.ts
index 98e282a8c..9c5fe1fc6 100644
--- a/libs/shared/lib/data-access/store/querybuilderSlice.ts
+++ b/libs/shared/lib/data-access/store/querybuilderSlice.ts
@@ -11,6 +11,7 @@ export type QueryBuilderSettings = {
   limit: number;
   depth: { min: number; max: number };
   layout: AllLayoutAlgorithms | 'manual';
+  autocompleteRelation: boolean;
 };
 
 export type QueryBuilderState = {
@@ -27,6 +28,7 @@ export const initialState: QueryBuilderState = {
     limit: 500,
     depth: { min: 1, max: 1 },
     layout: 'manual',
+    autocompleteRelation: true,
   },
   // schemaLayout: 'Graphology_noverlap',
 };
diff --git a/libs/shared/lib/data-access/store/schemaSlice.ts b/libs/shared/lib/data-access/store/schemaSlice.ts
index 691cb7088..85097c6d0 100644
--- a/libs/shared/lib/data-access/store/schemaSlice.ts
+++ b/libs/shared/lib/data-access/store/schemaSlice.ts
@@ -9,6 +9,7 @@ import { SchemaFromBackend, SchemaGraph, SchemaGraphology } from '../../schema';
 export type SchemaSettings = {
   connectionType: 'connection' | 'bezier' | 'straight' | 'step';
   layoutName: AllLayoutAlgorithms;
+  animatedEdges: boolean;
 };
 
 type schemaSliceI = {
@@ -22,7 +23,8 @@ export const initialState: schemaSliceI = {
   // layoutName: 'Cytoscape_fcose',
   settings: {
     connectionType: 'connection',
-    layoutName: Layouts.KLAY,
+    layoutName: Layouts.DAGRE,
+    animatedEdges: false,
   },
 };
 export const schemaSlice = createSlice({
diff --git a/libs/shared/lib/graph-layout/cytoscape-layouts.ts b/libs/shared/lib/graph-layout/cytoscape-layouts.ts
index 4112c1f40..42d5d5e0c 100644
--- a/libs/shared/lib/graph-layout/cytoscape-layouts.ts
+++ b/libs/shared/lib/graph-layout/cytoscape-layouts.ts
@@ -438,6 +438,10 @@ class CytoscapeDagre extends Cytoscape {
 
     const layout = cy.layout({
       name: 'dagre',
+      animate: false,
+      // acyclicer: 'greedy',
+      ranker: 'longest-path',
+      spacingFactor: 0.7,
       ready: function () {
         // console.log('Layout.start');
       }, // on layoutready
diff --git a/libs/shared/lib/querybuilder/panel/querybuilder.tsx b/libs/shared/lib/querybuilder/panel/querybuilder.tsx
index e6d3b474e..0bab30e6b 100644
--- a/libs/shared/lib/querybuilder/panel/querybuilder.tsx
+++ b/libs/shared/lib/querybuilder/panel/querybuilder.tsx
@@ -1,12 +1,13 @@
-import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
 import {
   useConfig,
-  useQuerybuilder,
   useQuerybuilderGraph,
   useQuerybuilderSettings,
   useSchemaGraph,
   useSearchResultQB,
 } from '@graphpolaris/shared/lib/data-access/store';
+import { clearQB, setQuerybuilderGraphology, toQuerybuilderGraphology } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
+import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import { useDispatch } from 'react-redux';
 import ReactFlow, {
   Background,
   Connection,
@@ -22,24 +23,22 @@ import ReactFlow, {
   isNode,
   useReactFlow,
 } from 'reactflow';
-import styles from './querybuilder.module.scss';
-import { clearQB, setQuerybuilderGraphology, toQuerybuilderGraphology } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
-import { useDispatch } from 'react-redux';
+import { Dialog } from '../../components/Dialog';
+import { Button } from '../../components/buttons';
+import ControlContainer from '../../components/controls';
+import { addError } from '../../data-access/store/configSlice';
+import { toSchemaGraphology } from '../../data-access/store/schemaSlice';
+import { LayoutFactory } from '../../graph-layout';
 import { AllLogicMap, QueryElementTypes, createReactFlowElements, isLogicHandle, toHandleData } from '../model';
 import { ConnectionDragLine, ConnectionLine, EntityFlowElement, RelationPill } from '../pills';
 import LogicPill from '../pills/customFlowPills/logicpill/logicpill';
 import { dragPillStarted, movePillTo } from '../pills/dragging/dragPill';
-import { Dialog } from '../../components/Dialog';
+import styles from './querybuilder.module.scss';
 import { QueryBuilderLogicPillsPanel } from './querysidepanel/queryBuilderLogicPillsPanel';
+import { QueryBuilderRelatedNodesPanel } from './querysidepanel/queryBuilderRelatedNodesPanel';
 import { QueryMLDialog } from './querysidepanel/queryMLDialog';
 import { QuerySettingsDialog } from './querysidepanel/querySettingsDialog';
-import { toSchemaGraphology } from '../../data-access/store/schemaSlice';
-import { LayoutFactory } from '../../graph-layout';
 import { ConnectingNodeDataI } from './utils/connectorDrop';
-import { QueryBuilderRelatedNodesPanel } from './querysidepanel/queryBuilderRelatedNodesPanel';
-import { addError } from '../../data-access/store/configSlice';
-import ControlContainer from '../../components/controls';
-import { Button } from '../../components/buttons';
 
 export type QueryBuilderProps = {
   onRunQuery?: () => void;
@@ -211,6 +210,53 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
           // sendQuery();
         }
 
+        if (queryBuilderSettings.autocompleteRelation == true) {
+          let fromNodeID: any = null;
+          let toNodeID: any = null;
+          schema.nodes().forEach((node: string) => {
+            if (node === dragData.from) fromNodeID = node;
+            if (node === dragData.to) toNodeID = node;
+
+            if (fromNodeID && toNodeID) return;
+          });
+
+          if (fromNodeID && toNodeID) {
+            const fromNode = graphologyGraph.addPill2Graphology(
+              {
+                type: QueryElementTypes.Entity,
+                x: position.x - 180,
+                y: position.y,
+                name: fromNodeID,
+                schemaKey: fromNodeID,
+              },
+              schema.getNodeAttribute(fromNodeID, 'attributes')
+            );
+
+            const toNode = graphologyGraph.addPill2Graphology(
+              {
+                type: QueryElementTypes.Entity,
+                x: position.x + 250,
+                y: position.y,
+                name: toNodeID,
+                schemaKey: toNodeID,
+              },
+              schema.getNodeAttribute(toNodeID, 'attributes')
+            );
+
+            graphologyGraph.addEdge2Graphology(fromNode, relation, {
+              type: 'connection',
+              sourceHandleData: toHandleData(fromNodeID),
+              targetHandleData: toHandleData(dragData.collection),
+            });
+
+            graphologyGraph.addEdge2Graphology(relation, toNode, {
+              type: 'connection',
+              sourceHandleData: toHandleData(dragData.collection),
+              targetHandleData: toHandleData(toNodeID),
+            });
+          }
+        }
+
         dispatch(setQuerybuilderGraphology(graphologyGraph));
         break;
       default:
diff --git a/libs/shared/lib/querybuilder/panel/querysidepanel/querySettingsDialog.tsx b/libs/shared/lib/querybuilder/panel/querysidepanel/querySettingsDialog.tsx
index cae5133bc..bddfd287a 100644
--- a/libs/shared/lib/querybuilder/panel/querysidepanel/querySettingsDialog.tsx
+++ b/libs/shared/lib/querybuilder/panel/querysidepanel/querySettingsDialog.tsx
@@ -4,8 +4,9 @@ import React from 'react';
 import { useAppDispatch, useQuerybuilderSettings } from '../../../data-access';
 import { QueryBuilderSettings, setQuerybuilderSettings } from '../../../data-access/store/querybuilderSlice';
 import { addWarning } from '../../../data-access/store/configSlice';
-import { FormActions, FormBody, FormCard, FormDiv, FormHBar, FormTitle } from '../../../components/forms';
+import { FormActions, FormBody, FormCard, FormControl, FormDiv, FormHBar, FormTitle } from '../../../components/forms';
 import { Layouts } from '@graphpolaris/shared/lib/graph-layout';
+import Input from '@graphpolaris/shared/lib/components/inputs';
 
 type QuerySettingsDialogProps = DialogProps;
 
@@ -43,6 +44,19 @@ export const QuerySettingsDialog = React.forwardRef<HTMLDivElement, QuerySetting
               }}
             >
               <FormTitle title="Query Settings" onClose={props.onClose} />
+              <FormHBar />
+              <FormControl>
+                <Input
+                  type="boolean"
+                  value={state.autocompleteRelation}
+                  label="Autocomplete Edges"
+                  tooltip="When enabled, if you drag a relationship to the query, the query builder will automatically add the entity nodes it is connected to."
+                  onChange={(value: boolean) => {
+                    setState({ ...state, autocompleteRelation: value as any });
+                  }}
+                />
+              </FormControl>
+
               <FormHBar />
               <div className="form-control px-5">
                 <label className="label">
diff --git a/libs/shared/lib/querybuilder/panel/shemaquerybuilder.stories.tsx b/libs/shared/lib/querybuilder/panel/schemaquerybuilder.stories.tsx
similarity index 97%
rename from libs/shared/lib/querybuilder/panel/shemaquerybuilder.stories.tsx
rename to libs/shared/lib/querybuilder/panel/schemaquerybuilder.stories.tsx
index bb9dc3aac..63b141e88 100644
--- a/libs/shared/lib/querybuilder/panel/shemaquerybuilder.stories.tsx
+++ b/libs/shared/lib/querybuilder/panel/schemaquerybuilder.stories.tsx
@@ -32,7 +32,7 @@ const SchemaAndQueryBuilder = () => {
 
 const Component: Meta = {
   component: SchemaAndQueryBuilder,
-  title: 'Panel',
+  title: 'Integration/Schema and QueryBuilder',
   decorators: [
     // using the real store here
     (story) => (
diff --git a/libs/shared/lib/querybuilder/pills/customFlowLines/connection.tsx b/libs/shared/lib/querybuilder/pills/customFlowLines/connection.tsx
index 9bded7d29..3504f6d57 100644
--- a/libs/shared/lib/querybuilder/pills/customFlowLines/connection.tsx
+++ b/libs/shared/lib/querybuilder/pills/customFlowLines/connection.tsx
@@ -1,5 +1,4 @@
-import React from 'react';
-import { EdgeProps, getSmoothStepPath, Position, Background, MarkerType, BaseEdge } from 'reactflow';
+import { EdgeProps, Position, getSmoothStepPath } from 'reactflow';
 
 import './connection.scss';
 
@@ -53,10 +52,10 @@ export function ConnectionLine({ id, sourceX, sourceY, targetX, targetY, style,
     targetX: targetX,
     targetY: targetY,
     targetPosition,
+    offset: Math.abs(targetX - sourceX) / 10,
+    borderRadius: Math.abs(targetX - sourceX) / 10,
   });
 
-  // console.log(source, target, path);
-
   return (
     <g stroke="#2e2e2e">
       <path id={id} fill="none" strokeWidth={1} style={style} d={path[0]} />
diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx
index 56777be2c..7f2cac788 100644
--- a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx
+++ b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx
@@ -18,7 +18,7 @@ export const EntityFlowElement = React.memo((node: SchemaReactflowEntityNode) =>
   const graph = useQuerybuilderGraph();
   const attributeEdges = useMemo(
     () => graph.edges.filter((edge) => edge.source === node.id && !!edge?.attributes?.sourceHandleData.attributeType),
-    [graph],
+    [graph]
   );
 
   const [hovered, setHovered] = useState(false);
@@ -54,13 +54,13 @@ export const EntityFlowElement = React.memo((node: SchemaReactflowEntityNode) =>
             handle={data.leftRelationHandleId}
             type="target"
             position={Position.Left}
-            className={'!top-8 !left-2 !bg-accent-700 !rounded-none w-2 h-2'}
+            className={'!top-8 !left-2 !bg-accent-700 !rounded-none'}
           />
           <FilterHandle
             handle={data.rightRelationHandleId}
             type="source"
             position={Position.Right}
-            className={'!top-8 !right-2 !bg-accent-700 !rounded-none w-2 h-2'}
+            className={'!top-8 !right-2 !bg-accent-700 !rounded-none'}
           />
           <div className="text-center py-1">{data.name}</div>
           {data?.attributes && (
diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-handles.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-handles.tsx
index 56fcf6848..61ea6fc06 100644
--- a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-handles.tsx
+++ b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relation-handles.tsx
@@ -13,18 +13,21 @@ const getArrow = {
 };
 export type RelationshipHandleArrowType = 'right' | 'left' | 'both';
 
-type Props = { handle: QueryGraphEdgeHandle; type: HandleType; point: RelationshipHandleArrowType; onDoubleClick?: () => void };
+type Props = {
+  handle: QueryGraphEdgeHandle;
+  type: HandleType;
+  point: RelationshipHandleArrowType;
+  onDoubleClick?: () => void;
+};
 
 export const LeftHandle = (props: Props) => {
-  const offset = 15;
-
   return (
     <FilterHandle
       handle={props.handle}
       type={props.type}
       position={Position.Left}
-      className="!top-4"
-      style={{ transform: `translate(${offset}px, -3px)` }}
+      className="!top- !left-2"
+      // style={{ transform: `translate(${offsetX}px, ${offsetY}px)` }}
       onDoubleClickCapture={(e) => {
         e.preventDefault();
         e.stopPropagation();
@@ -39,15 +42,13 @@ export const LeftHandle = (props: Props) => {
 };
 
 export const RightHandle = (props: Props) => {
-  const offset = -13;
-
   return (
     <FilterHandle
       handle={props.handle}
       type={props.type}
       position={Position.Right}
-      className="!top-4"
-      style={{ transform: `translate(${offset}px, -3px)` }}
+      className="!top-8 !right-2"
+      // style={{ transform: `translate(${offsetX}px, ${offsetY}px)` }}
       onDoubleClickCapture={(e) => {
         e.preventDefault();
         e.stopPropagation();
diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.tsx
index b1ec0d735..7280803b6 100644
--- a/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.tsx
+++ b/libs/shared/lib/querybuilder/pills/customFlowPills/relationpill/relationpill.tsx
@@ -18,11 +18,11 @@ export const RelationPill = memo((node: SchemaReactflowRelationNode) => {
   const dispatch = useAppDispatch();
   const graphologyNodeAttributes = useMemo<RelationNodeAttributes | undefined>(
     () => (graphologyGraph.hasNode(node.id) ? { ...(graphologyGraph.getNodeAttributes(node.id) as RelationNodeAttributes) } : undefined),
-    [node.id],
+    [node.id]
   );
   const attributeEdges = useMemo(
     () => graph.edges.filter((edge) => edge.source === node.id && !!edge?.attributes?.sourceHandleData.attributeType),
-    [graph],
+    [graph]
   );
   const [hovered, setHovered] = useState(false);
   const [handleBeingDragged, setHandleBeingDragged] = useState(-1);
@@ -85,84 +85,85 @@ export const RelationPill = memo((node: SchemaReactflowRelationNode) => {
   };
 
   return (
-    <div
-      className={`rounded-sm shadow min-w-[200px] text-[13px] bg-gradient-to-r pt-1 from-[#4893D4] to-[#1A476E]`}
-      onMouseEnter={onMouseEnter}
-      onMouseLeave={onMouseLeave}
-    >
-      <div className={`py-1 ${data.selected ? 'bg-secondary-400' : 'bg-secondary-50'}`}>
-        <span>
-          {data.rightEntityHandleId && (
-            <RightHandle handle={data.rightEntityHandleId} type="source" point={direction} onDoubleClick={onChangeDirection} />
-          )}
-        </span>
-        <div className="px-10">
-          <span>{data?.name}</span>
+    <div className="p-3 bg-transparent" onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
+      <div className={`rounded-sm shadow min-w-[8rem] text-[0.8rem] bg-gradient-to-r pt-1 from-[#4893D4] to-[#1A476E]`}>
+        <div className={`py-1 ${data.selected ? 'bg-secondary-400' : 'bg-secondary-50'}`}>
+          <div className="px-10">
+            <div className="text-center py-1">
+              {data?.name}
+              <span>
+                <span> [</span>
+                <input
+                  className={
+                    'bg-inherit text-center appearance-none mx-0.1 rounded-sm ' +
+                    (depth.min < 0 || depth.min > depth.max ? ' bg-danger-400 ' : '')
+                  }
+                  style={{ maxWidth: calcWidth(depth.min) }}
+                  type="number"
+                  min={0}
+                  placeholder={'?'}
+                  value={depth.min}
+                  onChange={(e) => {
+                    setDepth({ ...depth, min: parseInt(e.target.value) });
+                  }}
+                  onBlur={(e) => {
+                    onNodeUpdated();
+                  }}
+                  onKeyDown={(e) => {
+                    if (e.key === 'Enter') {
+                      onNodeUpdated();
+                    }
+                  }}
+                ></input>
+                <span>..</span>
+                <input
+                  className={
+                    'bg-inherit text-center appearance-none mx-0.1 rounded-sm ' +
+                    (depth.max > 99 || depth.min > depth.max ? ' bg-danger-400 ' : '')
+                  }
+                  style={{ maxWidth: calcWidth(depth.max) }}
+                  type="number"
+                  min={1}
+                  placeholder={'?'}
+                  value={depth.max}
+                  onChange={(e) => {
+                    setDepth({ ...depth, max: parseInt(e.target.value) });
+                  }}
+                  onBlur={(e) => {
+                    onNodeUpdated();
+                  }}
+                  onKeyDown={(e) => {
+                    if (e.key === 'Enter') {
+                      onNodeUpdated();
+                    }
+                  }}
+                ></input>
+                <span>]</span>
+              </span>
+            </div>
+          </div>
           <span>
-            <span>[</span>
-            <input
-              className={
-                'bg-inherit text-center appearance-none mx-0.5 rounded-sm ' +
-                (depth.min < 0 || depth.min > depth.max ? ' bg-danger-400 ' : '')
-              }
-              style={{ maxWidth: calcWidth(depth.min) }}
-              type="number"
-              min={0}
-              placeholder={'?'}
-              value={depth.min}
-              onChange={(e) => {
-                setDepth({ ...depth, min: parseInt(e.target.value) });
-              }}
-              onBlur={(e) => {
-                onNodeUpdated();
-              }}
-              onKeyDown={(e) => {
-                if (e.key === 'Enter') {
-                  onNodeUpdated();
-                }
-              }}
-            ></input>
-            <span>..</span>
-            <input
-              className={
-                'bg-inherit text-center appearance-none mx-0.5 rounded-sm ' +
-                (depth.max > 99 || depth.min > depth.max ? ' bg-danger-400 ' : '')
-              }
-              style={{ maxWidth: calcWidth(depth.max) }}
-              type="number"
-              min={1}
-              placeholder={'?'}
-              value={depth.max}
-              onChange={(e) => {
-                setDepth({ ...depth, max: parseInt(e.target.value) });
-              }}
-              onBlur={(e) => {
-                onNodeUpdated();
-              }}
-              onKeyDown={(e) => {
-                if (e.key === 'Enter') {
-                  onNodeUpdated();
-                }
-              }}
-            ></input>
-            <span>]</span>
+            {data.leftEntityHandleId && (
+              <LeftHandle handle={data.leftEntityHandleId} type="target" point={'left'} onDoubleClick={onChangeDirection} />
+            )}
           </span>
-        </div>
-        <span>
-          {data.leftEntityHandleId && (
-            <LeftHandle handle={data.leftEntityHandleId} type="target" point={'left'} onDoubleClick={onChangeDirection} />
+
+          <span>
+            {data.rightEntityHandleId && (
+              <RightHandle handle={data.rightEntityHandleId} type="source" point={direction} onDoubleClick={onChangeDirection} />
+            )}
+          </span>
+          {data?.attributes && (
+            <PillDropdown
+              node={node}
+              attributes={data.attributes}
+              attributeEdges={attributeEdges.map((edge) => edge?.attributes)}
+              hovered={hovered}
+              handleBeingDraggedIdx={handleBeingDragged}
+              onHandleMouseDown={onHandleMouseDown}
+            />
           )}
-        </span>
-        {data?.attributes && (
-          <PillDropdown
-            node={node}
-            attributes={data.attributes}
-            attributeEdges={attributeEdges.map((edge) => edge?.attributes)}
-            hovered={hovered}
-            handleBeingDraggedIdx={handleBeingDragged}
-            onHandleMouseDown={onHandleMouseDown}
-          />
-        )}
+        </div>
       </div>
     </div>
   );
diff --git a/libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts b/libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts
index b57c1eaa9..6438a1e19 100644
--- a/libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts
+++ b/libs/shared/lib/querybuilder/query-utils/query2backend.spec.ts
@@ -20,6 +20,7 @@ const defaultSettings: QueryBuilderSettings = {
   limit: 500,
   depth: { min: 0, max: 1 },
   layout: 'manual',
+  autocompleteRelation: true,
 };
 
 describe('QueryUtils Entity and Relations', () => {
diff --git a/libs/shared/lib/schema/model/reactflow.tsx b/libs/shared/lib/schema/model/reactflow.tsx
index cecfd06a2..f6fbbb3db 100644
--- a/libs/shared/lib/schema/model/reactflow.tsx
+++ b/libs/shared/lib/schema/model/reactflow.tsx
@@ -36,6 +36,7 @@ export interface SchemaReactflowData {
   summedNullAmount: number;
   label: string;
   type: string;
+  hovered: boolean;
 }
 
 export interface SchemaReactflowEntity extends SchemaReactflowData {
diff --git a/libs/shared/lib/schema/panel/schema.tsx b/libs/shared/lib/schema/panel/schema.tsx
index 73b9a8fd7..531bd450a 100644
--- a/libs/shared/lib/schema/panel/schema.tsx
+++ b/libs/shared/lib/schema/panel/schema.tsx
@@ -1,24 +1,24 @@
-import { AlgorithmToLayoutProvider, AllLayoutAlgorithms, LayoutFactory } from '@graphpolaris/shared/lib/graph-layout';
-import { schemaGraphology2Reactflow, schemaExpandRelation } from '@graphpolaris/shared/lib/schema/schema-utils';
 import { useSchemaGraph, useSchemaSettings, useSearchResultSchema, useSessionCache } from '@graphpolaris/shared/lib/data-access/store';
+import { AlgorithmToLayoutProvider, AllLayoutAlgorithms, LayoutFactory } from '@graphpolaris/shared/lib/graph-layout';
+import { schemaExpandRelation, schemaGraphology2Reactflow } from '@graphpolaris/shared/lib/schema/schema-utils';
 import { SmartBezierEdge, SmartStepEdge, SmartStraightEdge } from '@tisoap/react-flow-smart-edge';
 
 import { useEffect, useMemo, useRef, useState } from 'react';
-import ReactFlow, { Node, Edge, ReactFlowProvider, useNodesState, useEdgesState, ReactFlowInstance } from 'reactflow';
+import ReactFlow, { Edge, Node, ReactFlowInstance, ReactFlowProvider, useEdgesState, useNodesState } from 'reactflow';
 
 import 'reactflow/dist/style.css';
 
-import { ConnectionDragLine, ConnectionLine } from '@graphpolaris/shared/lib/querybuilder/pills';
-import { EntityNode } from '../pills/nodes/entity/entity-node';
-import { RelationNode } from '../pills/nodes/relation/relation-node';
-import NodeEdge from '../pills/edges/node-edge';
-import SelfEdge from '../pills/edges/self-edge';
-import { SchemaDialog } from './schemaDialog';
 import { wsSchemaRequest } from '@graphpolaris/shared/lib/data-access/api/wsSchema';
-import { toSchemaGraphology } from '../../data-access/store/schemaSlice';
+import { ConnectionDragLine, ConnectionLine } from '@graphpolaris/shared/lib/querybuilder/pills';
 import { Button } from '../../components/buttons';
 import ControlContainer from '../../components/controls';
 import { wsGetStates } from '../../data-access';
+import { toSchemaGraphology } from '../../data-access/store/schemaSlice';
+import NodeEdge from '../pills/edges/node-edge';
+import SelfEdge from '../pills/edges/self-edge';
+import { EntityNode } from '../pills/nodes/entity/entity-node';
+import { RelationNode } from '../pills/nodes/relation/relation-node';
+import { SchemaDialog } from './schemaDialog';
 
 interface Props {
   content?: string;
@@ -47,8 +47,8 @@ export const Schema = (props: Props) => {
   const settings = useSchemaSettings();
   const searchResults = useSearchResultSchema();
   const [toggleSchemaSettings, setToggleSchemaSettings] = useState(false);
-  const [nodes, setNodes, onNodeChanged] = useNodesState([] as Node[]);
-  const [edges, setEdges, onEdgeChanged] = useEdgesState([] as Edge[]);
+  const [nodes, setNodes, onNodesChange] = useNodesState([] as Node[]);
+  const [edges, setEdges, onEdgesChange] = useEdgesState([] as Edge[]);
   const [firstUserConnection, setFirstUserConnection] = useState<boolean>(true);
   const [auth, setAuth] = useState(props.auth);
 
@@ -89,7 +89,7 @@ export const Schema = (props: Props) => {
     updateLayout();
     const expandedSchema = schemaExpandRelation(schemaGraphology);
     layout.current?.layout(expandedSchema);
-    const schemaFlow = schemaGraphology2Reactflow(expandedSchema, settings.connectionType);
+    const schemaFlow = schemaGraphology2Reactflow(expandedSchema, settings.connectionType, settings.animatedEdges);
     setNodes(schemaFlow.nodes);
     setEdges(schemaFlow.edges);
   }, [schemaGraph, settings]);
@@ -146,8 +146,8 @@ export const Schema = (props: Props) => {
               nodeTypes={nodeTypes}
               edgeTypes={edgeTypes}
               connectionLineComponent={ConnectionDragLine}
-              onNodesChange={onNodeChanged}
-              onEdgesChange={onEdgeChanged}
+              onNodesChange={onNodesChange}
+              onEdgesChange={onEdgesChange}
               nodes={nodes}
               edges={edges}
               onInit={(reactFlowInstance) => {
diff --git a/libs/shared/lib/schema/panel/schemaDialog.tsx b/libs/shared/lib/schema/panel/schemaDialog.tsx
index e8fbeaca8..644be97cd 100644
--- a/libs/shared/lib/schema/panel/schemaDialog.tsx
+++ b/libs/shared/lib/schema/panel/schemaDialog.tsx
@@ -11,8 +11,6 @@ export const SchemaDialog = (props: DialogProps) => {
   const settings = useSchemaSettings();
   const dispatch = useAppDispatch();
   const [state, setState] = React.useState<SchemaSettings>(settings);
-  const [display, setDisplay] = useState<string[]>([]);
-  const [opacity, setOpacity] = useState<number>(0);
 
   useEffect(() => {
     setState(settings);
@@ -34,38 +32,7 @@ export const SchemaDialog = (props: DialogProps) => {
                 submit();
               }}
             >
-              <FormTitle title="Quick Settings" onClose={props.onClose} />
-              <FormHBar />
-              <FormControl>
-                <Input
-                  type="checkbox"
-                  value={display}
-                  options={['Points', 'Line', 'Box']}
-                  onChange={(value: string[]) => {
-                    setDisplay(value);
-                  }}
-                />
-              </FormControl>
-              <FormHBar />
-              <FormControl>
-                <Input
-                  type="slider"
-                  label="Opacity"
-                  unit={'%'}
-                  value={opacity}
-                  min={0}
-                  max={100}
-                  step={1}
-                  onChange={(value: number) => setOpacity(value)}
-                />
-              </FormControl>
-              <FormHBar />
-              <FormControl>
-                <label className="label">
-                  <span className="label-text">Histogram</span>
-                </label>
-                ...
-              </FormControl>
+              <FormTitle title="Schema Settings" onClose={props.onClose} />
               <FormHBar />
               <FormControl>
                 <Input
@@ -79,6 +46,17 @@ export const SchemaDialog = (props: DialogProps) => {
                 />
               </FormControl>
               <FormHBar />
+              <FormControl>
+                <Input
+                  type="boolean"
+                  value={state.animatedEdges}
+                  label="Animated Edges"
+                  onChange={(value: boolean) => {
+                    setState({ ...state, animatedEdges: value as any });
+                  }}
+                />
+              </FormControl>
+              <FormHBar />
               <FormControl>
                 <Input
                   type="dropdown"
diff --git a/libs/shared/lib/schema/pills/edges/node-edge.tsx b/libs/shared/lib/schema/pills/edges/node-edge.tsx
index 52114f9a0..6e230d231 100644
--- a/libs/shared/lib/schema/pills/edges/node-edge.tsx
+++ b/libs/shared/lib/schema/pills/edges/node-edge.tsx
@@ -60,7 +60,7 @@ export default function NodeEdge({
         type="smoothstep"
         id={id}
         fill="none"
-        strokeWidth={0.5}
+        strokeWidth={1.5}
         style={style}
         // The d is used to create the path for the edge.
         d={`M${sourceX},${sourceY}h ${data.d} L ${targetX + data.d},${targetY} h ${-data.d}`}
diff --git a/libs/shared/lib/schema/pills/nodes/entity/SchemaEntityPopup.tsx b/libs/shared/lib/schema/pills/nodes/entity/SchemaEntityPopup.tsx
index 3e9b26c70..c0f5c9247 100644
--- a/libs/shared/lib/schema/pills/nodes/entity/SchemaEntityPopup.tsx
+++ b/libs/shared/lib/schema/pills/nodes/entity/SchemaEntityPopup.tsx
@@ -9,8 +9,9 @@
  * We do not test components/renderfunctions/styling files.
  * See testing plan for more details.*/
 
-import React from 'react';
+import { FormBody, FormCard, FormControl, FormHBar, FormTitle } from '@graphpolaris/shared/lib/components/forms';
 import { SchemaReactflowEntity } from '@graphpolaris/shared/lib/schema/model';
+import { FormEvent } from 'react';
 
 export type SchemaEntityPopupProps = {
   data: SchemaReactflowEntity;
@@ -22,30 +23,62 @@ export type SchemaEntityPopupProps = {
  * @param data Input data of type NodeQualityDataForEntities, which is for the popup.
  */
 export const SchemaEntityPopup = (props: SchemaEntityPopupProps) => {
+  function submit() {
+    // dispatch(setSchemaSettings(state));
+    props.onClose();
+  }
+
   return (
-    <div className="card card-bordered rounded-none text-[0.9rem] min-w-[10rem]">
-      <div className="card-body p-0">
-        <span className="px-2.5 pt-2">
-          <span>Nodes</span>
-          <span className="float-right">TBD</span>
-        </span>
-        <div className="h-[1px] w-full bg-secondary-200"></div>
-        <div className="px-2.5 text-[0.8rem]">
-          <p>
-            Null Values: <span className="float-right">TBD</span>
-          </p>
-          <p>
-            Not connected: <span className="float-right">TBD</span>
-          </p>
-        </div>
-        <div className="h-[1px] w-full bg-secondary-200"></div>
-        <button
-          className="btn btn-outline btn-accent border-0 btn-sm p-0 m-0 text-[0.8rem] mb-2 mx-2.5 min-h-0 h-5"
-          onClick={() => props.onClose()}
+    // <FormDiv hAnchor="left">
+    <>
+      <FormCard>
+        <FormBody
+          onSubmit={(e: FormEvent<HTMLFormElement>) => {
+            e.preventDefault();
+            submit();
+          }}
         >
-          Close
-        </button>
-      </div>
-    </div>
+          <FormTitle
+            title="Node Statistics"
+            // title={props.data.name}
+            onClose={props.onClose}
+          />
+          <FormHBar />
+
+          <span className="px-5 pt-2">
+            <span>Name</span>
+            <span className="float-right break-all text-wrap text-pretty font-light font-mono">{props.data.name}</span>
+          </span>
+
+          <FormHBar />
+
+          <span className="px-5 pt-2">
+            <span>Attributes</span>
+            <span className="float-right font-light font-mono">{props.data.attributes.length}</span>
+          </span>
+
+          {props.data.attributes.map((attribute: any) => {
+            return (
+              <div key={attribute.name} className="px-5 pt-1">
+                <span>{attribute.name}</span>
+                <span className="float-right font-light font-mono">{attribute.type}</span>
+              </div>
+            );
+          })}
+          <FormHBar />
+
+          <FormControl>
+            <button
+              className="btn btn-outline btn-accent border-0 btn-sm p-0 m-0 text-[0.8rem] mb-2 mx-2.5 min-h-0 h-5"
+              onClick={() => {
+                submit();
+              }}
+            >
+              Close
+            </button>
+          </FormControl>
+        </FormBody>
+      </FormCard>
+    </>
   );
 };
diff --git a/libs/shared/lib/schema/pills/nodes/entity/entity-node.stories.tsx b/libs/shared/lib/schema/pills/nodes/entity/entity-node.stories.tsx
index 23db8c916..9bf1fe02b 100644
--- a/libs/shared/lib/schema/pills/nodes/entity/entity-node.stories.tsx
+++ b/libs/shared/lib/schema/pills/nodes/entity/entity-node.stories.tsx
@@ -46,7 +46,49 @@ export const Default = {
     const schema = SchemaUtils.schemaBackend2Graphology({
       nodes: [
         {
-          name: 'Thijs',
+          name: 'NodeDefault',
+          attributes: [
+            { name: 'city', type: 'string' },
+            { name: 'vip', type: 'bool' },
+            { name: 'state', type: 'string' },
+          ],
+        },
+      ],
+      edges: [],
+    });
+
+    dispatch(setSchema(schema.export()));
+  },
+};
+
+export const TooLongTextLabel = {
+  play: async () => {
+    const dispatch = Mockstore.dispatch;
+    const schema = SchemaUtils.schemaBackend2Graphology({
+      nodes: [
+        {
+          name: 'NodeDefaultNodeDefaultNodeDefaultNodeDefaultNodeDefaultNodeDefault',
+          attributes: [
+            { name: 'city', type: 'string' },
+            { name: 'vip', type: 'bool' },
+            { name: 'state', type: 'string' },
+          ],
+        },
+      ],
+      edges: [],
+    });
+
+    dispatch(setSchema(schema.export()));
+  },
+};
+
+export const ShortTextLabel = {
+  play: async () => {
+    const dispatch = Mockstore.dispatch;
+    const schema = SchemaUtils.schemaBackend2Graphology({
+      nodes: [
+        {
+          name: 'N',
           attributes: [
             { name: 'city', type: 'string' },
             { name: 'vip', type: 'bool' },
diff --git a/libs/shared/lib/schema/pills/nodes/entity/entity-node.tsx b/libs/shared/lib/schema/pills/nodes/entity/entity-node.tsx
index f533cc908..4e343b817 100644
--- a/libs/shared/lib/schema/pills/nodes/entity/entity-node.tsx
+++ b/libs/shared/lib/schema/pills/nodes/entity/entity-node.tsx
@@ -57,12 +57,12 @@ export const EntityNode = React.memo(({ id, selected, data }: NodeProps<SchemaRe
   return (
     <>
       {openPopup && (
-        <Popup open={openPopup} hAnchor="left" className="-top-10" offset="-9rem">
+        <Popup open={openPopup} hAnchor="left" className="-top-8" offset="-20rem">
           <SchemaEntityPopup data={data} onClose={() => setOpenPopup(false)} />
         </Popup>
       )}
       <div
-        className={`rounded-sm transition-all duration-150 shadow shadow-dark/10 hover:shadow-md hover:shadow-dark/20 min-w-[8rem] text-[0.8rem] bg-gradient-to-r pt-1 from-[#FFA952] to-[#D66700]`}
+        className={`rounded-sm transition-all duration-150 shadow shadow-dark/10 hover:shadow-md hover:shadow-dark/20 min-w-[4rem] max-w-[8rem] text-[0.8rem] bg-gradient-to-r pt-1 from-[#FFA952] to-[#D66700]`}
         onDragStart={(event) => onDragStart(event)}
         onDragStartCapture={(event) => onDragStart(event)}
         onMouseDownCapture={(event) => {
@@ -88,7 +88,7 @@ export const EntityNode = React.memo(({ id, selected, data }: NodeProps<SchemaRe
             className={styles.handleTriangleRight}
             type="source"
           ></Handle>
-          <div className="p-2 py-1 text-center">
+          <div style={{ pointerEvents: 'none' }} className="p-2 py-1 text-center truncate">
             <span className="">{id}</span>
           </div>
         </div>
diff --git a/libs/shared/lib/schema/pills/nodes/relation/SchemaRelationshipPopup.tsx b/libs/shared/lib/schema/pills/nodes/relation/SchemaRelationshipPopup.tsx
index d87169926..81767e432 100644
--- a/libs/shared/lib/schema/pills/nodes/relation/SchemaRelationshipPopup.tsx
+++ b/libs/shared/lib/schema/pills/nodes/relation/SchemaRelationshipPopup.tsx
@@ -9,10 +9,9 @@
  * We do not test components/renderfunctions/styling files.
  * See testing plan for more details.*/
 
-import { FormBody, FormDiv, FormCard, FormHBar } from '@graphpolaris/shared/lib/components/forms';
+import { FormBody, FormCard, FormControl, FormHBar, FormTitle } from '@graphpolaris/shared/lib/components/forms';
 import { SchemaReactflowRelation } from '@graphpolaris/shared/lib/schema/model';
-import React from 'react';
-import { NodeProps } from 'reactflow';
+import { FormEvent } from 'react';
 
 export type SchemaRelationshipPopupProps = {
   data: SchemaReactflowRelation;
@@ -24,30 +23,68 @@ export type SchemaRelationshipPopupProps = {
  * @param data Input data of type NodeQualityDataForEntities, which is for the popup.
  */
 export const SchemaRelationshipPopup = (props: SchemaRelationshipPopupProps) => {
+  function submit() {
+    // dispatch(setSchemaSettings(state));
+  }
+
+  console.log('data relation popup', props);
   return (
-    <div className="card card-bordered rounded-none text-[0.9rem] min-w-[10rem]">
-      <div className="card-body p-0">
-        <span className="px-2.5 pt-2">
-          <span>Relationships</span>
-          <span className="float-right">TBD</span>
-        </span>
-        <div className="h-[1px] w-full bg-secondary-200"></div>
-        <div className="px-2.5 text-[0.8rem]">
-          <p>
-            Null Values: <span className="float-right">TBD</span>
-          </p>
-          <p>
-            Not connected: <span className="float-right">TBD</span>
-          </p>
-        </div>
-        <div className="h-[1px] w-full bg-secondary-200"></div>
-        <button
-          className="btn btn-outline btn-primary border-0 btn-sm p-0 m-0 text-[0.8rem] mb-2 mx-2.5 min-h-0 h-5"
-          onClick={() => props.onClose()}
+    <>
+      <FormCard>
+        <FormBody
+          onSubmit={(e: FormEvent<HTMLFormElement>) => {
+            e.preventDefault();
+            submit();
+          }}
         >
-          Close
-        </button>
-      </div>
-    </div>
+          <FormTitle
+            title="Edge Statistics"
+            onClose={props.onClose}
+          />
+          <FormHBar />
+
+          <span className="px-5">
+            <span>Name</span>
+            <span className="float-right break-all text-wrap text-pretty font-light font-mono">{props.data.collection}</span>
+          </span>
+          <span className="px-5">
+            <span>From</span>
+            <span className="float-right break-all text-wrap text-pretty font-light font-mono">{props.data.from}</span>
+          </span>
+          <span className="px-5">
+            <span>To</span>
+            <span className="float-right break-all text-wrap text-pretty font-light font-mono">{props.data.to}</span>
+          </span>
+
+          <FormHBar />
+
+          <span className="px-5 pt-2">
+            <span>Attributes</span>
+            <span className="float-right font-light font-mono">{Object.keys(props.data.attributes).length}</span>
+          </span>
+
+          {Object.values(props.data.attributes).map((attribute: any) => {
+            return (
+              <div key={attribute.name} className="px-5">
+                <span>{attribute.name}</span>
+                <span className="float-right font-light font-mono">{attribute.type}</span>
+              </div>
+            );
+          })}
+          <FormHBar />
+
+          <FormControl>
+            <button
+              className="btn btn-outline btn-accent border-0 btn-sm p-0 m-0 text-[0.8rem] mb-2 mx-2.5 min-h-0 h-5"
+              onClick={() => {
+                submit();
+              }}
+            >
+              Close
+            </button>
+          </FormControl>
+        </FormBody>
+      </FormCard>
+    </>
   );
 };
diff --git a/libs/shared/lib/schema/pills/nodes/relation/relation-node.stories.tsx b/libs/shared/lib/schema/pills/nodes/relation/relation-node.stories.tsx
index bb8bc3f1b..c62745747 100644
--- a/libs/shared/lib/schema/pills/nodes/relation/relation-node.stories.tsx
+++ b/libs/shared/lib/schema/pills/nodes/relation/relation-node.stories.tsx
@@ -46,7 +46,7 @@ export const Default = {
     const schema = SchemaUtils.schemaBackend2Graphology({
       nodes: [
         {
-          name: 'Thijs',
+          name: 'Node',
           attributes: [
             { name: 'city', type: 'string' },
             { name: 'vip', type: 'bool' },
@@ -56,11 +56,44 @@ export const Default = {
       ],
       edges: [
         {
-          name: 'Thijs:Thijs',
-          label: 'Thijs:Thijs',
-          from: 'Thijs',
-          to: 'Thijs',
-          collection: 'flights',
+          name: 'Node:Node',
+          label: 'Node:Node',
+          from: 'Node',
+          to: 'Node',
+          collection: 'Flights',
+          attributes: [
+            { name: 'arrivalTime', type: 'int' },
+            { name: 'departureTime', type: 'int' },
+          ],
+        },
+      ],
+    });
+
+    dispatch(setSchema(schema.export()));
+  },
+};
+
+export const TooLongLabel = {
+  play: async () => {
+    const dispatch = Mockstore.dispatch;
+    const schema = SchemaUtils.schemaBackend2Graphology({
+      nodes: [
+        {
+          name: 'Node',
+          attributes: [
+            { name: 'city', type: 'string' },
+            { name: 'vip', type: 'bool' },
+            { name: 'state', type: 'string' },
+          ],
+        },
+      ],
+      edges: [
+        {
+          name: 'Node:Node',
+          label: 'Node:Node',
+          from: 'Node',
+          to: 'Node',
+          collection: 'FlightsFlightsFlightsFlightsFlightsFlightsFlightsFlights',
           attributes: [
             { name: 'arrivalTime', type: 'int' },
             { name: 'departureTime', type: 'int' },
diff --git a/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx b/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx
index 6520c642e..5cb30ac7f 100644
--- a/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx
+++ b/libs/shared/lib/schema/pills/nodes/relation/relation-node.tsx
@@ -22,7 +22,7 @@ import { SchemaEdge } from '../../../model';
  * Can be dragged and dropped to the query builder.
  * @param {NodeProps} param0 The data of an entity flow element.
  */
-export const RelationNode = React.memo(({ id, selected, data }: NodeProps<SchemaReactflowRelationWithFunctions>) => {
+export const RelationNode = React.memo(({ id, selected, data, ...props }: NodeProps<SchemaReactflowRelationWithFunctions>) => {
   const [openPopup, setOpenPopup] = useState(false);
 
   /**
@@ -37,7 +37,9 @@ export const RelationNode = React.memo(({ id, selected, data }: NodeProps<Schema
       to: data.to,
       collection: data.collection,
       label: data.label,
-      attributes: data.attributes.map((attribute) => attribute.attributes).flat(), // TODO this seems wrong
+      attributes: Array.from(data.attributes)
+        .map((attribute) => attribute.attributes)
+        .flat(),
     };
     event.dataTransfer.setData('application/reactflow', JSON.stringify(eventData));
     event.dataTransfer.effectAllowed = 'move';
@@ -60,7 +62,7 @@ export const RelationNode = React.memo(({ id, selected, data }: NodeProps<Schema
   return (
     <>
       {openPopup && (
-        <Popup open={openPopup} hAnchor="left" className="-top-10" offset="-9rem">
+        <Popup open={openPopup} hAnchor="left" className="-top-8" offset="-20rem">
           <SchemaRelationshipPopup data={data} onClose={() => setOpenPopup(false)} />
         </Popup>
       )}
@@ -76,10 +78,7 @@ export const RelationNode = React.memo(({ id, selected, data }: NodeProps<Schema
         draggable
       >
         <div
-          className={`rounded-sm transition-all duration-150 shadow shadow-dark/10 hover:shadow-md hover:shadow-dark/20 min-w-[8rem] text-[0.8rem] bg-gradient-to-r pt-1 from-[#4893D4] to-[#1A476E]`}
-          style={{
-            backgroundColor: selected ? '#97a2b6' : '#f4f6f7',
-          }}
+          className={`rounded-sm transition-all duration-150 shadow shadow-dark/10 hover:shadow-md hover:shadow-dark/20 min-w-[4rem] max-w-[8rem] text-[0.8rem] bg-gradient-to-r pt-1 from-[#4893D4] to-[#1A476E]`}
         >
           <div className={`py-1 ${selected ? 'bg-secondary-300' : 'bg-secondary-100'}`}>
             <Handle
@@ -89,7 +88,7 @@ export const RelationNode = React.memo(({ id, selected, data }: NodeProps<Schema
               position={Position.Top}
               type="target"
             ></Handle>
-            <div className="p-2 py-1 text-center">
+            <div className="p-2 py-1 text-center truncate">
               <span className="">{data.collection}</span>
             </div>
 
diff --git a/libs/shared/lib/schema/schema-utils/schema-usecases.ts b/libs/shared/lib/schema/schema-utils/schema-usecases.ts
index abbe835f0..f544f0ad7 100644
--- a/libs/shared/lib/schema/schema-utils/schema-usecases.ts
+++ b/libs/shared/lib/schema/schema-utils/schema-usecases.ts
@@ -7,7 +7,6 @@ import { QueryElementTypes } from '../../querybuilder';
 import { SchemaGraph, SchemaGraphology } from '../model';
 
 //TODO does not belong here; maybe should go into the GraphPolarisThemeProvider
-const ANIMATEDEDGES = false;
 
 export function schemaExpandRelation(graph: SchemaGraphology): SchemaGraphology {
   const newGraph = graph.copy();
@@ -25,7 +24,7 @@ export function schemaExpandRelation(graph: SchemaGraphology): SchemaGraphology
       ...attributes,
       name: edge,
       label: edge,
-      attributes: [],
+      attributes: { ...attributes.attributes },
       x: 0,
       y: 0,
       type: QueryElementTypes.Relation,
@@ -49,7 +48,8 @@ export function schemaExpandRelation(graph: SchemaGraphology): SchemaGraphology
 // Takes the schema as an input and creates basic react flow elements for them.
 export function schemaGraphology2Reactflow(
   graph: Graph,
-  defaultEdgeType: string
+  defaultEdgeType: string,
+  animatedEdges = false
 ): {
   nodes: Array<Node<SchemaReactflowNodeWithFunctions | SchemaReactflowRelationWithFunctions>>;
   edges: Array<Edge>;
@@ -60,7 +60,7 @@ export function schemaGraphology2Reactflow(
   };
 
   initialElements.nodes = createReactFlowNodes(graph);
-  initialElements.edges = createReactFlowEdges(graph, defaultEdgeType);
+  initialElements.edges = createReactFlowEdges(graph, defaultEdgeType, animatedEdges);
 
   return initialElements;
 }
@@ -82,7 +82,7 @@ export function createReactFlowNodes(graph: Graph): Array<Node> {
   return nodeElements;
 }
 
-export function createReactFlowEdges(graph: Graph, defaultEdgeType: string): Array<Edge> {
+export function createReactFlowEdges(graph: Graph, defaultEdgeType: string, animatedEdges = false): Array<Edge> {
   const edgeElements: Array<Edge> = [];
 
   graph.forEachEdge((edge, attributes, source, target): void => {
@@ -96,7 +96,7 @@ export function createReactFlowEdges(graph: Graph, defaultEdgeType: string): Arr
       },
       // label: edge,
       type: attributes?.type || defaultEdgeType,
-      animated: ANIMATEDEDGES,
+      animated: animatedEdges,
       markerEnd: MarkerType.ArrowClosed, // TODO: Check
     };
     edgeElements.push(newEdge);
@@ -104,82 +104,3 @@ export function createReactFlowEdges(graph: Graph, defaultEdgeType: string): Arr
 
   return edgeElements;
 }
-
-// export function createReactFlowRelationNodes(graph: Graph): Elements<Node> {
-//   const nodeElements: Elements<Node> = [];
-//   graph.forEachEdge((edge, attributes, source, target): void => {
-//     const newRelationNode: Node = {
-//       id: edge,
-//       data: {
-//         label: edge,
-//         name: edge,
-//       },
-//       position: { x: attributes.x, y: attributes.y },
-//       type: QueryElementTypes.Relation,
-//     };
-//     nodeElements.push(newRelationNode);
-//   });
-
-//   return nodeElements;
-// }
-
-// export function createReactFlowRelationEdges(graph: Graph): Elements<Edge> {
-//   const edgeElements: Elements<Edge> = [];
-//   graph.forEachEdge((edge, attributes, source, target): void => {
-//     const newEdgeIncoming: Edge = {
-//       //into relation node
-//       id: edge + '' + source,
-//       source: source,
-//       target: edge,
-//       // label: edge,
-//       type: 'smoothstep',
-//       animated: ANIMATEDEDGES,
-//       arrowHeadType: ArrowHeadType.ArrowClosed,
-//     };
-//     edgeElements.push(newEdgeIncoming);
-
-//     const newEdgeOutgoing: Edge = {
-//       //out of relation node
-//       id: edge + '' + target,
-//       source: edge,
-//       target: target,
-//       // label: edge,
-//       type: 'smoothstep',
-//       animated: ANIMATEDEDGES,
-//       arrowHeadType: ArrowHeadType.ArrowClosed,
-//     };
-//     edgeElements.push(newEdgeOutgoing);
-//   });
-
-//   return edgeElements;
-// }
-
-// export function parseSchemaFromBackend(
-//   schemaFromBackend: SchemaFromBackend
-// ): Graph {
-//   const { nodes, edges } = schemaFromBackend;
-//   // Instantiate a directed graph that allows self loops and parallel edges
-//   const schemaGraph = new MultiGraph({ allowSelfLoops: true });
-//   // console.log('parsing schema');
-//   // The graph schema needs a node for each node AND edge. These need then be connected
-
-//   nodes.forEach((node) => {
-//     schemaGraph.addNode(node.name, {
-//       name: node.name,
-//       attributes: node.attributes,
-//       x: 0,
-//       y: 0,
-//     });
-//   });
-
-//   // The name of the edge will be name + from + to, since edge names are not unique
-//   edges.forEach((edge) => {
-//     const edgeID = [edge.name, '_', edge.from, edge.to].join(''); //ensure that all interpreted as string
-
-//     // This node is the actual edge
-//     schemaGraph.addDirectedEdgeWithKey(edgeID, edge.from, edge.to, {
-//       attribute: edge.attributes,
-//     });
-//   });
-//   return schemaGraph;
-// }
-- 
GitLab