diff --git a/.gitignore b/.gitignore
index 81298264fd1a8b12bb82d005229e5cfd9b2e9e61..9101d3294069ecf175fdd2f7c1fb3372480c7be4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,6 +33,7 @@ npm-debug.log
 yarn-error.log
 testem.log
 /typings
+debug.log
 
 # System Files
 .DS_Store
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 692ad0934834f56991870ef680e0b15b9352e4e7..647402e214df631263ffb209d200c6ca9bd300f7 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,6 +2,7 @@ image: node:16-alpine
 stages:
   - setup
   - test
+  - versioning
   - build
   - dockerize
 
@@ -67,7 +68,7 @@ build-docker:
   only:
     - main
   script:
-    - docker build --progress plain -t $CI_PROJECT_NAME-webserver-service:latest .
+    - docker build --progress plain -t datastropheregistry.azurecr.io/frontend:latest:latest .
   # after_script:
   #   - docker login datastropheregistry.azurecr.io -u $REGISTRY_USERNAME -p $REGISTRY_PASSWORD
   #   - if [[ ! -z $CI_COMMIT_BRANCH+x ]]; then DOCKER_TAG=$CI_COMMIT_BRANCH; else DOCKER_TAG=$CI_MERGE_REQUEST_TARGET_BRANCH_NAME; fi
@@ -75,3 +76,23 @@ build-docker:
   #   - docker push datastropheregistry.azurecr.io/$CI_PROJECT_NAME-webserver-service:$DOCKER_TAG
   dependencies:
     - build
+
+#####################
+# STAGE: versioning #
+#####################
+
+versioning:
+  image: node:13
+  stage: versioning
+  only:
+    refs:
+      - release
+      - master
+  before_script:
+    # Skip before_script since we will not need the requirements
+    - ''
+  script:
+    - nx affected --target version
+  artifacts:
+    paths:
+      - CHANGELOG.md
\ No newline at end of file
diff --git a/.storybook/main.js b/.storybook/main.js
index 1492ac61f6fd000a73c9f0d5995e3cfa7074c06c..7ea37db6d3a850ab3f1d94223bb9dcefe37b664e 100644
--- a/.storybook/main.js
+++ b/.storybook/main.js
@@ -1,11 +1,27 @@
 module.exports = {
-  stories: [],
+  stories: [
+    // '../../../libs/**/*.stories.@(tsx|md)',
+    {
+      // 👇 The directory field sets the directory your stories
+      directory: '../../../libs/shared/ui/pills/src/customFlowPills/entitypill',
+      // 👇 The titlePrefix field will generate automatic titles for your stories
+      titlePrefix: 'SharedUIComponents',
+      // 👇 Storybook will load all files that contain the stories extension
+      files: '*.stories.*',
+    },
+  ],
+  // async (list) => [...list, ...findStories()],
   addons: ['@storybook/addon-essentials'],
   // uncomment the property below if you want to apply some webpack config globally
-  // webpackFinal: async (config, { configType }) => {
-  //   // Make whatever fine-grained changes you need that should apply to all storybook configs
-
-  //   // Return the altered config
-  //   return config;
-  // },
+  webpackFinal: async (config, { configType }) => {
+    // Make whatever fine-grained changes you need that should apply to all storybook configs
+    // console.log('current main.js config', config);
+    // Return the altered config
+    return config;
+  },
 };
+
+function findStories() {
+  // your custom logic returns a list of files
+  console.log('findStories========================');
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 0c27507c7c9016a219a2527fa8d4b61b0302bfad..5ff6bea5dbd51c1594e94eb604de8bb22dfeb099 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -14,15 +14,15 @@
     "editor.defaultFormatter": "esbenp.prettier-vscode"
   },
   "conventionalCommits.scopes": [
-    "vis-rawjsonview",
+    "json",
     "panels",
-    "vis-semsub",
-    "vis-nl",
-    "vis-paoh",
-    "vis-schema",
+    "semsub",
+    "nl",
+    "paoh",
+    "schema",
     "storybook",
     "store",
-    "libs"
+    "libs",
   ],
   "jest.jestCommandLine": "nx affected:test",
 
diff --git a/README.md b/README.md
index 236cf9add7ef289499bfc11b39d6e919a4745dda..ca4510171dcf47798262c1568dbd6ee2e993c729 100644
--- a/README.md
+++ b/README.md
@@ -7,20 +7,20 @@ Due to the way auth works (using a sameSite cookie), the procedure for running l
 ### MacOS / Linux
 
 1. `sudo vim /etc/hosts` open the hosts file with your prefered text editor as root
-2. Add a new row containing `127.0.0.1 local.datastrophe.science.uu.nl`, this will route traffic from `local.datastrophe.science.uu.nl` to `127.0.0.1`
+2. Add a new row containing `127.0.0.1 local.graphpolaris.com`, this will route traffic from `local.graphpolaris.com` to `127.0.0.1`
 3. `brew install mkcert` install mkcert utility
 4. `mkcert -install` generate local CA (certificate authority)
 5. Move into the /certs folder at the project root using `cd`
-6. `mkcert --key-file local-key.pem --cert-file local-cert.pem local.datastrophe.science.uu.nl` create certificates for local SSL
+6. `mkcert --key-file local-key.pem --cert-file local-cert.pem local.graphpolaris.com` create certificates for local SSL
 
 ### Windows
 
 1. Open the `hosts` file under `C:\Windows\System32\drivers\etc` using a text editor, as administrator
-2. Add a new row containing `127.0.0.1 local.datastrophe.science.uu.nl`, this will route traffic from `local.datastrophe.science.uu.nl` to `127.0.0.1`
+2. Add a new row containing `127.0.0.1 local.graphpolaris.com`, this will route traffic from `local.graphpolaris.com` to `127.0.0.1`
 3. Install mkcert using any of the ways described [here](https://github.com/FiloSottile/mkcert#windows)
 4. Open an elevated Powershell or CMD session
 5. Move into the /certs folder at the project root using `cd`
-6. `mkcert --key-file local-key.pem --cert-file local-cert.pem local.datastrophe.science.uu.nl` create certificates for local SSL
+6. `mkcert --key-file local-key.pem --cert-file local-cert.pem local.graphpolaris.com` create certificates for local SSL
 
 > No idea if the Windows steps work
 
diff --git a/apps/web-graphpolaris/.storybook/.babelrc b/apps/web-graphpolaris/.storybook/.babelrc
new file mode 100644
index 0000000000000000000000000000000000000000..0f97847325a64a312ae586f7c5f43dea3fb17956
--- /dev/null
+++ b/apps/web-graphpolaris/.storybook/.babelrc
@@ -0,0 +1 @@
+{ "extends": "../.babelrc" }
diff --git a/apps/web-graphpolaris/.storybook/main.js b/apps/web-graphpolaris/.storybook/main.js
index 530fe3a06e224f618059baa7b1a5192c812459be..02ada918184b76e65ab9ac4b225326342c6db56c 100644
--- a/apps/web-graphpolaris/.storybook/main.js
+++ b/apps/web-graphpolaris/.storybook/main.js
@@ -28,4 +28,8 @@ module.exports = {
 
     return config;
   },
+  babel: async (options) => ({
+    ...options,
+    // any extra options you want to set
+  }),
 };
diff --git a/apps/web-graphpolaris/.storybook/tsconfig.json b/apps/web-graphpolaris/.storybook/tsconfig.json
index 7a1170995999ff179ae31ea7b87133b1c2161bc2..5a96b7a33c312784ae2c9381a0cdbfc439e83250 100644
--- a/apps/web-graphpolaris/.storybook/tsconfig.json
+++ b/apps/web-graphpolaris/.storybook/tsconfig.json
@@ -1,7 +1,7 @@
 {
   "extends": "../tsconfig.json",
   "compilerOptions": {
-    "emitDecoratorMetadata": true,
+    "emitDecoratorMetadata": false,
     "outDir": ""
   },
   "files": [
@@ -14,6 +14,5 @@
     "../**/*.spec.js",
     "../**/*.spec.tsx",
     "../**/*.spec.jsx"
-  ],
-  "include": ["../src/**/*", "*.js"]
+  ]
 }
diff --git a/apps/web-graphpolaris/project.json b/apps/web-graphpolaris/project.json
index dc9c691acc30a77b498ee4ee9c7abf79928cff15..7cc05ad711a6509e0b2b6c346c1149ca1d7e3d2f 100644
--- a/apps/web-graphpolaris/project.json
+++ b/apps/web-graphpolaris/project.json
@@ -7,7 +7,7 @@
       "executor": "@nrwl/web:dev-server",
       "options": {
         "buildTarget": "web-graphpolaris:build",
-        "host": "local.datastrophe.science.uu.nl",
+        "host": "local.graphpolaris.com",
         "port": 4200,
         "watch": true,
         "hmr": true,
diff --git a/apps/web-graphpolaris/src/assets/icons/ExportIcon.png b/apps/web-graphpolaris/src/assets/icons/ExportIcon.png
new file mode 100644
index 0000000000000000000000000000000000000000..62976b8a7902bc100cad532959e3d9e0d5fea42c
Binary files /dev/null and b/apps/web-graphpolaris/src/assets/icons/ExportIcon.png differ
diff --git a/apps/web-graphpolaris/src/components/login/loginScreen.tsx b/apps/web-graphpolaris/src/components/login/loginScreen.tsx
index 93339edaadd13653822233ecf9aac3468f9f4435..fc1cf4a89f19194a218396348dbcc5cf4dc1e608 100644
--- a/apps/web-graphpolaris/src/components/login/loginScreen.tsx
+++ b/apps/web-graphpolaris/src/components/login/loginScreen.tsx
@@ -102,7 +102,7 @@ const LoginScreen = () => {
         <img
           onClick={() =>
             openSignInWindow(
-              'https://datastrophe.science.uu.nl/user/sign-in?provider=1'
+              'https://api.graphpolaris.com/user/sign-in?provider=1'
             )
           }
           src="assets/login-screen/google.png"
@@ -111,7 +111,7 @@ const LoginScreen = () => {
         <img
           onClick={() =>
             openSignInWindow(
-              'https://datastrophe.science.uu.nl/user/sign-in?provider=2'
+              'https://api.graphpolaris.com/user/sign-in?provider=2'
             )
           }
           src="assets/login-screen/github.png"
@@ -119,9 +119,7 @@ const LoginScreen = () => {
         />
         <p
           onClick={() =>
-            openSignInWindow(
-              'https://datastrophe.science.uu.nl/user/create_free/'
-            )
+            openSignInWindow('https://api.graphpolaris.com/user/create_free/')
           }
         >
           Developer
diff --git a/apps/web-graphpolaris/src/components/querybuilder/querybuilder.stories.tsx b/apps/web-graphpolaris/src/components/querybuilder/querybuilder.stories.tsx
index b406d60b5879ace0bfe90fa43b1a151fd83198f8..2de5deb8795590c12bdd020c65687f5d72e3e4b4 100644
--- a/apps/web-graphpolaris/src/components/querybuilder/querybuilder.stories.tsx
+++ b/apps/web-graphpolaris/src/components/querybuilder/querybuilder.stories.tsx
@@ -14,6 +14,7 @@ import { addPill, handles } from '@graphpolaris/querybuilder/usecases';
 
 export default {
   component: QueryBuilder,
+  title: 'Panels/QueryBuilder',
 } as ComponentMeta<typeof QueryBuilder>;
 
 const Template: ComponentStory<typeof QueryBuilder> = (args) => (
diff --git a/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx b/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx
index 3870f76de645a2d47757c5e0a40e2c8bea89503b..063330aa016123b37498bf5d1f85690ccb247391 100644
--- a/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx
+++ b/apps/web-graphpolaris/src/components/querybuilder/querybuilder.tsx
@@ -40,6 +40,7 @@ const QueryBuilder = (props: {}) => {
   const nodes = useQuerybuilderNodes();
   const dispatch = useAppDispatch();
   const isDraggingPill = useRef(false);
+  console.log('inputnodes', nodes);
 
   const elements = useMemo(() => createReactFlowElements(nodes), [nodes]);
 
diff --git a/apps/web-graphpolaris/src/components/schema/schema.module.scss b/apps/web-graphpolaris/src/components/schema/schema.module.scss
new file mode 100644
index 0000000000000000000000000000000000000000..192a2fd7ace9b001b68c1e12af2bb2c048418546
--- /dev/null
+++ b/apps/web-graphpolaris/src/components/schema/schema.module.scss
@@ -0,0 +1,227 @@
+/**
+ * This program has been developed by students from the bachelor Computer Science at
+ * Utrecht University within the Software Project course.
+ * © Copyright Utrecht University (Department of Information and Computing Sciences)
+ */
+
+/* istanbul ignore file */
+/* The comment above was added so the code coverage wouldn't count this file towards code coverage.
+ * We do not test components/renderfunctions/styling files.
+ * See testing plan for more details.*/
+//  import { makeStyles, useTheme, Theme, createStyles } from '@material-ui/core/styles';
+//  import { BorderTopOutlined, PowerInputSharp } from '@material-ui/icons';
+//  import { ClassNameMap } from '@material-ui/styles';
+//  import { SchemaThemeHolder } from '../../../domain/entity/css-themes/themeHolder';
+
+.schemaPanel {
+  display: 'flex';
+  flex-direction: 'column';
+  justify-content: 'center';
+  //   '& div': {
+  //     '& svg' {
+  //       zindex: 2;
+  //     }
+  //   }
+}
+
+.controls {
+  left: 'auto !important';
+  bottom: 'auto !important';
+  top: '10px';
+  right: '20px';
+  width: 'auto !important';
+}
+
+//  export const useStyles = (theme: Theme) =>
+//    createStyles({
+//      schemaPanel: {
+//        display: 'flex',
+//        flexDirection: 'column',
+//        justifyContent: 'center',
+//        '& div': {
+//          '& svg': {
+//            zIndex: 2,
+//          },
+//        },
+//      },
+
+//      /**
+//       * When changing the height of the nodes, or the height of the buttons on the nodes, note that there is a hardcoded value 'height'
+//       * in schemaViewModelImpl (in the method 'setRelationNodePosition') that is based on these heights.
+//       */
+//      entityNode: {
+//        background: SchemaThemeHolder.entity.baseColor,
+//        display: 'flex',
+//        borderRadius: '1px',
+//        fontFamily: SchemaThemeHolder.fontFamily,
+//        fontWeight: 'bold',
+//        fontSize: `${SchemaThemeHolder.fontSize}px`,
+//        width: `${SchemaThemeHolder.entity.width}px`,
+//        lineHeight: `${SchemaThemeHolder.entity.height}px`,
+//      },
+
+//      nodeWrapper: {
+//        display: 'inherit',
+//        color: SchemaThemeHolder.entity.textColor,
+//        textAlign: 'center',
+//        justifyContent: 'space-between',
+//        alignItems: 'center',
+//        width: 'inherit',
+//      },
+
+//      nodeSpan: {
+//        margin: '0 0.5em 0 1em',
+//        float: 'right',
+//      },
+
+//      nodeData: {
+//        flexGrow: 2,
+//      },
+
+//      entityNodeAttributesBox: {
+//        height: 20,
+//        position: 'absolute',
+//        left: '115px',
+//        top: '-33%',
+//        transform: 'translateX(50%)',
+//        borderRadius: '1px',
+//        boxShadow: '-1px 2px 5px #888888',
+//        textAlign: 'right',
+//      },
+
+//      entityNodeNodesBox: {
+//        height: 20,
+//        position: 'absolute',
+//        left: '115px',
+//        top: '54%',
+//        transform: 'translateX(50%)',
+//        borderRadius: '1px',
+//        boxShadow: '-1px 2px 5px #888888',
+//        textAlign: 'right',
+//      },
+
+//      /**
+//       * Code for the shape of the relationNodes
+//       */
+//      relationNode: {
+//        height: 36,
+//        display: 'flex',
+//        fontFamily: SchemaThemeHolder.fontFamily,
+//        fontWeight: 'bold',
+//        fontSize: `${SchemaThemeHolder.fontSize}px`,
+//        width: `${SchemaThemeHolder.relation.width}px`,
+//        lineHeight: `${SchemaThemeHolder.relation.height}px`,
+//      },
+
+//      relationNodeTriangleGeneral: {
+//        position: 'absolute',
+//        width: 0,
+//        height: 0,
+//        margin: 'auto 0',
+//        borderStyle: 'solid',
+//        borderColor: 'transparent',
+//      },
+
+//      relationNodeTriangleLeft: {
+//        transform: 'translateX(-100%)',
+//        top: '0px',
+//        borderWidth: '18px 24px 18px 0',
+//      },
+
+//      relationNodeSmallTriangleLeft: {
+//        transform: 'translateX(-100%)',
+//        top: '30px',
+//        borderWidth: '0 8px 6px 0',
+//      },
+
+//      relationNodeTriangleRight: {
+//        transform: `translateX(${SchemaThemeHolder.relation.width}px)`,
+//        top: '0px',
+//        borderWidth: '18px 0 18px 24px',
+//      },
+
+//      relationNodeSmallTriangleRight: {
+//        transform: `translateX(${SchemaThemeHolder.relation.width}px)`,
+//        top: '30px',
+//        borderWidth: '0 0 6px 8px',
+//      },
+
+//      relationNodeAttributesBox: {
+//        position: 'absolute',
+//        top: '-4px',
+//        transform: `translateX(${SchemaThemeHolder.relation.width + 4}px)`,
+//        clipPath: 'polygon(0 0, 100% 0, 100% 100%, 26.5px 100%)',
+//        height: 20,
+//        textAlign: 'right',
+//      },
+
+//      relationNodeNodesBox: {
+//        position: 'absolute',
+//        top: '20px',
+//        transform: `translateX(${SchemaThemeHolder.relation.width + 4}px)`,
+//        clipPath: 'polygon(26.5px 0, 100% 0, 100% 100%, 0 100%)',
+//        height: 20,
+//        textAlign: 'right',
+//      },
+
+//      arrowup: {
+//        width: 0,
+//        height: 0,
+//        position: 'absolute',
+//        left: '50%',
+//        top: '-20%',
+//        transform: 'translateX(-50%)',
+//        margin: '0 auto',
+//        backgroundColor: 'transparent',
+//        borderStyle: 'solid',
+//        borderTopWidth: 0,
+//        borderRightWidth: 8,
+//        borderBottomWidth: 5,
+//        borderLeftWidth: 8,
+//        borderTopColor: 'transparent',
+//        borderRightColor: 'transparent',
+//        borderLeftColor: 'transparent',
+//      },
+
+//      arrowdown: {
+//        width: 0,
+//        height: 0,
+//        position: 'absolute',
+//        left: '50%',
+//        bottom: '-20%',
+//        transform: 'translateX(-50%)',
+//        margin: '0 auto',
+//        backgroundColor: 'transparent',
+//        borderStyle: 'solid',
+//        borderTopWidth: 5,
+//        borderRightWidth: 8,
+//        borderBottomWidth: 0,
+//        borderLeftWidth: 8,
+//        borderRightColor: 'transparent',
+//        borderBottomColor: 'transparent',
+//        borderLeftColor: 'transparent',
+//      },
+
+//      controls: {
+//        left: 'auto !important',
+//        bottom: 'auto !important',
+//        top: '10px',
+//        right: '20px',
+//        width: 'auto !important',
+//      },
+
+//      exportButton: {
+//        left: 'auto !important',
+//        bottom: 'auto !important',
+//        top: '10px',
+//        right: '20px',
+//        '& svg': {
+//          transform: 'scale(1.4)',
+//        },
+//      },
+
+//      menuText: {
+//        fontSize: 'small',
+//        fontFamily: 'Poppins, sans-serif',
+//      },
+//    });
diff --git a/apps/web-graphpolaris/src/components/schema/schema.spec.ts b/apps/web-graphpolaris/src/components/schema/schema.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d975c32fbdce50259e67bdb1161cfdc6992f8390
--- /dev/null
+++ b/apps/web-graphpolaris/src/components/schema/schema.spec.ts
@@ -0,0 +1,330 @@
+import { SchemaUtils } from '@graphpolaris/schema-utils';
+import { SchemaFromBackend } from '@graphpolaris/models';
+
+
+const twitterSchemaRaw: SchemaFromBackend = {
+  nodes: [
+    {
+      name: 'Me',
+      attributes: [
+        {
+          name: 'screen_name',
+          type: 'string',
+        },
+        {
+          name: 'name',
+          type: 'string',
+        },
+        {
+          name: 'location',
+          type: 'string',
+        },
+        {
+          name: 'followers',
+          type: 'int',
+        },
+        {
+          name: 'following',
+          type: 'int',
+        },
+        {
+          name: 'url',
+          type: 'string',
+        },
+        {
+          name: 'profile_image_url',
+          type: 'string',
+        },
+      ],
+    },
+    {
+      name: 'Link',
+      attributes: [
+        {
+          name: 'url',
+          type: 'string',
+        },
+      ],
+    },
+    {
+      name: 'Source',
+      attributes: [
+        {
+          name: 'name',
+          type: 'string',
+        },
+      ],
+    },
+    {
+      name: 'Hashtag',
+      attributes: [
+        {
+          name: 'name',
+          type: 'string',
+        },
+      ],
+    },
+    {
+      name: 'User',
+      attributes: [
+        {
+          name: 'screen_name',
+          type: 'string',
+        },
+        {
+          name: 'name',
+          type: 'string',
+        },
+        {
+          name: 'location',
+          type: 'string',
+        },
+        {
+          name: 'followers',
+          type: 'int',
+        },
+        {
+          name: 'following',
+          type: 'int',
+        },
+        {
+          name: 'url',
+          type: 'string',
+        },
+        {
+          name: 'profile_image_url',
+          type: 'string',
+        },
+        {
+          name: 'screen_name',
+          type: 'string',
+        },
+        {
+          name: 'name',
+          type: 'string',
+        },
+        {
+          name: 'location',
+          type: 'string',
+        },
+        {
+          name: 'followers',
+          type: 'int',
+        },
+        {
+          name: 'following',
+          type: 'int',
+        },
+        {
+          name: 'statuses',
+          type: 'int',
+        },
+        {
+          name: 'url',
+          type: 'string',
+        },
+        {
+          name: 'profile_image_url',
+          type: 'string',
+        },
+      ],
+    },
+    {
+      name: 'Tweet',
+      attributes: [
+        {
+          name: 'id',
+          type: 'int',
+        },
+        {
+          name: 'id_str',
+          type: 'string',
+        },
+        {
+          name: 'text',
+          type: 'string',
+        },
+        {
+          name: 'favorites',
+          type: 'int',
+        },
+        {
+          name: 'import_method',
+          type: 'string',
+        },
+      ],
+    },
+  ],
+  edges: [
+    {
+      name: 'USING',
+      collection: 'USING',
+      from: 'Tweet',
+      to: 'Source',
+      attributes: [],
+    },
+    {
+      name: 'SIMILAR_TO',
+      collection: 'SIMILAR_TO',
+      from: 'User',
+      to: 'User',
+      attributes: [
+        {
+          name: 'score',
+          type: 'float',
+        },
+      ],
+    },
+    {
+      name: 'SIMILAR_TO',
+      collection: 'SIMILAR_TO',
+      from: 'User',
+      to: 'Me',
+      attributes: [
+        {
+          name: 'score',
+          type: 'float',
+        },
+      ],
+    },
+    {
+      name: 'AMPLIFIES',
+      collection: 'AMPLIFIES',
+      from: 'Me',
+      to: 'User',
+      attributes: [],
+    },
+    {
+      name: 'AMPLIFIES',
+      collection: 'AMPLIFIES',
+      from: 'User',
+      to: 'User',
+      attributes: [],
+    },
+    {
+      name: 'RT_MENTIONS',
+      collection: 'RT_MENTIONS',
+      from: 'Me',
+      to: 'User',
+      attributes: [],
+    },
+    {
+      name: 'RT_MENTIONS',
+      collection: 'RT_MENTIONS',
+      from: 'User',
+      to: 'User',
+      attributes: [],
+    },
+    {
+      name: 'FOLLOWS',
+      collection: 'FOLLOWS',
+      from: 'User',
+      to: 'Me',
+      attributes: [],
+    },
+    {
+      name: 'FOLLOWS',
+      collection: 'FOLLOWS',
+      from: 'Me',
+      to: 'User',
+      attributes: [],
+    },
+    {
+      name: 'FOLLOWS',
+      collection: 'FOLLOWS',
+      from: 'User',
+      to: 'User',
+      attributes: [],
+    },
+    {
+      name: 'FOLLOWS',
+      collection: 'FOLLOWS',
+      from: 'Me',
+      to: 'Me',
+      attributes: [],
+    },
+    {
+      name: 'INTERACTS_WITH',
+      collection: 'INTERACTS_WITH',
+      from: 'User',
+      to: 'User',
+      attributes: [],
+    },
+    {
+      name: 'INTERACTS_WITH',
+      collection: 'INTERACTS_WITH',
+      from: 'Me',
+      to: 'User',
+      attributes: [],
+    },
+    {
+      name: 'RETWEETS',
+      collection: 'RETWEETS',
+      from: 'Tweet',
+      to: 'Tweet',
+      attributes: [],
+    },
+    {
+      name: 'REPLY_TO',
+      collection: 'REPLY_TO',
+      from: 'Tweet',
+      to: 'Tweet',
+      attributes: [],
+    },
+    {
+      name: 'CONTAINS',
+      collection: 'CONTAINS',
+      from: 'Tweet',
+      to: 'Link',
+      attributes: [],
+    },
+    {
+      name: 'MENTIONS',
+      collection: 'MENTIONS',
+      from: 'Tweet',
+      to: 'User',
+      attributes: [],
+    },
+    {
+      name: 'MENTIONS',
+      collection: 'MENTIONS',
+      from: 'Tweet',
+      to: 'Me',
+      attributes: [],
+    },
+    {
+      name: 'TAGS',
+      collection: 'TAGS',
+      from: 'Tweet',
+      to: 'Hashtag',
+      attributes: [],
+    },
+    {
+      name: 'POSTS',
+      collection: 'POSTS',
+      from: 'User',
+      to: 'Tweet',
+      attributes: [],
+    },
+    {
+      name: 'POSTS',
+      collection: 'POSTS',
+      from: 'Me',
+      to: 'Tweet',
+      attributes: [],
+    },
+  ],
+};
+
+const twitterSchema =
+  SchemaUtils.ParseSchemaFromBackend(twitterSchemaRaw);
+
+
+
+  describe('import tests', () => {
+    it('should resolve all imports appropriately for _external_ libs @graphpolaris/schema-utils and @graphpolaris/model ', () => {
+      const graph = twitterSchema;
+      expect(graph);
+    });
+  
+  });
\ No newline at end of file
diff --git a/apps/web-graphpolaris/src/components/schema/schema.stories.tsx b/apps/web-graphpolaris/src/components/schema/schema.stories.tsx
index 873afdf6b36983cd9d6a493235df20b3ed654452..01e070c34b6fc7f427ef2c558e738bab10d9d1b5 100644
--- a/apps/web-graphpolaris/src/components/schema/schema.stories.tsx
+++ b/apps/web-graphpolaris/src/components/schema/schema.stories.tsx
@@ -11,12 +11,24 @@ import React from 'react';
 import { Provider } from 'react-redux';
 import Schema from './schema';
 
+import {
+  movieSchemaRaw,
+  northwindSchemaRaw,
+  simpleSchemaRaw,
+  twitterSchemaRaw,
+  movieSchema,
+  northWindSchema,
+  simpleSchema,
+  twitterSchema,
+} from '@graphpolaris/shared/mock-data';
+
 export default {
   /* 👇 The title prop is optional.
    * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
    * to learn how to generate automatic titles
    */
-  title: 'Schema',
+  title: 'Panels/Schema',
+
   component: Schema,
   decorators: [
     (story) => (
@@ -146,5 +158,44 @@ TestWithSchema.play = async () => {
 
   console.info('dispatch dummy schema', schema.order);
   dispatch(setSchema(schema.export()));
-  // handleSchemaLayout(schema);
+};
+
+export const TestWithMockNorthWindSchema = Template.bind({});
+
+TestWithMockNorthWindSchema.play = async () => {
+  const dispatch = Mockstore.dispatch;
+
+  const schema = northWindSchema;
+  console.info('dispatch northWindSchema schema', schema.order);
+  dispatch(setSchema(schema.export()));
+};
+
+export const TestWithMockTwitterSchema = Template.bind({});
+
+TestWithMockTwitterSchema.play = async () => {
+  const dispatch = Mockstore.dispatch;
+
+  const schema = twitterSchema;
+  console.info('dispatch TestWithMockTwitterSchema schema', schema.order);
+  dispatch(setSchema(schema.export()));
+};
+
+export const TestWithMockSimpleSchema = Template.bind({});
+
+TestWithMockSimpleSchema.play = async () => {
+  const dispatch = Mockstore.dispatch;
+
+  const schema = simpleSchema;
+  console.info('dispatch simpleSchema schema', schema.order);
+  dispatch(setSchema(schema.export()));
+};
+
+export const TestWithMockMovieSchema = Template.bind({});
+
+TestWithMockMovieSchema.play = async () => {
+  const dispatch = Mockstore.dispatch;
+
+  const schema = movieSchema;
+  console.info('dispatch movieSchema schema', schema.order);
+  dispatch(setSchema(schema.export()));
 };
diff --git a/apps/web-graphpolaris/src/components/schema/schema.tsx b/apps/web-graphpolaris/src/components/schema/schema.tsx
index 4167487adf58fa5068e4a8698a1490bf26ad8421..dfe7a159b4b15dbe838d81aadb3e55992ea585f7 100644
--- a/apps/web-graphpolaris/src/components/schema/schema.tsx
+++ b/apps/web-graphpolaris/src/components/schema/schema.tsx
@@ -1,20 +1,50 @@
 import { AllLayoutAlgorithms, LayoutFactory } from '@graphpolaris/graph-layout';
-import { createReactFlowElements } from '@graphpolaris/schema/usecases';
+import {
+  createReactFlowElements,
+  expandSchema,
+} from '@graphpolaris/schema/usecases';
 import {
   useSchema,
   useSchemaLayout,
 } from '@graphpolaris/shared/data-access/store';
 import { MultiGraph } from 'graphology';
 // import { AllLayoutAlgorithms, LayoutFactory } from '@graphpolaris/graph-layout';
-import { useEffect, useState } from 'react';
-import ReactFlow, { FlowElement, ReactFlowProvider } from 'react-flow-renderer';
+import { useEffect, useMemo, useState } from 'react';
+import ReactFlow, {
+  ControlButton,
+  Controls,
+  FlowElement,
+  ReactFlowProvider,
+} from 'react-flow-renderer';
+import styles from './schema.module.scss';
+
+import {
+  EntityRFPill,
+  RelationRFPill,
+  AttributeRFPill,
+  ConnectionDragLine,
+  ConnectionLine
+} from '@graphpolaris/shared/ui/pills';
+// import ConnectionDragLine from '@graphpolaris/shared/ui/pills';
+// import AttributeRFPill from '@graphpolaris/shared/ui/pills';
+// import EntityRFPill from '@graphpolaris/shared/ui/pills';
+// import RelationRFPill from '@graphpolaris/shared/ui/pills';
 
 interface Props {
   // content: string;
 }
 
 const onLoad = (reactFlowInstance: any) => {
-  setTimeout(() => reactFlowInstance.fitView(), 0);
+  setTimeout(() => reactFlowInstance.fitView(), 100);
+};
+
+const nodeTypes = {
+  entity: EntityRFPill,
+  relation: RelationRFPill,
+  attribute: AttributeRFPill,
+};
+const edgeTypes = {
+  connection: ConnectionLine,
 };
 
 const Schema = (props: Props) => {
@@ -24,27 +54,25 @@ const Schema = (props: Props) => {
   // const [dbschema, setSchema] = useState(useSchema());
   const [schemaLayout, setSchemaLayout] = useState(useSchemaLayout());
 
-  useEffect(() => {
-    console.log('dbSchema', dbschema, dbschema.order);
-  }, [dbschema]);
+  // useEffect(() => {
+  //   console.log('dbSchema', dbschema, dbschema.order);
+  // }, [dbschema]);
+
+  const expandedSchema = useMemo(() => expandSchema(dbschema), [dbschema]);
 
   useEffect(() => {
     if (dbschema == undefined || dbschema.order == 0) {
       return;
     }
+
     const layoutFactory = new LayoutFactory();
-    console.log('schema Layout', schemaLayout, 'order', dbschema.order);
+    console.log('schema Layout', schemaLayout, 'order', expandedSchema);
     const layout = layoutFactory.createLayout(
       schemaLayout as AllLayoutAlgorithms
     );
-    layout?.layout(dbschema);
-
-    // dbschema.forEachNode((node, attr) => {
-    //   console.log('x', dbschema.getNodeAttribute(node, 'x'));
-    //   console.log('y', dbschema.getNodeAttribute(node, 'y'));
-    // });
+    layout?.layout(expandedSchema);
 
-    const flowElements = createReactFlowElements(dbschema);
+    const flowElements = createReactFlowElements(expandedSchema);
     setElements(flowElements);
     console.log(
       'update schema useEffect',
@@ -65,13 +93,38 @@ const Schema = (props: Props) => {
     >
       {elements.length == 0 && <p>DEBUG: No Elements</p>}
       <ReactFlowProvider>
-        <ReactFlow elements={elements} style={graphStyles} onLoad={onLoad} />
-        {/* // onElementsRemove={onElementsRemove}
-        // onConnect={onConnect}
-        // 
-        snapToGrid={true}
-        snapGrid={[15, 15]}
-      ></ReactFlow> */}
+        <ReactFlow
+          className={styles.schemaPanel}
+          onlyRenderVisibleElements={false}
+          nodesDraggable={false}
+          nodeTypes={nodeTypes}
+          edgeTypes={edgeTypes}
+          connectionLineComponent={ConnectionDragLine}
+          elements={elements}
+          style={graphStyles}
+          onLoad={onLoad}
+        >
+          <Controls
+            showInteractive={false}
+            showZoom={false}
+            showFitView={true}
+            className={styles.controls}
+          >
+            <ControlButton
+              className={styles.exportButton}
+              title={'Export graph schema'}
+              onClick={(event) => {
+                event.stopPropagation();
+                // this.setState({
+                //   ...this.state,
+                //   exportMenuAnchor: event.currentTarget,
+                // });
+              }}
+            >
+              {/* <img src={exportIcon} width={21}></img> */}
+            </ControlButton>
+          </Controls>
+        </ReactFlow>
       </ReactFlowProvider>
     </div>
   );
diff --git a/apps/web-graphpolaris/src/components/visualisations/nodelink/nodelinkvis.stories.tsx b/apps/web-graphpolaris/src/components/visualisations/nodelink/nodelinkvis.stories.tsx
index fb22505b674ca26e9acb79192fb100fa11c3c159..1d688b93798fbb0aeeb50d159547128c96204da1 100644
--- a/apps/web-graphpolaris/src/components/visualisations/nodelink/nodelinkvis.stories.tsx
+++ b/apps/web-graphpolaris/src/components/visualisations/nodelink/nodelinkvis.stories.tsx
@@ -7,7 +7,7 @@ export default {
    * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
    * to learn how to generate automatic titles
    */
-  title: 'NodeLink',
+  title: 'Components/Visualizations/NodeLink',
   component: NodeLink,
   decorators: [(story) => <div style={{ padding: '3rem' }}>{story()}</div>],
 } as ComponentMeta<typeof NodeLink>;
diff --git a/apps/web-graphpolaris/src/components/visualisations/paohvis/paohvis.stories.tsx b/apps/web-graphpolaris/src/components/visualisations/paohvis/paohvis.stories.tsx
index 795a79aba58096b8ee5beb94af287ec7fadaab51..866d8b316ab2fd9711e5c95db2ea64e6466baab5 100644
--- a/apps/web-graphpolaris/src/components/visualisations/paohvis/paohvis.stories.tsx
+++ b/apps/web-graphpolaris/src/components/visualisations/paohvis/paohvis.stories.tsx
@@ -7,7 +7,7 @@ export default {
    * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
    * to learn how to generate automatic titles
    */
-  title: 'PaohVis',
+  title: 'Components/Visualizations/PaohVis',
   component: PaohVis,
   decorators: [(story) => <div style={{ padding: '3rem' }}>{story()}</div>],
 } as ComponentMeta<typeof PaohVis>;
diff --git a/apps/web-graphpolaris/src/components/visualisations/rawjsonvis/rawjsonvis.stories.tsx b/apps/web-graphpolaris/src/components/visualisations/rawjsonvis/rawjsonvis.stories.tsx
index 3b10ca2d12d79022af4ed24afd69c60c3235f4e0..a5c7561eb920505ebf17db4217495016a707cf83 100644
--- a/apps/web-graphpolaris/src/components/visualisations/rawjsonvis/rawjsonvis.stories.tsx
+++ b/apps/web-graphpolaris/src/components/visualisations/rawjsonvis/rawjsonvis.stories.tsx
@@ -18,7 +18,7 @@ export default {
    * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
    * to learn how to generate automatic titles
    */
-  title: 'RawJSONVIS',
+  title: 'Components/Visualizations/RawJSONVIS',
   component: RawJSONVis,
   decorators: [
     (story) => (
diff --git a/apps/web-graphpolaris/src/components/visualisations/semanticsubstrates/semanticsubstrates.stories.tsx b/apps/web-graphpolaris/src/components/visualisations/semanticsubstrates/semanticsubstrates.stories.tsx
index 944011b532a61cef8159459e8922b79f496dbb5c..8ffade3001311a731d9041c5a154d5bd78d1232c 100644
--- a/apps/web-graphpolaris/src/components/visualisations/semanticsubstrates/semanticsubstrates.stories.tsx
+++ b/apps/web-graphpolaris/src/components/visualisations/semanticsubstrates/semanticsubstrates.stories.tsx
@@ -14,7 +14,7 @@ export default {
    * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
    * to learn how to generate automatic titles
    */
-  title: 'SemanticSubstrates',
+  title: 'Components/Visualizations/SemanticSubstrates',
   component: SemanticSubstrates,
   decorators: [
     (story) => (
diff --git a/deployment.yml b/deployment.yml
new file mode 100644
index 0000000000000000000000000000000000000000..fc945c18719db08baaf0e539fd9a16bcecb04609
--- /dev/null
+++ b/deployment.yml
@@ -0,0 +1,35 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: frontend-webserver-deployment
+  labels:
+    app: frontend-webserver
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: frontend-webserver
+  template:
+    metadata:
+      labels:
+        app: frontend-webserver
+    spec:
+      containers:
+        - name: container
+          image: datastropheregistry.azurecr.io/frontend:latest
+          ports:
+            - containerPort: 3000
+      imagePullSecrets:
+        - name: docker-regcred
+
+---
+kind: Service
+apiVersion: v1
+metadata:
+  name: frontend-webserver
+spec:
+  selector:
+    app: frontend-webserver
+  ports:
+    - port: 3000
+      targetPort: 80
diff --git a/libs/schema/usecases/src/lib/schema-usecases.ts b/libs/schema/usecases/src/lib/schema-usecases.ts
index 5efe7de805e90dcc79a9d4e468c75026b5d18899..d3151bfe9fbfc42e4e2cdc54e64575c5382b7ef9 100644
--- a/libs/schema/usecases/src/lib/schema-usecases.ts
+++ b/libs/schema/usecases/src/lib/schema-usecases.ts
@@ -1,164 +1,149 @@
 import Graph from 'graphology';
 import { Attributes } from 'graphology-types';
-import { Edge, Elements, Node } from 'react-flow-renderer';
-
-// type CytoNode = {
-//   data: {
-//     id: string;
-//     type: string;
-//     source?: string;
-//     target?: string;
-//     position?: {
-//       x: number;
-//       y: number;
-//     };
-//   };
-// };
-
-// // Layouts a given schema
-// export function handleSchemaLayout(graph: Graph): void {
-//   const layout = createSchemaLayout(graph);
-
-//   layout.then((cy) => {
-//     //cy.cy.elements().forEach((elem) => {
-//     cy.cy.nodes().forEach((elem: any) => {
-//       const position = elem.position();
-//       console.log(elem.id());
-
-//       graph.setNodeAttribute(elem.id(), 'x', position.x);
-//       graph.setNodeAttribute(elem.id(), 'y', position.y);
-//     });
-
-//     store.dispatch(setSchema(graph));
-//   });
-// }
-
-// // Creates a schema layout (async)
-// function createSchemaLayout(graph: Graph): Promise<cytoscape.EventObject> {
-//   const cytonodes: CytoNode[] = trimSchema(graph);
-
-//   const cy = cytoscape({
-//     elements: cytonodes,
-//   });
-
-//   const options = {
-//     name: 'cose',
-
-//     // Whether to animate while running the layout
-//     // true : Animate continuously as the layout is running
-//     // false : Just show the end result
-//     // 'end' : Animate with the end result, from the initial positions to the end positions
-//     animate: true,
-
-//     // Easing of the animation for animate:'end'
-//     animationEasing: undefined,
-
-//     // The duration of the animation for animate:'end'
-//     animationDuration: undefined,
-
-//     // A function that determines whether the node should be animated
-//     // All nodes animated by default on animate enabled
-//     // Non-animated nodes are positioned immediately when the layout starts
-//     // animateFilter: function (node: any, i: any) {
-//     //   return true;
-//     // },
-
-//     // The layout animates only after this many milliseconds for animate:true
-//     // (prevents flashing on fast runs)
-//     animationThreshold: 250,
-
-//     // Number of iterations between consecutive screen positions update
-//     refresh: 20,
-
-//     // Whether to fit the network view after when done
-//     fit: true,
-
-//     // Padding on fit
-//     padding: 30,
-
-//     // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
-//     boundingBox: undefined,
-
-//     // Excludes the label when calculating node bounding boxes for the layout algorithm
-//     nodeDimensionsIncludeLabels: false,
-
-//     // Randomize the initial positions of the nodes (true) or use existing positions (false)
-//     randomize: false,
-
-//     // Extra spacing between components in non-compound graphs
-//     componentSpacing: 200, // 40
-
-//     // Node repulsion (non overlapping) multiplier
-//     nodeRepulsion: function (node: any) {
-//       return 2048;
-//     },
-
-//     // Node repulsion (overlapping) multiplier
-//     nodeOverlap: 4,
-
-//     // Ideal edge (non nested) length
-//     idealEdgeLength: function (edge: any) {
-//       return 32;
-//     },
-
-//     // Divisor to compute edge forces
-//     edgeElasticity: function (edge: any) {
-//       return 32;
-//     },
-
-//     // Nesting factor (multiplier) to compute ideal edge length for nested edges
-//     nestingFactor: 1.2,
+import { ArrowHeadType, Edge, Elements, Node } from 'react-flow-renderer';
 
-//     // Gravity force (constant)
-//     gravity: 1,
+//TODO does not belong here; maybe should go into the GraphPolarisThemeProvider
+const ANIMATEDEDGES = false;
 
-//     // Maximum number of iterations to perform
-//     numIter: 1000,
+export function expandSchema(graph: Graph): Graph {
+  const newGraph = graph.copy();
 
-//     // Initial temperature (maximum node displacement)
-//     initialTemp: 1000,
-
-//     // Cooling factor (how the temperature is reduced between consecutive iterations
-//     coolingFactor: 0.99,
-
-//     // Lower temperature threshold (below this point the layout will end)
-//     minTemp: 1.0,
-//   };
-
-//   const layout = cy.layout(options);
+  newGraph.forEachNode((node, attributes) => {
+    // console.log(node, attributes);
+    newGraph.mergeNodeAttributes(node, { type: 'entity' });
+  });
 
-//   layout.run();
+  //makeNewRelationNodes
+  graph.forEachEdge((edge, attributes, source, target): void => {
+    const newID = 'RelationNode:' + edge;
+    // console.log('making relationnode', edge, attributes, source, target, newID);
+    newGraph.addNode(newID, {
+      name: edge,
+      data: {
+        label: edge,
+        name: edge,
+      },
+      attributes,
+      x: 0,
+      y: 0,
+      type: 'relation',
+    });
+
+    const id = 'RelationEdge' + source + '->' + newID;
+    newGraph.addDirectedEdgeWithKey(id, source, newID, {
+      name: edge,
+    });
+
+    const id2 = 'RelationEdge' + newID + '->' + target;
+    // console.log('making newEdgeOutgoing', edge, attributes, source, target, id);
+    newGraph.addDirectedEdgeWithKey(id2, newID, target, {});
+
+    newGraph.dropEdge(edge);
+  });
 
-//   return layout.pon('layoutstop');
-// }
+  return newGraph;
+}
 
 // Takes the schema as an imput and creates basic react flow elements for them.
 export function createReactFlowElements(graph: Graph): Elements<Node | Edge> {
   const initialElements: Elements<Node | Edge> = [];
 
+  initialElements.push(...createReactFlowNodes(graph));
+  initialElements.push(...createReactFlowEdges(graph));
+  // initialElements.push(...createReactFlowRelationNodes(graph));
+  // initialElements.push(...createReactFlowRelationEdges(graph));
+  // console.log(initialElements);
+
+  return initialElements;
+}
+
+export function createReactFlowNodes(graph: Graph): Elements<Node> {
+  const nodeElements: Elements<Node> = [];
   graph.forEachNode((node: string, attributes: Attributes): void => {
     const newNode: Node = {
       id: node,
       data: {
         label: attributes.name,
+        name: attributes.name,
       },
       position: { x: attributes.x, y: attributes.y },
+      type: attributes.type,
     };
-    initialElements.push(newNode);
+    nodeElements.push(newNode);
   });
 
-  graph.forEachEdge((edge, _attributes, source, target): void => {
+  return nodeElements;
+}
+
+export function createReactFlowEdges(graph: Graph): Elements<Edge> {
+  const edgeElements: Elements<Edge> = [];
+
+  graph.forEachEdge((edge, attributes, source, target): void => {
+    // console.log('edges', attributes, edge);
     const newEdge: Edge = {
       id: edge,
       source: source,
       target: target,
+      // label: edge,
+      type: 'smoothstep',
+      animated: ANIMATEDEDGES,
+      arrowHeadType: ArrowHeadType.ArrowClosed,
     };
-    initialElements.push(newEdge);
+    edgeElements.push(newEdge);
   });
 
-  return initialElements;
+  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: '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 {
diff --git a/libs/shared/data-access/api/src/lib/database.ts b/libs/shared/data-access/api/src/lib/database.ts
index 8d6700de6f55dde670227d1cedf1080c44637e28..da706c42a71c1cedc8a777afd5870480b3ec17eb 100644
--- a/libs/shared/data-access/api/src/lib/database.ts
+++ b/libs/shared/data-access/api/src/lib/database.ts
@@ -14,7 +14,7 @@ export type AddDatabaseRequest = {
 
 export function AddDatabase(request: AddDatabaseRequest): Promise<void> {
   return new Promise((resolve, reject) => {
-    fetch('https://datastrophe.science.uu.nl/user/database', {
+    fetch('https://api.graphpolaris.com/user/database', {
       method: 'POST',
       credentials: 'same-origin',
       headers: new Headers({
@@ -34,7 +34,7 @@ export function AddDatabase(request: AddDatabaseRequest): Promise<void> {
 
 export function GetAllDatabases(): Promise<Array<string>> {
   return new Promise<Array<string>>((resolve, reject) => {
-    fetch('https://datastrophe.science.uu.nl/user/database', {
+    fetch('https://api.graphpolaris.com/user/database', {
       method: 'GET',
       credentials: 'same-origin',
       headers: new Headers({
@@ -57,7 +57,7 @@ export function GetAllDatabases(): Promise<Array<string>> {
 
 export function DeleteDatabase(name: string): Promise<void> {
   return new Promise((resolve, reject) => {
-    fetch('https://datastrophe.science.uu.nl/user/database/' + name, {
+    fetch('https://api.graphpolaris.com/user/database/' + name, {
       method: 'DELETE',
       credentials: 'same-origin',
       headers: new Headers({
diff --git a/libs/shared/data-access/api/src/lib/user.ts b/libs/shared/data-access/api/src/lib/user.ts
index 457ce20f6af6c8be31727f2b9757134827b4fc70..b3d7b3fc9e6b5411369a4a5b11e3ed8e7e009065 100644
--- a/libs/shared/data-access/api/src/lib/user.ts
+++ b/libs/shared/data-access/api/src/lib/user.ts
@@ -10,7 +10,7 @@ export type User = {
 
 export function GetUserInfo(): Promise<User> {
   return new Promise<User>((resolve, reject) => {
-    fetch('https://datastrophe.science.uu.nl/user/', {
+    fetch('https://api.graphpolaris.com/user/', {
       method: 'GET',
       credentials: 'same-origin',
       headers: new Headers({
diff --git a/libs/shared/data-access/authorization/src/lib/authorizationHandler.ts b/libs/shared/data-access/authorization/src/lib/authorizationHandler.ts
index fc92922419ad609e85767e55dd35b06b2a581012..bc6001fd2aed8d24d60abf15411167af64629e81 100644
--- a/libs/shared/data-access/authorization/src/lib/authorizationHandler.ts
+++ b/libs/shared/data-access/authorization/src/lib/authorizationHandler.ts
@@ -1,5 +1,3 @@
-import { Cookies } from 'react-cookie';
-
 export class AuthorizationHandler {
   private static _instance: AuthorizationHandler;
   private accessToken = '';
@@ -52,7 +50,7 @@ export class AuthorizationHandler {
    */
   private async getNewAccessToken(): Promise<authResponse> {
     // If we have an access token already, append it to the url as a query param to keep sessionID the same
-    let url = 'https://datastrophe.science.uu.nl/auth/refresh';
+    let url = 'https://api.graphpolaris.com/auth/refresh';
     if (this.accessToken != '') {
       url += '?access_token=' + this.accessToken;
     }
@@ -104,7 +102,7 @@ export class AuthorizationHandler {
    * initialiseRefreshToken attempts to initialise a refresh token
    */
   private async initialiseRefreshToken() {
-    fetch('https://datastrophe.science.uu.nl/auth/refresh', {
+    fetch('https://api.graphpolaris.com/auth/refresh', {
       method: 'POST',
       credentials: 'include',
     })
diff --git a/libs/shared/data-access/store/src/lib/schemaSlice.ts b/libs/shared/data-access/store/src/lib/schemaSlice.ts
index 9ff253e0a0611c3f3ff91edef3f19a6d015aaf73..c7d1febd677be2187fb20b67074f1839e87c9770 100644
--- a/libs/shared/data-access/store/src/lib/schemaSlice.ts
+++ b/libs/shared/data-access/store/src/lib/schemaSlice.ts
@@ -1,15 +1,17 @@
 import { createSlice, PayloadAction } from '@reduxjs/toolkit';
 import Graph, { MultiGraph } from 'graphology';
 import { SerializedGraph } from 'graphology-types';
-import { SchemaFromBackend } from 'libs/shared/models/src';
+import { SchemaFromBackend } from '@graphpolaris/models';
 import type { RootState } from './store';
+import { AllLayoutAlgorithms } from '@graphpolaris/graph-layout';
 
 /**************************************************************** */
 
 // Define the initial state using that type
 export const initialState = {
   graphologySerialized: new MultiGraph().export(),
-  layoutName: 'Cytoscape_fcose',
+  // layoutName: 'Cytoscape_fcose',
+  layoutName: 'Cytoscape_cise' as AllLayoutAlgorithms,
 };
 
 export const schemaSlice = createSlice({
@@ -24,8 +26,7 @@ export const schemaSlice = createSlice({
 
     setSchemaLayout: (state, action: PayloadAction<string>) => {
       console.log('setSchemaLayout', action);
-
-      state.layoutName = action.payload;
+      state.layoutName = action.payload as AllLayoutAlgorithms;
     },
 
     readInSchemaFromBackend: (
diff --git a/libs/shared/data-access/theme/src/lib/graphPolarisThemeProvider.spec.tsx b/libs/shared/data-access/theme/src/lib/graphPolarisThemeProvider.spec.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..66c2e4c384d4a5079ab54cda0772757e078a73a4
--- /dev/null
+++ b/libs/shared/data-access/theme/src/lib/graphPolarisThemeProvider.spec.tsx
@@ -0,0 +1,36 @@
+// import Button from '@mui/material/Button';
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { MockTheme, MockThemeGraphPolaris } from './mockTheme';
+
+describe('<MockTheme GraphPolarisThemeProvider>', () => {
+  // TODO: This test should be implemented and running, but I get import issues in the mono-repo with jest (MIB)
+  // https://emotion.sh/docs/@emotion/jest
+
+
+  it('renders without crashing', () => {
+  });
+  // it('passes smoke test no config of mocktheme', () => {
+  //   const div = document.createElement('div');
+
+  //   ReactDOM.render(
+  //     <MockTheme>
+  //       <Button variant="text">Text</Button>
+  //     </MockTheme>,
+  //     div
+  //   );
+  // });
+
+
+
+  // it('passes smoke test config GraphPolaris', () => {
+  //   const div = document.createElement('div');
+
+  //   ReactDOM.render(
+  //     <MockThemeGraphPolaris>
+  //       <Button variant="text">Text</Button>
+  //     </MockThemeGraphPolaris>,
+  //     div
+  //   );
+  // });
+});
diff --git a/libs/shared/data-access/theme/src/lib/mockTheme.tsx b/libs/shared/data-access/theme/src/lib/mockTheme.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..fccc611d7f80317eab5d898853171fb4e2404459
--- /dev/null
+++ b/libs/shared/data-access/theme/src/lib/mockTheme.tsx
@@ -0,0 +1,25 @@
+import React from 'react';
+
+import { createTheme, ThemeProvider } from '@mui/material/styles';
+
+export function MockTheme({ children }: any) {
+  const theme = React.useMemo(
+    () =>
+      // Map our color palette config (stored in redux) to the mui 5 format
+      createTheme({}),
+    []
+  );
+
+  return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
+}
+
+export function MockThemeGraphPolaris({ children }: any) {
+  const theme = React.useMemo(
+    () =>
+      // Map our color palette config (stored in redux) to the mui 5 format
+      createTheme({}),
+    []
+  );
+
+  return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
+}
diff --git a/libs/shared/graph-layout/jest.config.js b/libs/shared/graph-layout/jest.config.js
index ae3d05af50147b0e99ce13f1af409e0ca0752c64..4b12ff63c43b71caa0404e62ff739ba710d00f6e 100644
--- a/libs/shared/graph-layout/jest.config.js
+++ b/libs/shared/graph-layout/jest.config.js
@@ -6,4 +6,5 @@ module.exports = {
   },
   moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
   coverageDirectory: '../../../coverage/libs/shared/graph-layout',
+  setupFiles: ['jest-canvas-mock'],
 };
diff --git a/libs/shared/graph-layout/package.json b/libs/shared/graph-layout/package.json
index 52928d5e97f1f340d2b653e45eedea657b842575..608983d8170b59d3d1ce3b3b99474e8c6f122259 100644
--- a/libs/shared/graph-layout/package.json
+++ b/libs/shared/graph-layout/package.json
@@ -17,6 +17,8 @@
     "web-worker": "^1.2.0"
   },
   "devDependencies": {
-    "graphology-generators": "^0.11.2"
+    "canvas": "^2.9.1",
+    "graphology-generators": "^0.11.2",
+    "jest-canvas-mock": "^2.4.0"
   }
 }
diff --git a/libs/shared/graph-layout/src/lib/cytoscape-layouts.ts b/libs/shared/graph-layout/src/lib/cytoscape-layouts.ts
index b5190896b5916d8652368bff7f39477f62b17a47..dfca253dc04244dde98885839c6f84312a68e046 100644
--- a/libs/shared/graph-layout/src/lib/cytoscape-layouts.ts
+++ b/libs/shared/graph-layout/src/lib/cytoscape-layouts.ts
@@ -99,7 +99,7 @@ export abstract class Cytoscape extends Layout<CytoscapeProvider> {
       cytonodes.push({
         data: {
           id: node,
-          // type: 'node',
+          type: 'node',
           label: 'start',
           count: 50,
           color: 'green',
@@ -111,7 +111,7 @@ export abstract class Cytoscape extends Layout<CytoscapeProvider> {
       cytonodes.push({
         data: {
           id: edge,
-          // type: 'edge',
+          type: 'edge',
           source: source,
           target: target,
         },
@@ -208,12 +208,12 @@ export abstract class Cytoscape extends Layout<CytoscapeProvider> {
         };
       });
 
-      console.log(
-        'width',
-        node.numericStyle('width'),
-        'height',
-        node.numericStyle('height')
-      );
+      // console.log(
+      //   'width',
+      //   node.numericStyle('width'),
+      //   'height',
+      //   node.numericStyle('height')
+      // );
     });
   }
 }
@@ -244,10 +244,10 @@ class CytoscapeKlay extends Cytoscape {
       // boundingBox: undefined,
 
       ready: function () {
-        console.log('Layout.ready');
+        console.info('Layout.ready');
       }, // on layoutready
       stop: function () {
-        console.log('Layout.stop');
+        console.debug('Layout.stop');
       }, // on layoutstop
     } as any);
     layout.run();
@@ -432,10 +432,10 @@ class CytoscapeElk extends Cytoscape {
         separateConnectedComponents: false,
       },
       ready: function () {
-        console.log('Layout.ready');
+        // console.log('Layout.start');
       }, // on layoutready
       stop: function () {
-        console.log('Layout.stop');
+        // console.log('Layout.stop');
       }, // on layoutstop
     } as any);
     layout.run();
@@ -464,10 +464,10 @@ class CytoscapeDagre extends Cytoscape {
     const layout = cy.layout({
       name: 'dagre',
       ready: function () {
-        console.log('Layout.ready');
+        // console.log('Layout.start');
       }, // on layoutready
       stop: function () {
-        console.log('Layout.stop');
+        // // console.log('Layout.stop');
       }, // on layoutstop
     } as any);
     layout.run();
@@ -497,10 +497,10 @@ class CytoscapeFCose extends Cytoscape {
       name: 'fcose',
       animate: false,
       ready: function () {
-        console.log('Layout.ready');
+        // // console.log('Layout.start');
       }, // on layoutready
       stop: function () {
-        console.log('Layout.stop');
+        // // console.log('Layout.stop');
       }, // on layoutstop
     } as any);
     layout.run();
@@ -531,10 +531,10 @@ class CytoscapeCoseBilkent extends Cytoscape {
       name: 'cose-bilkent',
       animate: false,
       ready: function () {
-        console.log('Layout.ready');
+        // console.log('Layout.start');
       }, // on layoutready
       stop: function () {
-        console.log('Layout.stop');
+        // console.log('Layout.stop');
       }, // on layoutstop
     } as any);
     layout.run();
@@ -563,10 +563,10 @@ class CytoscapeCise extends Cytoscape {
     const layout = cy.layout({
       name: 'cise',
       ready: function () {
-        console.log('Layout.ready');
+        // console.log('Layout.start');
       }, // on layoutready
       stop: function () {
-        console.log('Layout.stop');
+        // console.log('Layout.stop');
       }, // on layoutstop
     } as any);
     layout.run();
diff --git a/libs/shared/graph-layout/src/lib/layout-creator-usecase.spec.ts b/libs/shared/graph-layout/src/lib/layout-creator-usecase.spec.ts
index 2e86bb3556715997545768ad8d2822a5a0f9e45d..d41dc0b8b79e126989b8882efb49bcd99a855e0b 100644
--- a/libs/shared/graph-layout/src/lib/layout-creator-usecase.spec.ts
+++ b/libs/shared/graph-layout/src/lib/layout-creator-usecase.spec.ts
@@ -37,15 +37,7 @@ describe('LayoutFactory Graphology Libries', () => {
       const layoutAlgorithm = layoutFactory.createLayout('Graphology_noverlap');
       layoutAlgorithm?.layout(graph);
 
-      // const positionMap = new Set<string>();
-      graph.forEachNode((node, attr) => {
-        expect(graph.getNodeAttribute(node, 'x')).toBeDefined();
-        expect(graph.getNodeAttribute(node, 'y')).toBeDefined();
-
-        // const pos = '' + attr['x'] + '' + attr['y'];
-        // expect(positionMap.has(pos)).toBeFalsy();
-        // positionMap.add(pos);
-      });
+      checkNodeAttributesDefined(graph);
     },
     TIMEOUT
   );
@@ -66,13 +58,16 @@ describe('LayoutFactory Graphology Libries', () => {
       const layout = layoutFactory.createLayout('Graphology_noverlap');
       layout?.layout(graph);
 
-      const positionMap = new Set<string>();
-      graph.forEachNode((node, attr) => {
-        const pos = '' + attr['x'] + '' + attr['y'];
+      checkNodeAttributesDefined(graph);
+      checkPositionOverlap(graph);
 
-        expect(positionMap.has(pos)).toBeFalsy();
-        positionMap.add(pos);
-      });
+      // const positionMap = new Set<string>();
+      // graph.forEachNode((node, attr) => {
+      //   const pos = '' + attr['x'] + '' + attr['y'];
+
+      //   expect(positionMap.has(pos)).toBeFalsy();
+      //   positionMap.add(pos);
+      // });
     },
     TIMEOUT
   );
@@ -94,13 +89,16 @@ describe('LayoutFactory Graphology Libries', () => {
       layout?.setDimensions(100, 100);
       layout?.layout(graph);
 
-      const positionMap = new Set<string>();
-      graph.forEachNode((node, attr) => {
-        const pos = '' + attr['x'] + '' + attr['y'];
+      checkNodeAttributesDefined(graph);
+      checkPositionOverlap(graph);
 
-        expect(positionMap.has(pos)).toBeFalsy();
-        positionMap.add(pos);
-      });
+      // const positionMap = new Set<string>();
+      // graph.forEachNode((node, attr) => {
+      //   const pos = '' + attr['x'] + '' + attr['y'];
+
+      //   expect(positionMap.has(pos)).toBeFalsy();
+      //   positionMap.add(pos);
+      // });
     },
     TIMEOUT
   );
@@ -122,13 +120,16 @@ describe('LayoutFactory Graphology Libries', () => {
       layout?.setDimensions(100, 100);
       layout?.layout(graph);
 
-      const positionMap = new Set<string>();
-      graph.forEachNode((node, attr) => {
-        const pos = '' + attr['x'] + '' + attr['y'];
+      checkNodeAttributesDefined(graph);
+      checkPositionOverlap(graph);
 
-        expect(positionMap.has(pos)).toBeFalsy();
-        positionMap.add(pos);
-      });
+      // const positionMap = new Set<string>();
+      // graph.forEachNode((node, attr) => {
+      //   const pos = '' + attr['x'] + '' + attr['y'];
+
+      //   expect(positionMap.has(pos)).toBeFalsy();
+      //   positionMap.add(pos);
+      // });
     },
     TIMEOUT
   );
@@ -153,20 +154,17 @@ describe('LayoutFactory Graphology Libries', () => {
 
       // console.log('after');
 
-      const positionMap = new Set<string>();
-      graph.forEachNode((node, attr) => {
-        expect(graph.getNodeAttribute(node, 'x')).toBeDefined();
-        expect(graph.getNodeAttribute(node, 'y')).toBeDefined();
+      checkNodeAttributesDefined(graph);
+      checkPositionOverlap(graph);
 
-        const pos = '' + attr['x'] + '' + attr['y'];
-        console.log(pos);
-
-        expect(positionMap.has(pos)).toBeFalsy();
-        positionMap.add(pos);
+      // const positionMap = new Set<string>();
+      // graph.forEachNode((node, attr) => {
+      //   const pos = '' + attr['x'] + '' + attr['y'];
+      //   // console.log(pos);
 
-        expect(isNaN(graph.getNodeAttribute(node, 'x'))).toBeFalsy();
-        expect(isNaN(graph.getNodeAttribute(node, 'y'))).toBeFalsy();
-      });
+      //   expect(positionMap.has(pos)).toBeFalsy();
+      //   positionMap.add(pos);
+      // });
     },
     TIMEOUT
   );
@@ -193,7 +191,15 @@ describe('LayoutFactory Cytoscape Libraries', () => {
 
     // console.log('Number of nodes', graph.order);
     // console.log('Number of edges', graph.size);
-    expect(nodes).toBe(graph.order);
+    try {
+      expect(nodes).toBe(graph.order);
+    } catch (error) {
+      console.error(
+        'expect(nodes).toBe(graph.order) but was',
+        nodes,
+        graph.order
+      );
+    }
     expect(edges).toBe(graph.size);
   });
 
@@ -215,15 +221,15 @@ describe('LayoutFactory Cytoscape Libraries', () => {
       const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_klay');
       layoutAlgorithm?.layout(graph);
 
-      const positionMap = new Set<string>();
-      graph.forEachNode((node, attr) => {
-        expect(graph.getNodeAttribute(node, 'x')).toBeDefined();
-        expect(graph.getNodeAttribute(node, 'y')).toBeDefined();
+      checkNodeAttributesDefined(graph);
+      checkPositionOverlap(graph);
 
-        const pos = '' + attr['x'] + '' + attr['y'];
-        expect(positionMap.has(pos)).toBeFalsy();
-        positionMap.add(pos);
-      });
+      // const positionMap = new Set<string>();
+      // graph.forEachNode((node, attr) => {
+      //   const pos = '' + attr['x'] + '' + attr['y'];
+      //   expect(positionMap.has(pos)).toBeFalsy();
+      //   positionMap.add(pos);
+      // });
     },
     TIMEOUT
   );
@@ -246,15 +252,14 @@ describe('LayoutFactory Cytoscape Libraries', () => {
       const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_elk');
       layoutAlgorithm?.layout(graph, false);
 
-      const positionMap = new Set<string>();
-      graph.forEachNode((node, attr) => {
-        expect(graph.getNodeAttribute(node, 'x')).toBeDefined();
-        expect(graph.getNodeAttribute(node, 'y')).toBeDefined();
-
-        const pos = '' + attr['x'] + '' + attr['y'];
-        expect(positionMap.has(pos)).toBeFalsy();
-        positionMap.add(pos);
-      });
+      checkNodeAttributesDefined(graph);
+      checkPositionOverlap(graph);
+      // const positionMap = new Set<string>();
+      // graph.forEachNode((node, attr) => {
+      //   const pos = '' + attr['x'] + '' + attr['y'];
+      //   expect(positionMap.has(pos)).toBeFalsy();
+      //   positionMap.add(pos);
+      // });
     },
     TIMEOUT
   );
@@ -277,15 +282,8 @@ describe('LayoutFactory Cytoscape Libraries', () => {
       const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_dagre');
       layoutAlgorithm?.layout(graph);
 
-      const positionMap = new Set<string>();
-      graph.forEachNode((node, attr) => {
-        expect(graph.getNodeAttribute(node, 'x')).toBeDefined();
-        expect(graph.getNodeAttribute(node, 'y')).toBeDefined();
-
-        const pos = '' + attr['x'] + '' + attr['y'];
-        expect(positionMap.has(pos)).toBeFalsy();
-        positionMap.add(pos);
-      });
+      checkNodeAttributesDefined(graph);
+      checkPositionOverlap(graph);
     },
     TIMEOUT
   );
@@ -308,15 +306,8 @@ describe('LayoutFactory Cytoscape Libraries', () => {
       const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_fcose');
       layoutAlgorithm?.layout(graph, false);
 
-      const positionMap = new Set<string>();
-      graph.forEachNode((node, attr) => {
-        expect(graph.getNodeAttribute(node, 'x')).toBeDefined();
-        expect(graph.getNodeAttribute(node, 'y')).toBeDefined();
-
-        const pos = '' + attr['x'] + '' + attr['y'];
-        expect(positionMap.has(pos)).toBeFalsy();
-        positionMap.add(pos);
-      });
+      checkNodeAttributesDefined(graph);
+      checkPositionOverlap(graph);
     },
     TIMEOUT
   );
@@ -341,15 +332,8 @@ describe('LayoutFactory Cytoscape Libraries', () => {
       );
       layoutAlgorithm?.layout(graph);
 
-      const positionMap = new Set<string>();
-      graph.forEachNode((node, attr) => {
-        expect(graph.getNodeAttribute(node, 'x')).toBeDefined();
-        expect(graph.getNodeAttribute(node, 'y')).toBeDefined();
-
-        const pos = '' + attr['x'] + '' + attr['y'];
-        expect(positionMap.has(pos)).toBeFalsy();
-        positionMap.add(pos);
-      });
+      checkNodeAttributesDefined(graph);
+      checkPositionOverlap(graph);
     },
     TIMEOUT
   );
@@ -372,16 +356,50 @@ describe('LayoutFactory Cytoscape Libraries', () => {
       const layoutAlgorithm = layoutFactory.createLayout('Cytoscape_cise');
       layoutAlgorithm?.layout(graph);
 
-      const positionMap = new Set<string>();
-      graph.forEachNode((node, attr) => {
-        expect(graph.getNodeAttribute(node, 'x')).toBeDefined();
-        expect(graph.getNodeAttribute(node, 'y')).toBeDefined();
-
-        const pos = '' + attr['x'] + '' + attr['y'];
-        expect(positionMap.has(pos)).toBeFalsy();
-        positionMap.add(pos);
-      });
+      checkNodeAttributesDefined(graph);
+      checkPositionOverlap(graph);
     },
     TIMEOUT
   );
 });
+
+function checkNodeAttributesDefined(graph: Graph) {
+  try {
+    graph.forEachNode((node, attr) => {
+      expect(graph.getNodeAttribute(node, 'x')).toBeDefined();
+    });
+  } catch (error) {
+    console.error('Node attribute x is not defined');
+  }
+
+  try {
+    graph.forEachNode((node, attr) => {
+      expect(graph.getNodeAttribute(node, 'y')).toBeDefined();
+    });
+  } catch (error) {
+    console.error('Node attribute y is not defined');
+  }
+}
+
+function checkPositionOverlap(graph: Graph) {
+  // try {
+  //   const positionMap = new Set<string>();
+  //   graph.forEachNode((node, attr) => {
+  //     const pos = '' + attr['x'] + '' + attr['y'];
+  //     expect(positionMap.has(pos)).toBeFalsy();
+  //     positionMap.add(pos);
+  //   });
+  // } catch (error) {
+  //   console.error('A node position overlap was found');
+  // }
+
+  try {
+    const positionMap = new Set<string>();
+    graph.forEachNode((node, attr) => {
+      expect(isNaN(graph.getNodeAttribute(node, 'x'))).toBeFalsy();
+      expect(isNaN(graph.getNodeAttribute(node, 'y'))).toBeFalsy();
+    });
+  } catch (error) {
+    console.error('A node position may not be NaN');
+  }
+}
diff --git a/libs/shared/graph-layout/src/lib/layout.ts b/libs/shared/graph-layout/src/lib/layout.ts
index caa834f545a7e013965f979ce71839498f4c6b4a..f58a7c8ae01116689e9780f2defdb848a76bf414 100644
--- a/libs/shared/graph-layout/src/lib/layout.ts
+++ b/libs/shared/graph-layout/src/lib/layout.ts
@@ -10,7 +10,7 @@ export abstract class Layout<provider extends Providers> {
     public provider: provider,
     public algorithm: LayoutAlgorithm<provider>
   ) {
-    console.log(
+    console.info(
       `Created the following Layout: ${provider} - ${this.algorithm}`
     );
   }
diff --git a/libs/shared/graph-layout/yarn.lock b/libs/shared/graph-layout/yarn.lock
index a648f2038763e7f89799b53b4144232254ac577a..d6417799638911037b94648cfd91ac818d88b357 100644
--- a/libs/shared/graph-layout/yarn.lock
+++ b/libs/shared/graph-layout/yarn.lock
@@ -2,11 +2,56 @@
 # yarn lockfile v1
 
 
+"@mapbox/node-pre-gyp@^1.0.0":
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz#09a8781a3a036151cdebbe8719d6f8b25d4058bc"
+  integrity sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==
+  dependencies:
+    detect-libc "^2.0.0"
+    https-proxy-agent "^5.0.0"
+    make-dir "^3.1.0"
+    node-fetch "^2.6.7"
+    nopt "^5.0.0"
+    npmlog "^5.0.1"
+    rimraf "^3.0.2"
+    semver "^7.3.5"
+    tar "^6.1.11"
+
 "@yomguithereal/helpers@^1.1.1":
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/@yomguithereal/helpers/-/helpers-1.1.1.tgz#185dfb0f88ca2beec53d0adf6eed15c33b1c549d"
   integrity sha512-UYvAq/XCA7xoh1juWDYsq3W0WywOB+pz8cgVnE1b45ZfdMhBvHDrgmSFG3jXeZSr2tMTYLGHFHON+ekG05Jebg==
 
+abbrev@1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
+  integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
+
+agent-base@6:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
+  integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
+  dependencies:
+    debug "4"
+
+ansi-regex@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+  integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+"aproba@^1.0.3 || ^2.0.0":
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
+  integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
+
+are-we-there-yet@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c"
+  integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==
+  dependencies:
+    delegates "^1.0.0"
+    readable-stream "^3.6.0"
+
 avsdf-base@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/avsdf-base/-/avsdf-base-1.0.0.tgz#80c437d7d15d2bd201d9c31804e7b7a15a84781a"
@@ -14,6 +59,53 @@ avsdf-base@^1.0.0:
   dependencies:
     layout-base "^1.0.0"
 
+balanced-match@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+  integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+canvas@^2.9.1:
+  version "2.9.1"
+  resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.9.1.tgz#58ec841cba36cef0675bc7a74ebd1561f0b476b0"
+  integrity sha512-vSQti1uG/2gjv3x6QLOZw7TctfufaerTWbVe+NSduHxxLGB+qf3kFgQ6n66DSnuoINtVUjrLLIK2R+lxrBG07A==
+  dependencies:
+    "@mapbox/node-pre-gyp" "^1.0.0"
+    nan "^2.15.0"
+    simple-get "^3.0.3"
+
+chownr@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
+  integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
+
+color-name@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+color-support@^1.1.2:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
+  integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+console-control-strings@^1.0.0, console-control-strings@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+  integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
+
 cose-base@^1.0.0:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/cose-base/-/cose-base-1.0.3.tgz#650334b41b869578a543358b80cda7e0abe0a60a"
@@ -28,6 +120,11 @@ cose-base@^2.0.0:
   dependencies:
     layout-base "^2.0.0"
 
+cssfontparser@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/cssfontparser/-/cssfontparser-1.2.1.tgz#f4022fc8f9700c68029d542084afbaf425a3f3e3"
+  integrity sha1-9AIvyPlwDGgCnVQghK+69CWj8+M=
+
 cytoscape-cise@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/cytoscape-cise/-/cytoscape-cise-1.0.0.tgz#29ac061c74e036441c0b5687e7ebc25e919d507c"
@@ -90,16 +187,84 @@ dagre@^0.8.5:
     graphlib "^2.1.8"
     lodash "^4.17.15"
 
+debug@4:
+  version "4.3.4"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+  integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+  dependencies:
+    ms "2.1.2"
+
+decompress-response@^4.2.0:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986"
+  integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==
+  dependencies:
+    mimic-response "^2.0.0"
+
+delegates@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+  integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
+
+detect-libc@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd"
+  integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==
+
 elkjs@^0.7.0:
   version "0.7.1"
   resolved "https://registry.yarnpkg.com/elkjs/-/elkjs-0.7.1.tgz#4751c5e918a4988139baf7f214e010aea22de969"
   integrity sha512-lD86RWdh480/UuRoHhRcnv2IMkIcK6yMDEuT8TPBIbO3db4HfnVF+1lgYdQi99Ck0yb+lg5Eb46JCHI5uOsmAw==
 
+emoji-regex@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+  integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
 events@^3.3.0:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
   integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
 
+fs-minipass@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
+  integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
+  dependencies:
+    minipass "^3.0.0"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+  integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+gauge@^3.0.0:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395"
+  integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==
+  dependencies:
+    aproba "^1.0.3 || ^2.0.0"
+    color-support "^1.1.2"
+    console-control-strings "^1.0.0"
+    has-unicode "^2.0.1"
+    object-assign "^4.1.1"
+    signal-exit "^3.0.0"
+    string-width "^4.2.3"
+    strip-ansi "^6.0.1"
+    wide-align "^1.1.2"
+
+glob@^7.1.3:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
+  integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.4"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
 graphlib@^2.1.8:
   version "2.1.8"
   resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da"
@@ -177,11 +342,50 @@ graphology@^0.24.1:
     events "^3.3.0"
     obliterator "^2.0.2"
 
+has-unicode@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
+  integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
+
 heap@^0.2.6:
   version "0.2.7"
   resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc"
   integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==
 
+https-proxy-agent@^5.0.0:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
+  integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
+  dependencies:
+    agent-base "6"
+    debug "4"
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2, inherits@^2.0.3:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+is-fullwidth-code-point@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+  integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+jest-canvas-mock@^2.4.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.4.0.tgz#947b71442d7719f8e055decaecdb334809465341"
+  integrity sha512-mmMpZzpmLzn5vepIaHk5HoH3Ka4WykbSoLuG/EKoJd0x0ID/t+INo1l8ByfcUJuDM+RIsL4QDg/gDnBbrj2/IQ==
+  dependencies:
+    cssfontparser "^1.2.1"
+    moo-color "^1.0.2"
+
 klayjs@^0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/klayjs/-/klayjs-0.4.1.tgz#5bf9fadc7a3e44b94082bba501e7d803076dcfc2"
@@ -222,6 +426,52 @@ lodash@^4.17.15:
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
   integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
 
+lru-cache@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+  integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+  dependencies:
+    yallist "^4.0.0"
+
+make-dir@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
+  integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
+  dependencies:
+    semver "^6.0.0"
+
+mimic-response@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
+  integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
+
+minimatch@^3.0.4:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+  integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+minipass@^3.0.0:
+  version "3.1.6"
+  resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee"
+  integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==
+  dependencies:
+    yallist "^4.0.0"
+
+minizlib@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
+  integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
+  dependencies:
+    minipass "^3.0.0"
+    yallist "^4.0.0"
+
+mkdirp@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
+  integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
+
 mnemonist@^0.39.0:
   version "0.39.0"
   resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.39.0.tgz#4c83dd22e8d9d05dfb721ff66a905fec4c460041"
@@ -229,17 +479,207 @@ mnemonist@^0.39.0:
   dependencies:
     obliterator "^2.0.1"
 
+moo-color@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/moo-color/-/moo-color-1.0.3.tgz#d56435f8359c8284d83ac58016df7427febece74"
+  integrity sha512-i/+ZKXMDf6aqYtBhuOcej71YSlbjT3wCO/4H1j8rPvxDJEifdwgg5MaFyu6iYAT8GBZJg2z0dkgK4YMzvURALQ==
+  dependencies:
+    color-name "^1.1.4"
+
+ms@2.1.2:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+  integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+nan@^2.15.0:
+  version "2.15.0"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee"
+  integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==
+
+node-fetch@^2.6.7:
+  version "2.6.7"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
+  integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
+  dependencies:
+    whatwg-url "^5.0.0"
+
+nopt@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
+  integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==
+  dependencies:
+    abbrev "1"
+
+npmlog@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0"
+  integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==
+  dependencies:
+    are-we-there-yet "^2.0.0"
+    console-control-strings "^1.1.0"
+    gauge "^3.0.0"
+    set-blocking "^2.0.0"
+
+object-assign@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+  integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
 obliterator@^2.0.1, obliterator@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.2.tgz#25f50dc92e1181371b9d8209d11890f1a3c2fc21"
   integrity sha512-g0TrA7SbUggROhDPK8cEu/qpItwH2LSKcNl4tlfBNT54XY+nOsqrs0Q68h1V9b3HOSpIWv15jb1lax2hAggdIg==
 
+once@^1.3.0, once@^1.3.1:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+  dependencies:
+    wrappy "1"
+
 pandemonium@^1.5.0:
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/pandemonium/-/pandemonium-1.5.0.tgz#93f35af555de1420022b341e730215c51c725be3"
   integrity sha512-9PU9fy93rJhZHLMjX+4M1RwZPEYl6g7DdWKGmGNhkgBZR5+tOBVExNZc00kzdEGMxbaAvWdQy9MqGAScGwYlcA==
 
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+  integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+readable-stream@^3.6.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
+  integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
+  dependencies:
+    inherits "^2.0.3"
+    string_decoder "^1.1.1"
+    util-deprecate "^1.0.1"
+
+rimraf@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+  integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+  dependencies:
+    glob "^7.1.3"
+
+safe-buffer@~5.2.0:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+semver@^6.0.0:
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+  integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+
+semver@^7.3.5:
+  version "7.3.7"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
+  integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
+  dependencies:
+    lru-cache "^6.0.0"
+
+set-blocking@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+  integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+
+signal-exit@^3.0.0:
+  version "3.0.7"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+  integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+
+simple-concat@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
+  integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
+
+simple-get@^3.0.3:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.1.tgz#cc7ba77cfbe761036fbfce3d021af25fc5584d55"
+  integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==
+  dependencies:
+    decompress-response "^4.2.0"
+    once "^1.3.1"
+    simple-concat "^1.0.0"
+
+"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
+string_decoder@^1.1.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+  integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+  dependencies:
+    safe-buffer "~5.2.0"
+
+strip-ansi@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
+tar@^6.1.11:
+  version "6.1.11"
+  resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
+  integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==
+  dependencies:
+    chownr "^2.0.0"
+    fs-minipass "^2.0.0"
+    minipass "^3.0.0"
+    minizlib "^2.1.1"
+    mkdirp "^1.0.3"
+    yallist "^4.0.0"
+
+tr46@~0.0.3:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
+  integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
+
+util-deprecate@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+  integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
 web-worker@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.2.0.tgz#5d85a04a7fbc1e7db58f66595d7a3ac7c9c180da"
   integrity sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==
+
+webidl-conversions@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+  integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
+
+whatwg-url@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
+  integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
+  dependencies:
+    tr46 "~0.0.3"
+    webidl-conversions "^3.0.0"
+
+wide-align@^1.1.2:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
+  integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
+  dependencies:
+    string-width "^1.0.2 || 2 || 3 || 4"
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+
+yallist@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+  integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
diff --git a/libs/shared/mock-data/src/schema/mock-data.spec.ts b/libs/shared/mock-data/src/schema/mock-data.spec.ts
index f86e6d316a9867914bc99061442e2b3f8f56e854..7aa1fa5766f3badc02e1c7e851bddb7968037e25 100644
--- a/libs/shared/mock-data/src/schema/mock-data.spec.ts
+++ b/libs/shared/mock-data/src/schema/mock-data.spec.ts
@@ -1,8 +1,8 @@
 import Graph from 'graphology';
-import { movieSchemaRaw, movieSchema } from './moviesSchemaRaw';
-import { northWindSchema, northwindSchemaRaw } from './northwindSchemaRaw';
-import { simpleSchema, simpleSchemaRaw } from './simpleRaw';
-import { twitterSchema, twitterSchemaRaw } from './twitterSchemaRaw';
+import { movieSchemaRaw, movieSchema } from '..';
+import { northWindSchema, northwindSchemaRaw } from '..';
+import { simpleSchema, simpleSchemaRaw } from '..';
+import { twitterSchema, twitterSchemaRaw } from '..';
 
 describe('MockData Tests', () => {
   it('should have raw data available movie', () => {
@@ -29,27 +29,35 @@ describe('MockData Tests', () => {
     const graph = movieSchema;
     expect(graph);
 
-    expect(graph.constructor.name.toLowerCase().indexOf('graph') != -1).toBeTruthy();
+    expect(
+      graph.constructor.name.toLowerCase().indexOf('graph') != -1
+    ).toBeTruthy();
   });
 
   it('should have data available as graphology model northwind', () => {
     const graph = northWindSchema;
     expect(graph);
 
-    expect(graph.constructor.name.toLowerCase().indexOf('graph') != -1).toBeTruthy();
+    expect(
+      graph.constructor.name.toLowerCase().indexOf('graph') != -1
+    ).toBeTruthy();
   });
 
   it('should have data available as graphology model simpleSchemaRaw', () => {
     const graph = simpleSchema;
     expect(graph);
 
-    expect(graph.constructor.name.toLowerCase().indexOf('graph') != -1).toBeTruthy();
+    expect(
+      graph.constructor.name.toLowerCase().indexOf('graph') != -1
+    ).toBeTruthy();
   });
 
   it('should have data available as graphology model twitterSchemaRaw', () => {
     const graph = twitterSchema;
     expect(graph);
 
-    expect(graph.constructor.name.toLowerCase().indexOf('graph') != -1).toBeTruthy();
+    expect(
+      graph.constructor.name.toLowerCase().indexOf('graph') != -1
+    ).toBeTruthy();
   });
 });
diff --git a/libs/shared/mock-data/src/schema/moviesSchemaRaw.ts b/libs/shared/mock-data/src/schema/moviesSchemaRaw.ts
index 762bce33c65af64ba2bdac7928697224afd5d651..678ff5fcfd56cd12ee2d4d45cb2564d998f7a533 100644
--- a/libs/shared/mock-data/src/schema/moviesSchemaRaw.ts
+++ b/libs/shared/mock-data/src/schema/moviesSchemaRaw.ts
@@ -1,7 +1,7 @@
 import { SchemaUtils } from '@graphpolaris/schema-utils';
-import { SchemaFromBackend } from 'libs/shared/models/src';
+import { SchemaFromBackend } from '@graphpolaris/models';
 
-export const movieSchemaRaw = {
+export const movieSchemaRaw: SchemaFromBackend = {
   nodes: [
     {
       name: 'Movie',
@@ -98,6 +98,5 @@ export const movieSchemaRaw = {
   ],
 };
 
-export const movieSchema = SchemaUtils.ParseSchemaFromBackend(
-  movieSchemaRaw as SchemaFromBackend
-);
+export const movieSchema = SchemaUtils.ParseSchemaFromBackend(movieSchemaRaw);
+
diff --git a/libs/shared/mock-data/src/schema/northwindSchemaRaw.ts b/libs/shared/mock-data/src/schema/northwindSchemaRaw.ts
index 59f248f5fbd9117a1021cc1852100e24c02f85ec..f38a405228273fe992d3aea70a624ea0c01acf74 100644
--- a/libs/shared/mock-data/src/schema/northwindSchemaRaw.ts
+++ b/libs/shared/mock-data/src/schema/northwindSchemaRaw.ts
@@ -1,7 +1,7 @@
 import { SchemaUtils } from '@graphpolaris/schema-utils';
-import { SchemaFromBackend } from 'libs/shared/models/src';
+import { SchemaFromBackend } from '@graphpolaris/models';
 
-export const northwindSchemaRaw = {
+export const northwindSchemaRaw: SchemaFromBackend = {
   nodes: [
     {
       name: 'Order',
@@ -286,6 +286,5 @@ export const northwindSchemaRaw = {
   ],
 };
 
-export const northWindSchema = SchemaUtils.ParseSchemaFromBackend(
-  northwindSchemaRaw as SchemaFromBackend
-);
+export const northWindSchema =
+  SchemaUtils.ParseSchemaFromBackend(northwindSchemaRaw);
diff --git a/libs/shared/mock-data/src/schema/simpleRaw.ts b/libs/shared/mock-data/src/schema/simpleRaw.ts
index e8fee0ca1eea766231f802433ee7f52ecd7b273d..668cf403c8fcb3b030c9bd883400d98efa101484 100644
--- a/libs/shared/mock-data/src/schema/simpleRaw.ts
+++ b/libs/shared/mock-data/src/schema/simpleRaw.ts
@@ -1,7 +1,7 @@
 import { SchemaUtils } from '@graphpolaris/schema-utils';
 import { SchemaFromBackend } from '@graphpolaris/models';
 
-export const simpleSchemaRaw = {
+export const simpleSchemaRaw: SchemaFromBackend = {
   nodes: [
     {
       name: 'Thijs',
@@ -103,6 +103,4 @@ export const simpleSchemaRaw = {
   ],
 };
 
-export const simpleSchema = SchemaUtils.ParseSchemaFromBackend(
-  simpleSchemaRaw as SchemaFromBackend
-);
+export const simpleSchema = SchemaUtils.ParseSchemaFromBackend(simpleSchemaRaw);
diff --git a/libs/shared/mock-data/src/schema/twitterSchemaRaw.ts b/libs/shared/mock-data/src/schema/twitterSchemaRaw.ts
index ba922222c3fa2d9600f1318649ae59c7d0033f90..871ab8e755c4704668fef01d8a0f57e63db139df 100644
--- a/libs/shared/mock-data/src/schema/twitterSchemaRaw.ts
+++ b/libs/shared/mock-data/src/schema/twitterSchemaRaw.ts
@@ -1,7 +1,7 @@
 import { SchemaUtils } from '@graphpolaris/schema-utils';
-import { SchemaFromBackend } from 'libs/shared/models/src';
+import { SchemaFromBackend } from '@graphpolaris/models';
 
-export const twitterSchemaRaw = {
+export const twitterSchemaRaw: SchemaFromBackend = {
   nodes: [
     {
       name: 'Me',
@@ -315,6 +315,5 @@ export const twitterSchemaRaw = {
   ],
 };
 
-export const twitterSchema = SchemaUtils.ParseSchemaFromBackend(
-  twitterSchemaRaw as SchemaFromBackend
-);
+export const twitterSchema =
+  SchemaUtils.ParseSchemaFromBackend(twitterSchemaRaw);
diff --git a/libs/shared/mock-data/tsconfig.spec.json b/libs/shared/mock-data/tsconfig.spec.json
index 4afc999ad429eea63e777308527c4e8f629e4198..59041160ab21de130a3c8d6c7c916e5d8094b8dc 100644
--- a/libs/shared/mock-data/tsconfig.spec.json
+++ b/libs/shared/mock-data/tsconfig.spec.json
@@ -2,7 +2,7 @@
   "extends": "./tsconfig.json",
   "compilerOptions": {
     "outDir": "../../../dist/out-tsc",
-    "module": "commonjs",
+    "module": "esnext",
     "types": ["jest", "node"],
     "composite": true
   },
diff --git a/libs/shared/schema-utils/src/lib/schema-utils.ts b/libs/shared/schema-utils/src/lib/schema-utils.ts
index 0ca15baedb7cdbe011e12d9eaf688c0fa23a73ed..accb03be132218c8f6633a6b53004d02622c439d 100644
--- a/libs/shared/schema-utils/src/lib/schema-utils.ts
+++ b/libs/shared/schema-utils/src/lib/schema-utils.ts
@@ -20,6 +20,8 @@ export class SchemaUtils {
       });
     });
 
+    
+
     // The name of the edge will be name + from + to, since edge names are not unique
     edges.forEach((edge: Edge) => {
       const edgeID = [edge.name, '_', edge.from, edge.to].join(''); //ensure that all interpreted as string
diff --git a/libs/shared/ui/pills/.babelrc b/libs/shared/ui/pills/.babelrc
new file mode 100644
index 0000000000000000000000000000000000000000..ccae900be42788285eb6e1b3a2af4b81f56f14fe
--- /dev/null
+++ b/libs/shared/ui/pills/.babelrc
@@ -0,0 +1,12 @@
+{
+  "presets": [
+    [
+      "@nrwl/react/babel",
+      {
+        "runtime": "automatic",
+        "useBuiltIns": "usage"
+      }
+    ]
+  ],
+  "plugins": []
+}
diff --git a/libs/shared/ui/pills/.eslintrc.json b/libs/shared/ui/pills/.eslintrc.json
new file mode 100644
index 0000000000000000000000000000000000000000..3cd64217595c25dc61c2fd055ec4dbe21195a3e3
--- /dev/null
+++ b/libs/shared/ui/pills/.eslintrc.json
@@ -0,0 +1,18 @@
+{
+  "extends": ["plugin:@nrwl/nx/react", "../../../../.eslintrc.json"],
+  "ignorePatterns": ["!**/*"],
+  "overrides": [
+    {
+      "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
+      "rules": {}
+    },
+    {
+      "files": ["*.ts", "*.tsx"],
+      "rules": {}
+    },
+    {
+      "files": ["*.js", "*.jsx"],
+      "rules": {}
+    }
+  ]
+}
diff --git a/libs/shared/ui/pills/README.md b/libs/shared/ui/pills/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..84610a47f2335ec607ead16b69bc23b460a48655
--- /dev/null
+++ b/libs/shared/ui/pills/README.md
@@ -0,0 +1,7 @@
+# shared-ui-pills
+
+This library was generated with [Nx](https://nx.dev).
+
+## Running unit tests
+
+Run `nx test shared-ui-pills` to execute the unit tests via [Jest](https://jestjs.io).
diff --git a/libs/shared/ui/pills/jest.config.js b/libs/shared/ui/pills/jest.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..7d0623362ab495667bd3a33c5c51f96c3383564b
--- /dev/null
+++ b/libs/shared/ui/pills/jest.config.js
@@ -0,0 +1,9 @@
+module.exports = {
+  displayName: 'shared-ui-pills',
+  preset: '../../../../jest.preset.js',
+  transform: {
+    '^.+\\.[tj]sx?$': 'babel-jest',
+  },
+  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
+  coverageDirectory: '../../../../coverage/libs/shared/ui/pills',
+};
diff --git a/libs/shared/ui/pills/project.json b/libs/shared/ui/pills/project.json
new file mode 100644
index 0000000000000000000000000000000000000000..ea222183fbe8abb6af31b3ddf45edfbbf6fbd836
--- /dev/null
+++ b/libs/shared/ui/pills/project.json
@@ -0,0 +1,23 @@
+{
+  "root": "libs/shared/ui/pills",
+  "sourceRoot": "libs/shared/ui/pills/src",
+  "projectType": "library",
+  "tags": [],
+  "targets": {
+    "lint": {
+      "executor": "@nrwl/linter:eslint",
+      "outputs": ["{options.outputFile}"],
+      "options": {
+        "lintFilePatterns": ["libs/shared/ui/pills/**/*.{ts,tsx,js,jsx}"]
+      }
+    },
+    "test": {
+      "executor": "@nrwl/jest:jest",
+      "outputs": ["coverage/libs/shared/ui/pills"],
+      "options": {
+        "jestConfig": "libs/shared/ui/pills/jest.config.js",
+        "passWithNoTests": true
+      }
+    }
+  }
+}
diff --git a/libs/shared/ui/pills/src/customFlowLines/connection.tsx b/libs/shared/ui/pills/src/customFlowLines/connection.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..23fb1ec2ee51d04477f18aad63ae5b0f15dd6dfb
--- /dev/null
+++ b/libs/shared/ui/pills/src/customFlowLines/connection.tsx
@@ -0,0 +1,74 @@
+import { handles } from '@graphpolaris/querybuilder/usecases';
+import React from 'react';
+import { EdgeProps, getSmoothStepPath, Position } from 'react-flow-renderer';
+
+/**
+ * A custom query element edge line component.
+ * @param {EdgeProps} param0 The coordinates for the start and end point, the id and the style.
+ */
+//  export const EntityRFPill = React.memo(({ data }: { data: any }) => {
+export function ConnectionLine({
+  id,
+  sourceX,
+  sourceY,
+  targetX,
+  targetY,
+  style,
+  sourceHandleId,
+  targetHandleId,
+}: EdgeProps) {
+  //Centering the line
+  sourceY -= 3;
+  targetY -= 3;
+
+  // Correct line positions with hardcoded numbers, because react flow lacks this functionality
+  // if (sourceHandleId == ) sourceX += 2;
+
+  // if (targetHandleId == Handles.ToAttributeHandle) targetX += 2;
+
+  let spos: Position = Position.Bottom;
+  if (sourceHandleId == handles.relation.fromEntity) {
+    spos = Position.Left;
+    sourceX += 7;
+    sourceY += 3;
+  } else if (sourceHandleId == handles.relation.toEntity) {
+    spos = Position.Right;
+    sourceX -= 2;
+    sourceY -= 3;
+  } else if (
+    sourceHandleId !== undefined &&
+    sourceHandleId !== null &&
+    sourceHandleId.includes('functionHandle')
+  ) {
+    spos = Position.Top;
+    sourceX -= 4;
+    sourceY += 3;
+  }
+
+  let tpos: Position = Position.Bottom;
+  if (targetHandleId == handles.relation.fromEntity) {
+    tpos = Position.Left;
+    targetX += 7;
+    targetY += 3;
+  } else if (targetHandleId == handles.relation.toEntity) {
+    tpos = Position.Right;
+    targetX -= 2;
+    targetY -= 3;
+  }
+
+  // Create smoothstep line
+  const path = getSmoothStepPath({
+    sourceX: sourceX,
+    sourceY: sourceY,
+    sourcePosition: spos,
+    targetX: targetX,
+    targetY: targetY,
+    targetPosition: tpos,
+  });
+
+  return (
+    <g stroke="#2e2e2e">
+      <path id={id} fill="none" strokeWidth={3} style={style} d={path} />
+    </g>
+  );
+}
diff --git a/libs/shared/ui/pills/src/customFlowLines/connectionDrag.tsx b/libs/shared/ui/pills/src/customFlowLines/connectionDrag.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..0d567e584d66c26537abc2c1f6f3a97469dad1a5
--- /dev/null
+++ b/libs/shared/ui/pills/src/customFlowLines/connectionDrag.tsx
@@ -0,0 +1,41 @@
+import React from 'react';
+import { ConnectionLineComponentProps } from 'react-flow-renderer';
+
+/**
+ * A custom query element to render the line when connecting flow elements.
+ * @param {ConnectionLineComponentProps} param0 Source and target coordinates of the edges.
+ */
+export function ConnectionDragLine({
+  sourceX,
+  sourceY,
+  targetX,
+  targetY,
+}: ConnectionLineComponentProps) {
+  return (
+    <g>
+      <path
+        fill="none"
+        stroke="#222"
+        strokeWidth={2.5}
+        className="animated"
+        d={`M${sourceX},${sourceY}L ${targetX},${targetY}`}
+      />
+      <circle
+        cx={sourceX}
+        cy={sourceY}
+        fill="#fff"
+        r={3}
+        stroke="#222"
+        strokeWidth={1.5}
+      />
+      <circle
+        cx={targetX}
+        cy={targetY}
+        fill="#fff"
+        r={3}
+        stroke="#222"
+        strokeWidth={1.5}
+      />
+    </g>
+  );
+}
diff --git a/libs/shared/ui/pills/src/customFlowPills/attributepill/attributepill.module.scss b/libs/shared/ui/pills/src/customFlowPills/attributepill/attributepill.module.scss
new file mode 100644
index 0000000000000000000000000000000000000000..9ba5ba22c49c279617faef06e92d4256e49a36fb
--- /dev/null
+++ b/libs/shared/ui/pills/src/customFlowPills/attributepill/attributepill.module.scss
@@ -0,0 +1,60 @@
+@use './variables.module.scss';
+
+.attribute {
+  display: flex;
+  font-family: monospace;
+  font-weight: bold;
+  font-size: variables.$fontsize;
+  border-radius: 2px;
+}
+
+// .handle {
+//   border: 0px;
+//   border-radius: 10px;
+//   left: 12px;
+//   width: 7px;
+//   height: 7px;
+//   margin-bottom: 11px;
+//   background: rgba(255, 255, 255, 0.6);
+//   box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3);
+//   transform-origin: center;
+// }
+
+.contentWrapper {
+  display: flex;
+  align-items: center;
+
+  .content {
+    padding: variables.$ypad 0 variables.$ypad 1ch;
+    max-width: 15ch;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+  }
+}
+
+.attributeInput {
+  float: right;
+  padding: 0 1ch 0 0;
+  display: flex;
+  align-items: center;
+
+  input {
+    background-color: rgba(100, 100, 100, 0.1);
+    font-family: monospace;
+    font-size: variables.$fontsize;
+    border: 1px solid rgba(100, 100, 100, 0.3);
+    border-radius: 2px;
+    height: variables.$height;
+    outline: none;
+    transition: border 0.3s;
+    color: black;
+    &::placeholder {
+      color: black;
+    }
+
+    &:focus {
+      border: 1px solid rgba(0, 0, 0, 0.3);
+    }
+  }
+}
diff --git a/libs/shared/ui/pills/src/customFlowPills/attributepill/attributepill.tsx b/libs/shared/ui/pills/src/customFlowPills/attributepill/attributepill.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..41b5803cc251e5f345702e9a364ee9eac4c6b113
--- /dev/null
+++ b/libs/shared/ui/pills/src/customFlowPills/attributepill/attributepill.tsx
@@ -0,0 +1,96 @@
+import {
+  CheckDatatypeConstraint,
+  GetAttributeBoolOperators,
+} from '@graphpolaris/querybuilder/usecases';
+import {
+  updateQBAttributeOperator,
+  updateQBAttributeValue,
+  useAppDispatch,
+} from '@graphpolaris/shared/data-access/store';
+import { useTheme } from '@mui/material';
+import React, { useMemo, useState } from 'react';
+import styles from './attributepill.module.scss';
+import AttributeOperatorSelect from './operatorselect';
+
+/**
+ * Component to render an attribute flow element
+ * @param {FlowElement<EntityData>)} param0 The data of an entity flow element.
+ */
+export const AttributeRFPill = React.memo(
+  ({ id, data }: { id: string; data: any }) => {
+    const theme = useTheme();
+    const dispatch = useAppDispatch();
+    const [value, setValue] = useState(data?.value || '');
+
+    const onChange = (e: any) => {
+      setValue(e.target.value);
+    };
+    const validateInput = () => {
+      const newValue = CheckDatatypeConstraint(data.datatype, value);
+      setValue(newValue);
+      dispatch(updateQBAttributeValue({ id, value: newValue }));
+    };
+
+    // Calculates the size of the input
+    const getInputWidth = () => {
+      if (value == '') return 1;
+      else if (value.length > 10) return 10;
+      return value.length;
+    };
+
+    const boolOperators = useMemo(
+      () => GetAttributeBoolOperators(data?.datatype),
+      [data?.datatype]
+    );
+
+    // Determine the backgroundcolor based on if the attribute is connected to a entity or relation
+    let bgcolor;
+    if (data?.attributeOfA == 'entity')
+      bgcolor = theme.palette.queryBuilder.entity.lighterbg;
+    else if (data?.attributeOfA == 'relation')
+      bgcolor = theme.palette.queryBuilder.relation.lighterbg;
+    else bgcolor = theme.palette.queryBuilder.attribute.background;
+
+    return (
+      <div
+        className={styles.attribute}
+        style={{
+          background: bgcolor,
+          color: theme.palette.queryBuilder.text,
+        }}
+      >
+        {/* <Handle
+        id={Handles.Attribute}
+        type="source"
+        position={Position.Bottom}
+        className={styles.handle}
+      /> */}
+        <div className={styles.contentWrapper}>
+          <span className={styles.content} title={data.name}>
+            {data.name}
+          </span>
+          <AttributeOperatorSelect
+            selected={data?.operator}
+            options={boolOperators}
+            changed={(o) =>
+              dispatch(updateQBAttributeOperator({ id, operator: o.value }))
+            }
+          />
+          <span className={styles.attributeInput}>
+            <input
+              style={{ maxWidth: `${getInputWidth()}ch` }}
+              type="string"
+              placeholder={'?'}
+              value={value}
+              onChange={onChange}
+              onBlur={validateInput}
+              onKeyDown={(e) => e.key == 'Enter' && validateInput()}
+            ></input>
+          </span>
+        </div>
+      </div>
+    );
+  }
+);
+
+export default AttributeRFPill;
diff --git a/libs/shared/ui/pills/src/customFlowPills/attributepill/operatorselect.module.scss b/libs/shared/ui/pills/src/customFlowPills/attributepill/operatorselect.module.scss
new file mode 100644
index 0000000000000000000000000000000000000000..1c31d2744f029e77c53efbfac2aae8983640770a
--- /dev/null
+++ b/libs/shared/ui/pills/src/customFlowPills/attributepill/operatorselect.module.scss
@@ -0,0 +1,70 @@
+@use './variables.module.scss';
+
+.container {
+  position: relative;
+  vertical-align: baseline;
+  margin: 0 1ch;
+  font-weight: normal;
+  font-size: 7px;
+}
+
+.valueContainer {
+  color: #6a6a6a;
+  border: 1px solid rgba(0, 0, 0, 0);
+  border-radius: 2px;
+  background-color: transparent;
+
+  transition: border-color 0.2s;
+
+  height: variables.$height;
+  align-items: center;
+  display: flex;
+  padding: 0 1px 1px 1px;
+
+  &.highlighted,
+  &:hover {
+    border-color: rgba(0, 0, 0, 0.4);
+  }
+}
+
+.listbox {
+  font-size: 10px;
+  box-sizing: border-box;
+  padding: 5px;
+  margin: 5px 0 0 0;
+  list-style: none;
+  position: absolute;
+  height: auto;
+  box-shadow: 0 5px 13px -3px #e0e3e7;
+  background: white;
+  border: 1px solid #cdd2d7;
+  border-radius: 0.75em;
+  color: #1a2027;
+  overflow: auto;
+  z-index: 1;
+  outline: 0px;
+  left: -8px;
+
+  &.hidden {
+    opacity: 0;
+    visibility: hidden;
+    transition: opacity 0.4s 0.1s ease, visibility 0.4s 0.1s step-end;
+  }
+
+  & > li {
+    padding: 1px 4px;
+    border-radius: 2px;
+
+    &.selected {
+      background: #f1f1f1;
+    }
+
+    &:hover {
+      background: #e7ebf0;
+    }
+
+    &[aria-selected='true'] {
+      background: #e0e3e7;
+    }
+  }
+}
diff --git a/libs/shared/ui/pills/src/customFlowPills/attributepill/operatorselect.tsx b/libs/shared/ui/pills/src/customFlowPills/attributepill/operatorselect.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..470c87618fdbd49fd8d70b3de926e0d3605cd12a
--- /dev/null
+++ b/libs/shared/ui/pills/src/customFlowPills/attributepill/operatorselect.tsx
@@ -0,0 +1,85 @@
+import * as React from 'react';
+import { SelectOption } from '@mui/base';
+import styles from './operatorselect.module.scss';
+import { useRef, useState } from 'react';
+
+// const grey = {
+//   100: '#E7EBF0',
+//   200: '#E0E3E7',
+//   300: '#CDD2D7',
+//   400: '#B2BAC2',
+//   500: '#A0AAB4',
+//   600: '#6F7E8C',
+//   700: '#3E5060',
+//   800: '#2D3843',
+//   900: '#1A2027',
+// };
+
+interface Props {
+  options: SelectOption<string>[];
+  selected: string;
+  changed?: (newSelected: SelectOption<string>) => void;
+}
+
+function AttributeOperatorSelect({
+  options,
+  selected,
+  changed = () => {},
+}: Props) {
+  const listboxRef = useRef<HTMLUListElement>(null);
+  const [listboxVisible, setListboxVisible] = useState(false);
+  const [currSelected, setCurrSelected] = useState(
+    options.find((o) => o.value == selected)?.label || options[0].label
+  );
+
+  React.useEffect(() => {
+    if (listboxVisible) {
+      listboxRef.current?.focus();
+    }
+  }, [listboxVisible]);
+
+  const changeSelected = (option: SelectOption<string>) => {
+    if (option.label != currSelected) {
+      setCurrSelected(option.label);
+      changed(option);
+    }
+  };
+
+  return (
+    <div
+      className={styles.container}
+      // onMouseOver={() => setListboxVisible(true)}
+      onMouseOut={() => setListboxVisible(false)}
+      onClick={() => setListboxVisible(true)}
+      onFocus={() => setListboxVisible(true)}
+      onBlur={() => setListboxVisible(false)}
+    >
+      <div
+        className={
+          styles.valueContainer + ' ' + (listboxVisible && styles.highlighted)
+        }
+      >
+        <span>{currSelected}</span>
+      </div>
+      {options.length > 1 && (
+        <ul
+          className={styles.listbox + ' ' + (!listboxVisible && styles.hidden)}
+          ref={listboxRef}
+          onMouseOver={() => setListboxVisible(true)}
+        >
+          {options.map((option) => (
+            <li
+              className={option.label == currSelected ? styles.selected : ''}
+              key={option.value}
+              onClick={() => changeSelected(option)}
+            >
+              {option.label}
+            </li>
+          ))}
+        </ul>
+      )}
+    </div>
+  );
+}
+
+export default AttributeOperatorSelect;
diff --git a/libs/shared/ui/pills/src/customFlowPills/attributepill/variables.module.scss b/libs/shared/ui/pills/src/customFlowPills/attributepill/variables.module.scss
new file mode 100644
index 0000000000000000000000000000000000000000..08bc31bb67c70a043d0114880fceada930fe14b1
--- /dev/null
+++ b/libs/shared/ui/pills/src/customFlowPills/attributepill/variables.module.scss
@@ -0,0 +1,3 @@
+$height: 5px;
+$fontsize: 6px;
+$ypad: 2px;
diff --git a/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.module.scss b/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.module.scss
new file mode 100644
index 0000000000000000000000000000000000000000..755d2b41d564abe3f9e4eb41f56865ef7d2432f4
--- /dev/null
+++ b/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.module.scss
@@ -0,0 +1,49 @@
+.entity {
+  display: flex;
+  font-family: monospace;
+  font-weight: bold;
+  font-size: 10px;
+  padding: 4px 2ch;
+  border-radius: 3px;
+}
+
+.highlighted {
+  box-shadow: black 0 0 2px;
+}
+
+.handleLeft {
+  border: 0px;
+  border-radius: 0px;
+  left: 12px;
+  width: 7px;
+  height: 7px;
+  margin-bottom: 11px;
+  background: rgba(255, 255, 255, 0.6);
+  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3);
+  transform-origin: center;
+}
+
+// .handleBottom {
+//   border: 0px;
+//   border-radius: 0px;
+//   width: 7px;
+//   height: 7px;
+//   left: 27.5px;
+//   margin-bottom: 11px;
+//   background: rgba(255, 255, 255, 0.6);
+//   box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3);
+//   transform: rotate(-45deg);
+//   transform-origin: center;
+// }
+
+.contentWrapper {
+  margin-left: 3ch;
+
+  span {
+    max-width: 20ch;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+    display: block;
+  }
+}
diff --git a/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.stories.tsx b/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.stories.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..1ece902308aec3688acd3edd7086ffa89b87b821
--- /dev/null
+++ b/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.stories.tsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import { ComponentMeta, ComponentStory } from '@storybook/react';
+import EntityRFPill from './entitypill';
+import { configureStore } from '@reduxjs/toolkit';
+import { Provider } from 'react-redux';
+import { GraphPolarisThemeProvider } from '@graphpolaris/shared/data-access/theme';
+
+import {
+  colorPaletteConfigSlice,
+  querybuilderSlice,
+  schemaSlice,
+} from '@graphpolaris/shared/data-access/store';
+
+// import createTheme from '@mui/system/createTheme';
+// import { ThemeProvider } from '@mui/material';
+// const theme = createTheme({});
+
+export default {
+  /* 👇 The title prop is optional.
+   * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
+   * to learn how to generate automatic titles
+   */
+  title: 'Components/Pills/EntityRFPill',
+  component: EntityRFPill,
+  decorators: [
+    (story) => (
+      <Provider store={Mockstore}>
+        <GraphPolarisThemeProvider>{story()}</GraphPolarisThemeProvider>
+      </Provider>
+    ),
+  ],
+} as ComponentMeta<typeof EntityRFPill>;
+
+// A super-simple mock of a redux store
+const Mockstore = configureStore({
+  reducer: {
+    colorPaletteConfig: colorPaletteConfigSlice.reducer,
+    querybuilder: querybuilderSlice.reducer,
+    // schema: schemaSlice.reducer,
+  },
+});
+
+const Template: ComponentStory<typeof EntityRFPill> = (args) => (
+  <EntityRFPill {...args} />
+);
+// const Template = (args: any) => <EntityRFPill {...args} />;
+
+export const Default = Template.bind({});
+Default.args = {
+  data: {
+    name: 'TestEntity',
+  },
+};
+
+// Default.decorators = [
+//   (story) => (
+//     <Provider store={Mockstore}>
+//       <GraphPolarisThemeProvider>{story()}</GraphPolarisThemeProvider>
+//     </Provider>
+//   ),
+// ];
diff --git a/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.tsx b/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..29bd77d043e8b63c00ce46588efbf910e121bdcf
--- /dev/null
+++ b/libs/shared/ui/pills/src/customFlowPills/entitypill/entitypill.tsx
@@ -0,0 +1,72 @@
+// import { handles } from '@graphpolaris/querybuilder/usecases';
+import { useTheme } from '@mui/material';
+import React, { useEffect } from 'react';
+import { FlowElement, Handle, Position } from 'react-flow-renderer';
+import styles from './entitypill.module.scss';
+import cn from 'classnames';
+
+export const Handless = {
+  entity: {
+    attributes: 'attributesHandle',
+    relations: 'relationsHandle',
+  },
+};
+
+/** Links need handles to what they are connected to (and which side) */
+export enum Handles {
+  RelationLeft = 'leftEntityHandle', //target
+  RelationRight = 'rightEntityHandle', //target
+  ToAttributeHandle = 'attributesHandle', //target
+  ToRelation = 'relationsHandle', //source
+  Attribute = 'AttributeHandle', //source
+  ReceiveFunction = 'receiveFunctionHandle', //target
+  FunctionBase = 'functionHandle_', // + name from FunctionTypes args //source
+}
+
+/**
+ * Component to render an entity flow element
+ * @param {FlowElement<EntityData>)} param0 The data of an entity flow element.
+ */
+export const EntityRFPill = React.memo(({ data }: { data: any }) => {
+  const theme = useTheme();
+
+  // console.log('EntityRFPill', data);
+  return (
+    <div
+      className={cn(styles['entity'], {
+        [styles['highlighted']]: data.suggestedForConnection,
+      })}
+      style={{
+        background: theme.palette.queryBuilder.entity.background,
+        color: theme.palette.queryBuilder.text,
+      }}
+    >
+      {/* <Handle
+        id={handles.entity.relation}
+        type="source"
+        position={Position.Bottom}
+        className={styles['handleLeft']}
+        style={data?.isConnected ? { backgroundColor: '#2e2e2e' } : {}}
+      /> */}
+      {/* <Handle
+      id={Handles.ToAttributeHandle}
+      type="target"
+      position={Position.Bottom}
+      className={styles.handleBottom}
+      /> */}
+      <Handle
+        type="target"
+        position={Position.Left}
+        isValidConnection={(connection) => connection.source === 'some-id'}
+        onConnect={(params) => console.log('handle onConnect', params)}
+        style={{ background: '#fff' }}
+      />
+      <div className={styles['contentWrapper']}>
+        <span title={data.name}>{data.name}</span>
+      </div>
+      FooBar
+    </div>
+  );
+});
+
+export default EntityRFPill;
diff --git a/libs/shared/ui/pills/src/customFlowPills/entitypill/index.ts b/libs/shared/ui/pills/src/customFlowPills/entitypill/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8f909017f21464b2b42ba9cb57c7c4a40535601d
--- /dev/null
+++ b/libs/shared/ui/pills/src/customFlowPills/entitypill/index.ts
@@ -0,0 +1 @@
+export * from './entitypill';
diff --git a/libs/shared/ui/pills/src/customFlowPills/entitypill/test.stories.tsx b/libs/shared/ui/pills/src/customFlowPills/entitypill/test.stories.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..cff92b119bc74df2e4ba2b488f2e4705fe1b77e3
--- /dev/null
+++ b/libs/shared/ui/pills/src/customFlowPills/entitypill/test.stories.tsx
@@ -0,0 +1,19 @@
+// Button.stories.ts|tsx
+
+import React from 'react';
+
+import { ComponentStory, ComponentMeta } from '@storybook/react';
+import Button from '@mui/material/Button';
+
+export default {
+  /* 👇 The title prop is optional.
+   * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading
+   * to learn how to generate automatic titles
+   */
+  title: 'Shared Component Loading Works',
+  component: Button,
+} as ComponentMeta<typeof Button>;
+
+export const Primary: ComponentStory<typeof Button> = () => (
+  <Button>Component Loading works</Button>
+);
diff --git a/libs/shared/ui/pills/src/customFlowPills/relationpill/index.ts b/libs/shared/ui/pills/src/customFlowPills/relationpill/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..deef2e611ad7700adac469c821310a527f7a3989
--- /dev/null
+++ b/libs/shared/ui/pills/src/customFlowPills/relationpill/index.ts
@@ -0,0 +1 @@
+export * from './relationpill';
diff --git a/libs/shared/ui/pills/src/customFlowPills/relationpill/relationpill.module.scss b/libs/shared/ui/pills/src/customFlowPills/relationpill/relationpill.module.scss
new file mode 100644
index 0000000000000000000000000000000000000000..aff84b2e5d8b9caba6dd9fdd34a99b54614946d4
--- /dev/null
+++ b/libs/shared/ui/pills/src/customFlowPills/relationpill/relationpill.module.scss
@@ -0,0 +1,110 @@
+.relation {
+  display: flex;
+  text-align: center;
+  font-family: monospace;
+  font-weight: bold;
+  font-size: 10px;
+  background-color: transparent;
+}
+
+.highlighted {
+  box-shadow: black 0 0 2px;
+}
+
+.contentWrapper {
+  display: flex;
+  align-items: center;
+
+  .handleLeft {
+    position: relative;
+    z-index: 3;
+
+    top: 25%;
+    border: 0px;
+    border-radius: 0px;
+
+    background: transparent;
+    transform-origin: center;
+
+    width: 0;
+    height: 0;
+    border-top: 5px solid transparent;
+    border-bottom: 5px solid transparent;
+    border-right: rgba(255, 255, 255, 0.7) 6px solid;
+
+    &::after {
+      content: '';
+      display: block;
+      position: absolute;
+      width: 0;
+      height: 0;
+      border-top: 7px solid transparent;
+      border-bottom: 7px solid transparent;
+      border-right: rgba(0, 0, 0, 0.1) 8px solid;
+      top: -7px;
+      right: -7px;
+    }
+  }
+  .highlighted {
+    z-index: -1;
+    box-shadow: 0 0 2px 1px gray;
+  }
+
+  .content {
+    margin: 0 2ch;
+    padding: 3px 0;
+    max-width: 20ch;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+  }
+
+  .handleRight {
+    position: relative;
+    top: 25%;
+    border: 0px;
+    border-radius: 0px;
+
+    background: transparent;
+    transform-origin: center;
+
+    width: 0;
+    height: 0;
+    border-top: 5px solid transparent;
+    border-bottom: 5px solid transparent;
+    border-left: rgba(255, 255, 255, 0.7) 6px solid;
+
+    &::after {
+      content: '';
+      display: block;
+      position: absolute;
+      width: 0;
+      height: 0;
+      border-top: 7px solid transparent;
+      border-bottom: 7px solid transparent;
+      border-left: rgba(0, 0, 0, 0.1) 8px solid;
+      top: -7px;
+      left: -7px;
+    }
+  }
+}
+
+$height: 10px;
+.arrowLeft {
+  z-index: 2;
+  width: 0;
+  height: 0;
+  border-top: $height solid transparent;
+  border-bottom: $height solid transparent;
+
+  border-right: $height solid;
+}
+
+.arrowRight {
+  width: 0;
+  height: 0;
+  border-top: $height solid transparent;
+  border-bottom: $height solid transparent;
+
+  border-left: $height solid;
+}
diff --git a/libs/shared/ui/pills/src/customFlowPills/relationpill/relationpill.tsx b/libs/shared/ui/pills/src/customFlowPills/relationpill/relationpill.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..8861d5e0e4079d4605075dc58a3a7acf490dbf27
--- /dev/null
+++ b/libs/shared/ui/pills/src/customFlowPills/relationpill/relationpill.tsx
@@ -0,0 +1,98 @@
+import { handles } from '@graphpolaris/querybuilder/usecases';
+import { useTheme } from '@mui/material';
+import { Handle, Position } from 'react-flow-renderer';
+import cn from 'classnames';
+
+import styles from './relationpill.module.scss';
+import React from 'react';
+
+/**
+ * Component to render a relation flow element
+ * @param { FlowElement<RelationData>} param0 The data of a relation flow element.
+ */
+export const RelationRFPill = React.memo(({ data }: { data: any }) => {
+  // export default function RelationRFPill({ data }: { data: any }) {
+  const theme = useTheme();
+
+  // const minRef = useRef<HTMLInputElement>(null);
+  // const maxRef = useRef<HTMLInputElement>(null);
+
+  // const [readOnlyMin, setReadOnlyMin] = useState(true);
+  // const [readOnlyMax, setReadOnlyMax] = useState(true);
+
+  // const onDepthChanged = (depth: string) => {
+  //   // Don't allow depth above 99
+  //   const limit = 99;
+  //   if (data != undefined) {
+  //     data.depth.min = data.depth.min >= limit ? limit : data.depth.min;
+  //     data.depth.max = data.depth.max >= limit ? limit : data.depth.max;
+
+  //     // Check for for valid depth: min <= max
+  //     if (depth == 'min') {
+  //       if (data.depth.min > data.depth.max) data.depth.max = data.depth.min;
+  //       setReadOnlyMin(true);
+  //     } else if (depth == 'max') {
+  //       if (data.depth.max < data.depth.min) data.depth.min = data.depth.max;
+  //       setReadOnlyMax(true);
+  //     }
+
+  //     // Set to the correct width
+  //     if (maxRef.current)
+  //       maxRef.current.style.maxWidth = calcWidth(data.depth.max);
+  //     if (minRef.current)
+  //       minRef.current.style.maxWidth = calcWidth(data.depth.min);
+  //   }
+  // };
+
+  // const calcWidth = (data: number) => {
+  //   return data.toString().length + 0.5 + 'ch';
+  // };
+
+  return (
+    <div className={styles['relation']}>
+      <div
+        className={styles['arrowLeft']}
+        style={{
+          borderRightColor: theme.palette.queryBuilder.relation.background,
+        }}
+      />
+      <div
+        className={cn(styles['contentWrapper'], {
+          [styles['highlighted']]: data.suggestedForConnection,
+        })}
+        style={{
+          color: theme.palette.queryBuilder.text,
+          background: theme.palette.queryBuilder.relation.background,
+        }}
+      >
+        <Handle
+          id={handles.relation.fromEntity}
+          type="target"
+          position={Position.Left}
+          className={styles['handleLeft']}
+          style={
+            data?.isFromEntityConnected ? { borderRightColor: '#2e2e2e' } : {} // TODO: this should be color from theme
+          }
+        />
+        <span className={styles['content']} title={data.name}>
+          {data.name}
+        </span>
+        <Handle
+          id={handles.relation.toEntity}
+          type="source"
+          position={Position.Right}
+          className={styles['handleRight']}
+          style={
+            data?.isToEntityConnected ? { borderLeftColor: '#2e2e2e' } : {}
+          }
+        />
+      </div>
+      <div
+        className={styles['arrowRight']}
+        style={{
+          borderLeftColor: theme.palette.queryBuilder.relation.background,
+        }}
+      />
+    </div>
+  );
+});
diff --git a/libs/shared/ui/pills/src/index.ts b/libs/shared/ui/pills/src/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6ac42e28dc43ff2a7181b86bdc5488baf8631e8e
--- /dev/null
+++ b/libs/shared/ui/pills/src/index.ts
@@ -0,0 +1,9 @@
+export * from './lib/shared-ui-pills';
+
+export * from './customFlowLines/connection';
+export * from './customFlowLines/connectionDrag';
+
+export * from './customFlowPills/entitypill/entitypill';
+export * from './customFlowPills/attributepill/attributepill';
+export * from './customFlowPills/attributepill/operatorselect';
+export * from './customFlowPills/relationpill/relationpill';
diff --git a/libs/shared/ui/pills/src/lib/shared-ui-pills.module.scss b/libs/shared/ui/pills/src/lib/shared-ui-pills.module.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/libs/shared/ui/pills/src/lib/shared-ui-pills.spec.tsx b/libs/shared/ui/pills/src/lib/shared-ui-pills.spec.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..d9561ecbdb4508a43221e49c5ae140ead5e8fe85
--- /dev/null
+++ b/libs/shared/ui/pills/src/lib/shared-ui-pills.spec.tsx
@@ -0,0 +1,10 @@
+import { render } from '@testing-library/react';
+
+import SharedUiPills from './shared-ui-pills';
+
+describe('SharedUiPills', () => {
+  it('should render successfully', () => {
+    const { baseElement } = render(<SharedUiPills />);
+    expect(baseElement).toBeTruthy();
+  });
+});
diff --git a/libs/shared/ui/pills/src/lib/shared-ui-pills.tsx b/libs/shared/ui/pills/src/lib/shared-ui-pills.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6a804d611fbd303b2d569e6c44d76d7810116ca8
--- /dev/null
+++ b/libs/shared/ui/pills/src/lib/shared-ui-pills.tsx
@@ -0,0 +1,14 @@
+import './shared-ui-pills.module.scss';
+
+/* eslint-disable-next-line */
+export interface SharedUiPillsProps {}
+
+export function SharedUiPills(props: SharedUiPillsProps) {
+  return (
+    <div>
+      <h1>Welcome to SharedUiPills!</h1>
+    </div>
+  );
+}
+
+export default SharedUiPills;
diff --git a/libs/shared/ui/pills/src/schema/entitypill.component.tsx b/libs/shared/ui/pills/src/schema/entitypill.component.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..049328d0084cc0d1401870c7a5ce5d3066151dc0
--- /dev/null
+++ b/libs/shared/ui/pills/src/schema/entitypill.component.tsx
@@ -0,0 +1,62 @@
+import { useTheme } from '@mui/material';
+import React, { useEffect } from 'react';
+import { FlowElement, Handle, Position } from 'react-flow-renderer';
+import cn from 'classnames';
+import styles from './entitypill.module.scss';
+
+// export const Handless = {
+//   entity: {
+//     attributes: 'attributesHandle',
+//     relations: 'relationsHandle',
+//   },
+// };
+
+// /** Links need handles to what they are connected to (and which side) */
+// export enum Handles {
+//   RelationLeft = 'leftEntityHandle', //target
+//   RelationRight = 'rightEntityHandle', //target
+//   ToAttributeHandle = 'attributesHandle', //target
+//   ToRelation = 'relationsHandle', //source
+//   Attribute = 'AttributeHandle', //source
+//   ReceiveFunction = 'receiveFunctionHandle', //target
+//   FunctionBase = 'functionHandle_', // + name from FunctionTypes args //source
+// }
+
+/**
+ * Component to render an entity flow element
+ * @param {FlowElement<EntityData>)} param0 The data of an entity flow element.
+ */
+export const EntityRFPill = React.memo(({ data }: { data: any }) => {
+  const theme = useTheme();
+
+  return (
+    <div
+      className={cn(styles['entity'], {
+        [styles['highlighted']]: data.suggestedForConnection,
+      })}
+      style={{
+        background: theme.palette.queryBuilder.entity.background,
+        color: theme.palette.queryBuilder.text,
+      }}
+    >
+      <Handle
+        id={handles.entity.relation}
+        type="source"
+        position={Position.Bottom}
+        className={styles['handleLeft']}
+        style={data?.isConnected ? { backgroundColor: '#2e2e2e' } : {}}
+      />
+      {/* <Handle
+        id={Handles.ToAttributeHandle}
+        type="target"
+        position={Position.Bottom}
+        className={styles.handleBottom}
+      /> */}
+      <div className={styles['contentWrapper']}>
+        <span title={data.name}>{data.name}</span>
+      </div>
+    </div>
+  );
+});
+
+export default EntityRFPill;
diff --git a/libs/shared/ui/pills/src/schema/entitypill.module.scss b/libs/shared/ui/pills/src/schema/entitypill.module.scss
new file mode 100644
index 0000000000000000000000000000000000000000..755d2b41d564abe3f9e4eb41f56865ef7d2432f4
--- /dev/null
+++ b/libs/shared/ui/pills/src/schema/entitypill.module.scss
@@ -0,0 +1,49 @@
+.entity {
+  display: flex;
+  font-family: monospace;
+  font-weight: bold;
+  font-size: 10px;
+  padding: 4px 2ch;
+  border-radius: 3px;
+}
+
+.highlighted {
+  box-shadow: black 0 0 2px;
+}
+
+.handleLeft {
+  border: 0px;
+  border-radius: 0px;
+  left: 12px;
+  width: 7px;
+  height: 7px;
+  margin-bottom: 11px;
+  background: rgba(255, 255, 255, 0.6);
+  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3);
+  transform-origin: center;
+}
+
+// .handleBottom {
+//   border: 0px;
+//   border-radius: 0px;
+//   width: 7px;
+//   height: 7px;
+//   left: 27.5px;
+//   margin-bottom: 11px;
+//   background: rgba(255, 255, 255, 0.6);
+//   box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.3);
+//   transform: rotate(-45deg);
+//   transform-origin: center;
+// }
+
+.contentWrapper {
+  margin-left: 3ch;
+
+  span {
+    max-width: 20ch;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+    display: block;
+  }
+}
diff --git a/libs/shared/ui/pills/src/schema/index.ts b/libs/shared/ui/pills/src/schema/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/libs/shared/ui/pills/tsconfig.json b/libs/shared/ui/pills/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..3512bf7afeea1bf2e5fdb72b7dea8114f6c9fbe1
--- /dev/null
+++ b/libs/shared/ui/pills/tsconfig.json
@@ -0,0 +1,25 @@
+{
+  "extends": "../../../../tsconfig.base.json",
+  "compilerOptions": {
+    "jsx": "react-jsx",
+    "allowJs": true,
+    "esModuleInterop": true,
+    "allowSyntheticDefaultImports": true,
+    "forceConsistentCasingInFileNames": true,
+    "strict": true,
+    "noImplicitOverride": true,
+    "noPropertyAccessFromIndexSignature": true,
+    "noImplicitReturns": true,
+    "noFallthroughCasesInSwitch": true
+  },
+  "files": [],
+  "include": [],
+  "references": [
+    {
+      "path": "./tsconfig.lib.json"
+    },
+    {
+      "path": "./tsconfig.spec.json"
+    }
+  ]
+}
diff --git a/libs/shared/ui/pills/tsconfig.lib.json b/libs/shared/ui/pills/tsconfig.lib.json
new file mode 100644
index 0000000000000000000000000000000000000000..1ab8e06a7111b168e84debca80bb4ac82f72f2aa
--- /dev/null
+++ b/libs/shared/ui/pills/tsconfig.lib.json
@@ -0,0 +1,22 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../../../../dist/out-tsc",
+    "types": ["node"]
+  },
+  "files": [
+    "../../../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
+    "../../../../node_modules/@nrwl/react/typings/image.d.ts"
+  ],
+  "exclude": [
+    "**/*.spec.ts",
+    "**/*.test.ts",
+    "**/*.spec.tsx",
+    "**/*.test.tsx",
+    "**/*.spec.js",
+    "**/*.test.js",
+    "**/*.spec.jsx",
+    "**/*.test.jsx"
+  ],
+  "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
+}
diff --git a/libs/shared/ui/pills/tsconfig.spec.json b/libs/shared/ui/pills/tsconfig.spec.json
new file mode 100644
index 0000000000000000000000000000000000000000..315a5b0bbebaca96617a8dd5353901287ebd8e68
--- /dev/null
+++ b/libs/shared/ui/pills/tsconfig.spec.json
@@ -0,0 +1,19 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../../../../dist/out-tsc",
+    "module": "commonjs",
+    "types": ["jest", "node"]
+  },
+  "include": [
+    "**/*.test.ts",
+    "**/*.spec.ts",
+    "**/*.test.tsx",
+    "**/*.spec.tsx",
+    "**/*.test.js",
+    "**/*.spec.js",
+    "**/*.test.jsx",
+    "**/*.spec.jsx",
+    "**/*.d.ts"
+  ]
+}
diff --git a/nginx/nginx.conf b/nginx/nginx.conf
new file mode 100644
index 0000000000000000000000000000000000000000..ef2e21a890fc402b46472b5c18fd5b95c3a53a2e
--- /dev/null
+++ b/nginx/nginx.conf
@@ -0,0 +1,16 @@
+server {
+  listen 80;
+
+  location / {
+    root   /usr/share/nginx/html;
+    index  index.html index.htm;
+    try_files $uri $uri/ /index.html;
+  }
+
+  error_page 500 502 503 504 /50x.html;
+
+  location = /50x.html {
+      root /usr/share/nginx/html;
+  }
+
+}
diff --git a/package.json b/package.json
index 9d07a7788766fc39c5af14ad5142bf853f2776fe..8f417b0ea80e3803a8226572f4d7f5a90a2eeefa 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,8 @@
   "license": "MIT",
   "scripts": {
     "start": "nx serve",
+    "storybook": "nx run web-graphpolaris:storybook",
+    "dev": "nx run web-graphpolaris:dev",
     "build": "nx build",
     "test": "nx test",
     "test:all": "nx affected:test --all --codeCoverage --skip-nx-cache",
diff --git a/tsconfig.base.json b/tsconfig.base.json
index a5bafb69c61a39d4c988430267936172aeb36df7..1d989caf1546265d9884792e5fb894129aa5af64 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -17,11 +17,11 @@
     "paths": {
       "@graphpolaris/graph-layout": ["libs/shared/graph-layout/src/index.ts"],
       "@graphpolaris/models": ["libs/shared/models/src/index.ts"],
-      "@graphpolaris/schema-utils": ["libs/shared/schema-utils/src/index.ts"],
-      "@graphpolaris/schema/usecases": ["libs/schema/usecases/src/index.ts"],
       "@graphpolaris/querybuilder/usecases": [
         "libs/querybuilder/usecases/src/index.ts"
       ],
+      "@graphpolaris/schema-utils": ["libs/shared/schema-utils/src/index.ts"],
+      "@graphpolaris/schema/usecases": ["libs/schema/usecases/src/index.ts"],
       "@graphpolaris/shared/data-access/api": [
         "libs/shared/data-access/api/src/index.ts"
       ],
@@ -35,6 +35,7 @@
         "libs/shared/data-access/theme/src/index.ts"
       ],
       "@graphpolaris/shared/mock-data": ["libs/shared/mock-data/src/index.ts"],
+      "@graphpolaris/shared/ui/pills": ["libs/shared/ui/pills/src/index.ts"],
       "@mui/styled-engine": ["./node_modules/@mui/styled-engine-sc"]
     }
   },
diff --git a/workspace.json b/workspace.json
index d829d3ca0515a309a5a268383dcf3764953f2251..d5bbe8a52437f534858ca07379e372480fbeed95 100644
--- a/workspace.json
+++ b/workspace.json
@@ -11,6 +11,7 @@
     "shared-mock-data": "libs/shared/mock-data",
     "shared-models": "libs/shared/models",
     "shared-schema-utils": "libs/shared/schema-utils",
+    "shared-ui-pills": "libs/shared/ui/pills",
     "web-graphpolaris": "apps/web-graphpolaris",
     "web-graphpolaris-e2e": "apps/web-graphpolaris-e2e"
   }