From 526a50774575130d194f3ff95851a000131b033b Mon Sep 17 00:00:00 2001
From: "Vink, S.A. (Sjoerd)" <s.a.vink@uu.nl>
Date: Mon, 4 Mar 2024 20:45:10 +0000
Subject: [PATCH] feat(vis_settings): refactor for new viz settings panel api

---
 .gitignore                                    |   1 +
 apps/web/src/app/app.tsx                      |   9 +-
 .../src/app/panels/VisualizationDialog.tsx    | 106 ------
 .../dbConnectionSelector.tsx                  |   8 +-
 .../DatabaseManagement/forms/databaseForm.tsx |   2 +-
 .../DatabaseManagement/forms/settings.tsx     |  45 +--
 .../lib/components/charts/barplot/index.tsx   |   4 +-
 .../colorLegendDropdown.stories.tsx           |  22 ++
 .../colorComponents/colorDropdown/index.tsx   | 100 +++++
 .../colorLegend/colorLegendSeqDiv.stories.tsx |  34 ++
 .../colorComponents/colorLegend/index.tsx     |  93 +++++
 libs/shared/lib/components/forms/index.tsx    |   6 +-
 libs/shared/lib/components/icon/index.tsx     |   2 +-
 libs/shared/lib/components/info/index.tsx     |  16 +
 .../lib/components/info/info.stories.tsx      |  27 ++
 libs/shared/lib/components/inputs/index.tsx   |   6 +-
 libs/shared/lib/components/tooltip/index.tsx  |  48 ++-
 .../components/tooltip/tooltip.stories.tsx    |  60 ++-
 libs/shared/lib/data-access/api/eventBus.tsx  |  34 +-
 .../store/graphQueryResultSlice.ts            |  12 +-
 libs/shared/lib/data-access/store/hooks.ts    |  16 +-
 .../lib/data-access/store/schemaSlice.ts      |   6 +-
 .../data-access/store/visualizationSlice.ts   |  92 +++--
 .../utils/result-statistics/compress-graph.ts |  32 ++
 .../utils/result-statistics/dimensions.ts     |  39 ++
 .../result-statistics/extract-dimensions.ts   |  17 +
 .../utils/result-statistics/extract-stats.ts  |  55 +++
 .../result-statistics/graph-statistics.ts     |  26 ++
 .../utils/result-statistics/index.ts          |   2 +
 .../result-statistics.types.ts                |  52 +++
 .../querybuilder/model/graphology/model.ts    |   3 +-
 .../querybuilder/model/graphology/utils.ts    |   4 +-
 .../lib/querybuilder/model/logic/general.ts   |   1 +
 .../customFlowPills/entitypill/entitypill.tsx |   4 +-
 .../pills/pilldropdown/pilldropdown.tsx       |  39 +-
 libs/shared/lib/schema/model/FromBackend.ts   |   8 +
 .../lib/schema/schema-utils/schema-utils.ts   |  32 +-
 .../vis/configuration/advanced/advanced.tsx   |  32 ++
 .../lib/vis/configuration/advanced/index.ts   |   1 +
 .../vis/configuration/encodings/accessor.tsx  |  62 ++++
 .../vis/configuration/encodings/encoding.tsx  |  86 +++++
 .../encodings/encodings.types.ts              |  31 ++
 .../lib/vis/configuration/encodings/index.ts  |   2 +
 .../vis/configuration/encodings/selector.tsx  |  29 ++
 .../encodings/selectors/axis.stories.tsx      |  21 ++
 .../encodings/selectors/axis.tsx              |  11 +
 .../encodings/selectors/color.stories.tsx     |  21 ++
 .../encodings/selectors/color.tsx             |  16 +
 .../encodings/selectors/index.ts              |  15 +
 .../encodings/selectors/opacity.stories.tsx   |  21 ++
 .../encodings/selectors/opacity.tsx           |  15 +
 .../encodings/selectors/shape.stories.tsx     |  21 ++
 .../encodings/selectors/shape.tsx             |   6 +
 .../encodings/selectors/size.stories.tsx      |  22 ++
 .../encodings/selectors/size.tsx              |  15 +
 libs/shared/lib/vis/configuration/index.ts    |   1 +
 .../vis/configuration/interactions/index.ts   |   2 +
 .../interactions/interaction.tsx              |  24 ++
 .../interactions/interaction.types.ts         |   7 +
 .../vis/configuration/panel/panel-header.tsx  |  38 ++
 .../lib/vis/configuration/panel/panel.tsx     |  37 ++
 .../lib/vis/configuration/panel/tab-item.tsx  |  20 +
 .../lib/vis/configuration/settings/index.ts   |   2 +
 .../vis/configuration/settings/settings.tsx   |  84 +++++
 .../configuration/settings/settings.types.ts  |  10 +
 libs/shared/lib/vis/documentation.mdx         | 152 --------
 libs/shared/lib/vis/index.ts                  |   1 +
 libs/shared/lib/vis/index.tsx                 |  92 -----
 libs/shared/lib/vis/panel/dialog.tsx          | 103 ------
 libs/shared/lib/vis/panel/index.ts            |   4 -
 libs/shared/lib/vis/panel/visualization.tsx   |  77 ----
 .../shared/ResultNodeLinkParserUseCase.tsx    |   2 +-
 libs/shared/lib/vis/types.ts                  |  48 +--
 libs/shared/lib/vis/visualizationManager.tsx  | 108 ++++++
 .../shared/lib/vis/visualizationPanel.tsx     |  63 +++-
 .../lib/vis/visualizations/documentation.mdx  |  89 +++++
 libs/shared/lib/vis/visualizations/index.tsx  |   6 -
 .../mapvis/components/MapPanel.tsx            |   4 +-
 .../visualizations/mapvis/mapvis.stories.tsx  |   8 +-
 .../lib/vis/visualizations/mapvis/mapvis.tsx  |  18 +-
 .../vis/visualizations/matrix/matrixvis.tsx   |  66 ----
 .../{matrix => matrixvis}/Types.tsx           |   0
 .../components/BitmapTextLabel.ts             |   0
 .../components/ColumnGraphicsComponent.tsx    |  22 --
 .../components/ColumnLabelTrack.tsx           |   0
 .../components/ColumnSpriteComponent.tsx      |  12 -
 .../components/MatrixExport.tsx               |   0
 .../components/MatrixPixi.tsx                 | 347 ++++++------------
 .../components/MatrixPopup.tsx                |   0
 .../components/ReorderingManager.tsx          |  59 +--
 .../components/TextLabel.ts                   |   0
 .../{matrix => matrixvis}/index.ts            |   0
 .../{matrix => matrixvis}/matrix.stories.tsx  |  57 +--
 .../matrixvis.module.scss                     |   0
 .../visualizations/matrixvis/matrixvis.tsx    |  44 +++
 .../lib/vis/visualizations/nodelink/index.ts  |  12 -
 .../nodelink/nodelinkvis.module.scss.d.ts     |  10 -
 .../components/NLExport.tsx                   |   0
 .../components/NLForce.tsx                    |   0
 .../components/NLMachineLearning.tsx          |   0
 .../components/NLPixi.tsx                     |   0
 .../components/NLPopup.tsx                    |   0
 .../components/query2NL.tsx                   |   0
 .../components/utils.tsx                      |   0
 .../nodelinkvis.module.scss                   |   0
 .../nodelinkvis.stories.tsx                   |  18 +-
 .../{nodelink => nodelinkvis}/nodelinkvis.tsx |  90 ++---
 .../{nodelink => nodelinkvis}/types.ts        |   0
 .../paohvis/paohvis.stories.tsx               |  12 +-
 .../vis/visualizations/paohvis/paohvis.tsx    |   6 +-
 .../rawjsonvis/rawjsonvis.stories.tsx         |  14 +-
 .../visualizations/rawjsonvis/rawjsonvis.tsx  |   9 +-
 .../semanticsubstrates.stories.tsx            |   8 +-
 .../components/table.module.scss.d.ts         |   5 -
 .../lib/vis/visualizations/table_vis/index.ts |   1 -
 .../components/Table.tsx                      |   4 +-
 .../components/table.module.scss              |   0
 .../lib/vis/visualizations/tablevis/index.ts  |   0
 .../tablevis.stories.tsx                      |  10 +-
 .../tableVis.tsx => tablevis/tablevis.tsx}    |  48 ++-
 package.json                                  |   2 +-
 121 files changed, 2041 insertions(+), 1305 deletions(-)
 delete mode 100644 apps/web/src/app/panels/VisualizationDialog.tsx
 create mode 100644 libs/shared/lib/components/colorComponents/colorDropdown/colorLegendDropdown.stories.tsx
 create mode 100644 libs/shared/lib/components/colorComponents/colorDropdown/index.tsx
 create mode 100644 libs/shared/lib/components/colorComponents/colorLegend/colorLegendSeqDiv.stories.tsx
 create mode 100644 libs/shared/lib/components/colorComponents/colorLegend/index.tsx
 create mode 100644 libs/shared/lib/components/info/index.tsx
 create mode 100644 libs/shared/lib/components/info/info.stories.tsx
 create mode 100644 libs/shared/lib/data-access/utils/result-statistics/compress-graph.ts
 create mode 100644 libs/shared/lib/data-access/utils/result-statistics/dimensions.ts
 create mode 100644 libs/shared/lib/data-access/utils/result-statistics/extract-dimensions.ts
 create mode 100644 libs/shared/lib/data-access/utils/result-statistics/extract-stats.ts
 create mode 100644 libs/shared/lib/data-access/utils/result-statistics/graph-statistics.ts
 create mode 100644 libs/shared/lib/data-access/utils/result-statistics/index.ts
 create mode 100644 libs/shared/lib/data-access/utils/result-statistics/result-statistics.types.ts
 create mode 100644 libs/shared/lib/vis/configuration/advanced/advanced.tsx
 create mode 100644 libs/shared/lib/vis/configuration/advanced/index.ts
 create mode 100644 libs/shared/lib/vis/configuration/encodings/accessor.tsx
 create mode 100644 libs/shared/lib/vis/configuration/encodings/encoding.tsx
 create mode 100644 libs/shared/lib/vis/configuration/encodings/encodings.types.ts
 create mode 100644 libs/shared/lib/vis/configuration/encodings/index.ts
 create mode 100644 libs/shared/lib/vis/configuration/encodings/selector.tsx
 create mode 100644 libs/shared/lib/vis/configuration/encodings/selectors/axis.stories.tsx
 create mode 100644 libs/shared/lib/vis/configuration/encodings/selectors/axis.tsx
 create mode 100644 libs/shared/lib/vis/configuration/encodings/selectors/color.stories.tsx
 create mode 100644 libs/shared/lib/vis/configuration/encodings/selectors/color.tsx
 create mode 100644 libs/shared/lib/vis/configuration/encodings/selectors/index.ts
 create mode 100644 libs/shared/lib/vis/configuration/encodings/selectors/opacity.stories.tsx
 create mode 100644 libs/shared/lib/vis/configuration/encodings/selectors/opacity.tsx
 create mode 100644 libs/shared/lib/vis/configuration/encodings/selectors/shape.stories.tsx
 create mode 100644 libs/shared/lib/vis/configuration/encodings/selectors/shape.tsx
 create mode 100644 libs/shared/lib/vis/configuration/encodings/selectors/size.stories.tsx
 create mode 100644 libs/shared/lib/vis/configuration/encodings/selectors/size.tsx
 create mode 100644 libs/shared/lib/vis/configuration/index.ts
 create mode 100644 libs/shared/lib/vis/configuration/interactions/index.ts
 create mode 100644 libs/shared/lib/vis/configuration/interactions/interaction.tsx
 create mode 100644 libs/shared/lib/vis/configuration/interactions/interaction.types.ts
 create mode 100644 libs/shared/lib/vis/configuration/panel/panel-header.tsx
 create mode 100644 libs/shared/lib/vis/configuration/panel/panel.tsx
 create mode 100644 libs/shared/lib/vis/configuration/panel/tab-item.tsx
 create mode 100644 libs/shared/lib/vis/configuration/settings/index.ts
 create mode 100644 libs/shared/lib/vis/configuration/settings/settings.tsx
 create mode 100644 libs/shared/lib/vis/configuration/settings/settings.types.ts
 delete mode 100644 libs/shared/lib/vis/documentation.mdx
 create mode 100644 libs/shared/lib/vis/index.ts
 delete mode 100644 libs/shared/lib/vis/index.tsx
 delete mode 100644 libs/shared/lib/vis/panel/dialog.tsx
 delete mode 100644 libs/shared/lib/vis/panel/index.ts
 delete mode 100644 libs/shared/lib/vis/panel/visualization.tsx
 create mode 100644 libs/shared/lib/vis/visualizationManager.tsx
 rename apps/web/src/app/panels/Visualization.tsx => libs/shared/lib/vis/visualizationPanel.tsx (60%)
 create mode 100644 libs/shared/lib/vis/visualizations/documentation.mdx
 delete mode 100644 libs/shared/lib/vis/visualizations/matrix/matrixvis.tsx
 rename libs/shared/lib/vis/visualizations/{matrix => matrixvis}/Types.tsx (100%)
 rename libs/shared/lib/vis/visualizations/{matrix => matrixvis}/components/BitmapTextLabel.ts (100%)
 rename libs/shared/lib/vis/visualizations/{matrix => matrixvis}/components/ColumnGraphicsComponent.tsx (77%)
 rename libs/shared/lib/vis/visualizations/{matrix => matrixvis}/components/ColumnLabelTrack.tsx (100%)
 rename libs/shared/lib/vis/visualizations/{matrix => matrixvis}/components/ColumnSpriteComponent.tsx (70%)
 rename libs/shared/lib/vis/visualizations/{matrix => matrixvis}/components/MatrixExport.tsx (100%)
 rename libs/shared/lib/vis/visualizations/{matrix => matrixvis}/components/MatrixPixi.tsx (64%)
 rename libs/shared/lib/vis/visualizations/{matrix => matrixvis}/components/MatrixPopup.tsx (100%)
 rename libs/shared/lib/vis/visualizations/{matrix => matrixvis}/components/ReorderingManager.tsx (76%)
 rename libs/shared/lib/vis/visualizations/{matrix => matrixvis}/components/TextLabel.ts (100%)
 rename libs/shared/lib/vis/visualizations/{matrix => matrixvis}/index.ts (100%)
 rename libs/shared/lib/vis/visualizations/{matrix => matrixvis}/matrix.stories.tsx (61%)
 rename libs/shared/lib/vis/visualizations/{matrix => matrixvis}/matrixvis.module.scss (100%)
 create mode 100644 libs/shared/lib/vis/visualizations/matrixvis/matrixvis.tsx
 delete mode 100644 libs/shared/lib/vis/visualizations/nodelink/index.ts
 delete mode 100644 libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.module.scss.d.ts
 rename libs/shared/lib/vis/visualizations/{nodelink => nodelinkvis}/components/NLExport.tsx (100%)
 rename libs/shared/lib/vis/visualizations/{nodelink => nodelinkvis}/components/NLForce.tsx (100%)
 rename libs/shared/lib/vis/visualizations/{nodelink => nodelinkvis}/components/NLMachineLearning.tsx (100%)
 rename libs/shared/lib/vis/visualizations/{nodelink => nodelinkvis}/components/NLPixi.tsx (100%)
 rename libs/shared/lib/vis/visualizations/{nodelink => nodelinkvis}/components/NLPopup.tsx (100%)
 rename libs/shared/lib/vis/visualizations/{nodelink => nodelinkvis}/components/query2NL.tsx (100%)
 rename libs/shared/lib/vis/visualizations/{nodelink => nodelinkvis}/components/utils.tsx (100%)
 rename libs/shared/lib/vis/visualizations/{nodelink => nodelinkvis}/nodelinkvis.module.scss (100%)
 rename libs/shared/lib/vis/visualizations/{nodelink => nodelinkvis}/nodelinkvis.stories.tsx (84%)
 rename libs/shared/lib/vis/visualizations/{nodelink => nodelinkvis}/nodelinkvis.tsx (65%)
 rename libs/shared/lib/vis/visualizations/{nodelink => nodelinkvis}/types.ts (100%)
 delete mode 100644 libs/shared/lib/vis/visualizations/table_vis/components/table.module.scss.d.ts
 delete mode 100644 libs/shared/lib/vis/visualizations/table_vis/index.ts
 rename libs/shared/lib/vis/visualizations/{table_vis => tablevis}/components/Table.tsx (99%)
 rename libs/shared/lib/vis/visualizations/{table_vis => tablevis}/components/table.module.scss (100%)
 create mode 100644 libs/shared/lib/vis/visualizations/tablevis/index.ts
 rename libs/shared/lib/vis/visualizations/{table_vis => tablevis}/tablevis.stories.tsx (86%)
 rename libs/shared/lib/vis/visualizations/{table_vis/tableVis.tsx => tablevis/tablevis.tsx} (53%)

diff --git a/.gitignore b/.gitignore
index 6411dbf0d..7749c1bd2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -80,6 +80,7 @@ node_modules
 tsconfig.tsbuildinfo
 vite.config.ts.*
 
+
 # Generated ts definitions files for CSS modules
 *.d.ts
 *.module.scss.d.ts
diff --git a/apps/web/src/app/app.tsx b/apps/web/src/app/app.tsx
index ea709980a..90d3c2259 100644
--- a/apps/web/src/app/app.tsx
+++ b/apps/web/src/app/app.tsx
@@ -11,9 +11,12 @@ import Onboarding from '../components/onboarding/onboarding';
 import { wsQueryRequest } from '@graphpolaris/shared/lib/data-access/broker';
 import { URLParams, setParam } from '@graphpolaris/shared/lib/data-access/api/url';
 
-const Schema = React.lazy(() => import('@graphpolaris/shared/lib/schema/panel'));
-const VisualizationPanel = React.lazy(() => import('@graphpolaris/shared/lib/vis/panel'));
-const QueryBuilder = React.lazy(() => import('@graphpolaris/shared/lib/querybuilder'));
+import { Schema } from '@graphpolaris/shared/lib/schema/panel';
+import { VisualizationPanel } from '@graphpolaris/shared/lib/vis';
+import { QueryBuilder } from '@graphpolaris/shared/lib/querybuilder';
+// const Schema = React.lazy(() => import('@graphpolaris/shared/lib/schema/panel'));
+// const VisualizationPanel = React.lazy(() => import('@graphpolaris/shared/lib/vis'));
+// const QueryBuilder = React.lazy(() => import('@graphpolaris/shared/lib/querybuilder'));
 
 export type App = {
   load?: string;
diff --git a/apps/web/src/app/panels/VisualizationDialog.tsx b/apps/web/src/app/panels/VisualizationDialog.tsx
deleted file mode 100644
index 805d201b1..000000000
--- a/apps/web/src/app/panels/VisualizationDialog.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import { FormActions, FormBody, FormCard, FormControl, FormDiv, FormHBar, FormTitle } from '@graphpolaris/shared/lib/components/forms';
-import { DialogProps } from '@graphpolaris/shared/lib/components/Dialog';
-import { useAppDispatch, useVisualizationState } from '@graphpolaris/shared/lib/data-access';
-import { updateGeneralSettings, updateVisualizationSettings } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
-import Input from '@graphpolaris/shared/lib/components/inputs';
-import { globalConfigSchemaTypes, localConfigSchemaType } from '@graphpolaris/shared/lib/vis/types';
-
-export default function VisualizationDialog(props: DialogProps) {
-  const dispatch = useAppDispatch();
-  const vis = useVisualizationState();
-  const [settingsGeneral, setSettingsGeneral] = useState<globalConfigSchemaTypes>(vis.settings.general);
-  const [settingsSpecific, setSettingsSpecific] = useState<localConfigSchemaType>(vis.settings[vis.activeVisualization]);
-
-  useEffect(() => {
-    if (vis.settings.general !== settingsGeneral) {
-      dispatch(updateGeneralSettings(settingsGeneral));
-    }
-  }, [settingsGeneral]);
-
-  useEffect(() => {
-    if (vis.settings[vis.activeVisualization] !== settingsSpecific) {
-      dispatch(
-        updateVisualizationSettings({
-          id: vis.activeVisualization,
-          settings: settingsSpecific,
-        }),
-      );
-    }
-  }, [settingsSpecific]);
-
-  useEffect(() => {
-    setSettingsSpecific(vis.settings[vis.activeVisualization]);
-  }, [vis.settings[vis.activeVisualization]]);
-
-  return (
-    <>
-      {props.open && (
-        <FormDiv>
-          <FormCard>
-            <FormBody
-              onSubmit={(e) => {
-                e.preventDefault();
-                props.onClose();
-              }}
-            >
-              <FormTitle title="Settings" onClose={props.onClose} />
-              <FormHBar />
-
-              {settingsGeneral && (
-                <>
-                  <p className="font-sm font-bold pl-2">General</p>
-                  {Object.keys(settingsGeneral).map((val) => (
-                    <FormControl key={val}>
-                      <Input
-                        {...settingsGeneral[val]}
-                        value={vis.settings.general[val].value as any}
-                        onChange={(value: any) => {
-                          setSettingsGeneral((prevSettings) => ({
-                            ...prevSettings,
-                            [val]: { ...prevSettings[val], value: value },
-                          }));
-                        }}
-                      />
-                    </FormControl>
-                  ))}
-                </>
-              )}
-
-              {settingsSpecific && (
-                <>
-                  <FormHBar />
-                  <p className="font-sm font-bold pl-2">Specific</p>
-                  {Object.keys(settingsSpecific).map((val) => {
-                    const currentSetting = settingsSpecific[val];
-                    const shouldShowSetting = currentSetting.condition ? currentSetting.condition?.(settingsSpecific) : true;
-                    console.log(currentSetting, shouldShowSetting);
-                    return (
-                      shouldShowSetting && (
-                        <FormControl key={val}>
-                          <Input
-                            {...settingsSpecific[val]}
-                            value={settingsSpecific[val]?.value as any}
-                            onChange={(value: any) => {
-                              setSettingsSpecific((prevSettings) => ({
-                                ...prevSettings,
-                                [val]: { ...prevSettings[val], value: value },
-                              }));
-                            }}
-                          />
-                        </FormControl>
-                      )
-                    );
-                  })}
-                </>
-              )}
-
-              <FormHBar />
-              <FormActions onClose={props.onClose} />
-            </FormBody>
-          </FormCard>
-        </FormDiv>
-      )}
-    </>
-  );
-}
diff --git a/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx b/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx
index e15109c95..820d0d462 100644
--- a/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx
+++ b/apps/web/src/components/navbar/DatabaseManagement/dbConnectionSelector.tsx
@@ -19,7 +19,7 @@ export default function DatabaseSelector({}) {
   const [hovered, setHovered] = useState<string | null>(null);
   const [connecting, setConnecting] = useState<boolean>(false);
   const [dbSelectionMenuOpen, setDbSelectionMenuOpen] = useState<boolean>(false);
-  const [settingsMenuOpen, setSettingsMenuOpen] = useState<'create' | 'update' | undefined>(undefined);
+  const [settingsMenuOpen, setSettingsMenuOpen] = useState<'add' | 'update' | undefined>(undefined);
   const [selectedSaveState, setSelectedSaveState] = useState<SaveStateI | null>(null);
   // const [addDbConnectionFormOpen, setAddDbConnectionFormOpen] = useState<boolean>(false);
 
@@ -31,7 +31,7 @@ export default function DatabaseSelector({}) {
         settingsMenuOpen === undefined) ||
       session.currentSaveState === nilUUID
     ) {
-      setSettingsMenuOpen('create');
+      setSettingsMenuOpen('add');
     }
   }, [session, settingsMenuOpen]);
 
@@ -120,7 +120,7 @@ export default function DatabaseSelector({}) {
             </div>
           }
           onClick={() => {
-            if (session.saveStates && Object.keys(session.saveStates).length === 0) setSettingsMenuOpen('create');
+            if (session.saveStates && Object.keys(session.saveStates).length === 0) setSettingsMenuOpen('add');
             else setDbSelectionMenuOpen(!dbSelectionMenuOpen);
           }}
         />
@@ -133,7 +133,7 @@ export default function DatabaseSelector({}) {
                 e.preventDefault();
                 setDbSelectionMenuOpen(false);
                 setConnecting(false);
-                setSettingsMenuOpen('create');
+                setSettingsMenuOpen('add');
               }}
               title="Add new database"
             >
diff --git a/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx b/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx
index 31f59a59a..8a11c9ab9 100644
--- a/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx
+++ b/apps/web/src/components/navbar/DatabaseManagement/forms/databaseForm.tsx
@@ -50,7 +50,7 @@ export const DatabaseForm = (props: { data: SaveStateI; onChange: (data: SaveSta
     <>
       <Input
         type="text"
-        label="Name of database"
+        label="Name of database connection"
         value={formData.name}
         onChange={(value: string) =>
           setFormData((draft) => {
diff --git a/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx b/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx
index fe477e31a..eef51b9ed 100644
--- a/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx
+++ b/apps/web/src/components/navbar/DatabaseManagement/forms/settings.tsx
@@ -1,22 +1,16 @@
-import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
 import {
-  databaseNameMapping,
-  databaseProtocolMapping,
   useAppDispatch,
   SaveStateI,
   wsUpdateState,
-  DatabaseStatus,
   wsTestDatabaseConnection,
   wsCreateState,
-  nilUUID,
-  DatabaseType,
   useAuthorizationCache,
 } from '@graphpolaris/shared/lib/data-access';
 import { ErrorOutline } from '@mui/icons-material';
 import { Dialog } from '@graphpolaris/shared/lib/components/Dialog';
 import { Button } from '@graphpolaris/shared/lib/components/buttons';
 import { useImmer } from 'use-immer';
-import { Broker } from '@graphpolaris/shared/lib/data-access/broker/broker';
 import { addSaveState, testedSaveState } from '@graphpolaris/shared/lib/data-access/store/sessionSlice';
 import { DatabaseForm, INITIAL_SAVE_STATE } from './databaseForm';
 import { SampleDatabaseSelector } from './mockSaveStates';
@@ -27,12 +21,7 @@ type Connection = {
   verified: boolean | null;
 };
 
-export const SettingsForm = (props: {
-  onClose(): void;
-  open: 'create' | 'update';
-  saveState: SaveStateI | null;
-  disableCancel?: boolean;
-}) => {
+export const SettingsForm = (props: { onClose(): void; open: 'add' | 'update'; saveState: SaveStateI | null; disableCancel?: boolean }) => {
   const dispatch = useAppDispatch();
   const ref = useRef<HTMLDialogElement>(null);
   const auth = useAuthorizationCache();
@@ -46,7 +35,7 @@ export const SettingsForm = (props: {
     status: null,
     verified: null,
   });
-  const formTitle = props.open === 'create' ? 'Create' : 'Update';
+  const formTitle = props.open === 'add' ? 'Add' : 'Update';
 
   useEffect(() => {
     if (props.saveState && props.open === 'update') {
@@ -57,21 +46,22 @@ export const SettingsForm = (props: {
     }
   }, [props.saveState]);
 
-  async function handleSubmit() {
+  async function handleSubmit(saveStateData?: SaveStateI) {
+    if (!saveStateData) saveStateData = formData;
     setConnection(() => ({
       updating: true,
-      status: formTitle.slice(0, -1) + 'ing database credentials',
+      status: 'Testing database connection',
       verified: null,
     }));
 
-    wsTestDatabaseConnection(formData.db, (data) => {
-      if (!formData) {
+    wsTestDatabaseConnection(saveStateData.db, (data) => {
+      if (!saveStateData) {
         console.error('formData is null');
         return;
       }
-      if (formData.user_id !== auth.userID && auth.userID) {
-        console.error('formData.user_id is not equal to auth.userID');
-        formData.user_id = auth.userID;
+      if (saveStateData.user_id !== auth.userID && auth.userID) {
+        console.error('user_id is not equal to auth.userID');
+        saveStateData.user_id = auth.userID;
       }
       if (data && data.status === 'success') {
         setConnection((prevState) => ({
@@ -79,15 +69,15 @@ export const SettingsForm = (props: {
           status: 'Database connection verified',
           verified: true,
         }));
-        if (props.open === 'create') {
-          wsCreateState(formData, (_data) => {
+        if (props.open === 'add') {
+          wsCreateState(saveStateData, (_data) => {
             dispatch(addSaveState(_data));
             dispatch(testedSaveState(_data.id));
             closeDialog();
           });
         } else {
           dispatch(testedSaveState(data.saveStateID));
-          wsUpdateState(formData, (_data) => {
+          wsUpdateState(saveStateData, (_data) => {
             dispatch(addSaveState(_data));
             closeDialog();
           });
@@ -95,7 +85,7 @@ export const SettingsForm = (props: {
       } else {
         setConnection((prevState) => ({
           updating: false,
-          status: 'Database connection failed',
+          status: 'Database connection test failed',
           verified: false,
         }));
       }
@@ -124,7 +114,7 @@ export const SettingsForm = (props: {
   return (
     <Dialog open={!!props.open} onClose={props.onClose} className="lg:min-w-[50rem]">
       <div className="flex justify-between align-center">
-        <h1 className="text-xl font-bold">{formTitle} Database</h1>
+        <h1 className="text-xl font-bold">{formTitle} Database Connection</h1>
         <div>
           {sampleDataPanel === true ? (
             <Button variant="outline" label="Go back" onClick={() => setSampleDataPanel(false)} />
@@ -145,9 +135,8 @@ export const SettingsForm = (props: {
         {sampleDataPanel === true ? (
           <SampleDatabaseSelector
             onClick={(data) => {
-              setFormData({ ...data, user_id: auth.userID || '' });
               setHasError(false);
-              handleSubmit();
+              handleSubmit({ ...data, user_id: auth.userID || '' });
             }}
           />
         ) : (
diff --git a/libs/shared/lib/components/charts/barplot/index.tsx b/libs/shared/lib/components/charts/barplot/index.tsx
index 362918122..034b5f1f6 100644
--- a/libs/shared/lib/components/charts/barplot/index.tsx
+++ b/libs/shared/lib/components/charts/barplot/index.tsx
@@ -1,6 +1,6 @@
 import React, { LegacyRef, useEffect, useRef, useState } from 'react';
 import * as d3 from 'd3';
-import Tooltip, { TooltipProps } from '@graphpolaris/shared/lib/components/tooltip';
+import { BarplotTooltip } from '@graphpolaris/shared/lib/components/tooltip';
 
 export type BarPlotProps = {
   data: { category: string; count: number }[];
@@ -222,7 +222,7 @@ export const BarPlot = ({ typeBarPlot: typeBarplot, numBins, data }: BarPlotProp
         preserveAspectRatio="xMidYMid meet"
         viewBox={`0 0 ${dimensions.width} ${dimensions.height}`}
       ></svg>
-      {tooltipData && <Tooltip x={tooltipData.x} y={tooltipData.y} content={tooltipData.content} />}
+      {tooltipData && <BarplotTooltip x={tooltipData.x} y={tooltipData.y} content={tooltipData.content} />}
     </div>
   );
 };
diff --git a/libs/shared/lib/components/colorComponents/colorDropdown/colorLegendDropdown.stories.tsx b/libs/shared/lib/components/colorComponents/colorDropdown/colorLegendDropdown.stories.tsx
new file mode 100644
index 000000000..1f88c8e20
--- /dev/null
+++ b/libs/shared/lib/components/colorComponents/colorDropdown/colorLegendDropdown.stories.tsx
@@ -0,0 +1,22 @@
+import React, { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import DropdownColorLegend from '.';
+
+const Component: Meta<typeof DropdownColorLegend> = {
+  title: 'ColorManager/Legend/DropdownGP',
+  component: DropdownColorLegend,
+  argTypes: { onChange: { action: 'changed' } },
+  decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
+};
+
+export default Component;
+
+type Story = StoryObj<typeof Component>;
+
+export const DropdownColorLegendStory: Story = (args: any) => {
+  const [value, setValue] = useState<string>('');
+
+  return <DropdownColorLegend value={value} onChange={setValue} />;
+};
+
+DropdownColorLegendStory.args = {};
diff --git a/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx b/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx
new file mode 100644
index 000000000..41d3ce9ca
--- /dev/null
+++ b/libs/shared/lib/components/colorComponents/colorDropdown/index.tsx
@@ -0,0 +1,100 @@
+import React, { useState } from 'react';
+import * as d3 from 'd3';
+import { dataColors } from 'config/src/colors.js';
+import { DropdownButton, DropdownContainer, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns';
+import ColorLegend from '../colorLegend/index.js';
+
+type TailwindColor = {
+  colors: string | { [key: number]: string };
+  data: { min: number; max: number };
+  name: string;
+  showAxis: boolean;
+};
+
+type TailwindColors = {
+  [key: string]: TailwindColor;
+};
+
+function generateTailwindColors(dataColors: any) {
+  const tailwindColors: TailwindColors = {};
+
+  for (const colorName in dataColors) {
+    let colorData = dataColors[colorName];
+
+    if (colorName !== 'black' && colorName !== 'white') {
+      tailwindColors[colorName] = {
+        colors: colorData,
+        data: { min: 0, max: 100 },
+        name: `seq:${colorName}`,
+        showAxis: false,
+      };
+    }
+  }
+
+  return tailwindColors;
+}
+
+type DropdownColorLegendProps = {
+  value: any;
+  onChange: (val: any) => void;
+};
+
+export const DropdownColorLegend = ({ value, onChange }: DropdownColorLegendProps) => {
+  const colorStructure = generateTailwindColors(dataColors);
+  const [selectedColorLegend, setSelectedColorLegend] = useState<any>(null);
+
+  const [menuOpen, setMenuOpen] = useState<boolean>(false);
+  const [selectedOption, setSelectedOption] = useState<any>('Select colormap');
+
+  const handleButtonClick = (e: React.MouseEvent<HTMLButtonElement>) => {
+    e.preventDefault();
+    setMenuOpen(!menuOpen);
+  };
+
+  const handleOptionClick = (option: string) => {
+    setSelectedOption(option);
+    setSelectedColorLegend(colorStructure[option]);
+    setMenuOpen(false);
+  };
+
+  return (
+    <div className="w-200 h-200">
+      <DropdownContainer className="w-60">
+        <DropdownButton
+          title={
+            <div className="flex items-center h-4">
+              {selectedColorLegend ? (
+                <ColorLegend
+                  colors={selectedColorLegend.colors}
+                  data={selectedColorLegend.data}
+                  name={selectedColorLegend.name}
+                  showAxis={selectedColorLegend.showAxis}
+                />
+              ) : (
+                <p className="ml-2">{selectedOption}</p>
+              )}
+            </div>
+          }
+          onClick={handleButtonClick}
+        />
+        {menuOpen && (
+          <DropdownItemContainer align="top-10" className="w-60">
+            {Object.keys(colorStructure).map((option: any, index) => (
+              <li key={index} onClick={() => handleOptionClick(option)} className="cursor-pointer flex items-center ml-2 h-4 m-2">
+                <ColorLegend
+                  key={index.toString() + '_colorLegend'}
+                  colors={colorStructure[option].colors}
+                  data={colorStructure[option].data}
+                  name={colorStructure[option].name}
+                  showAxis={colorStructure[option].showAxis}
+                />
+              </li>
+            ))}
+          </DropdownItemContainer>
+        )}
+      </DropdownContainer>
+    </div>
+  );
+};
+
+export default DropdownColorLegend;
diff --git a/libs/shared/lib/components/colorComponents/colorLegend/colorLegendSeqDiv.stories.tsx b/libs/shared/lib/components/colorComponents/colorLegend/colorLegendSeqDiv.stories.tsx
new file mode 100644
index 000000000..57e8ae450
--- /dev/null
+++ b/libs/shared/lib/components/colorComponents/colorLegend/colorLegendSeqDiv.stories.tsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import { Meta } from '@storybook/react';
+import ColorLegend from '.';
+
+export default {
+  title: 'ColorManager/Legend/ColorLegend',
+  component: ColorLegend,
+  decorators: [(story) => <div style={{ margin: '0', width: '300px', height: '50px' }}>{story()}</div>],
+} as Meta<typeof ColorLegend>;
+
+export const Default = {
+  args: {
+    colors: {
+      5: 'hsl(220 80% 98%)',
+      10: 'hsl(220 71% 96%)',
+      20: 'hsl(220 95% 92%)',
+      30: 'hsl(220 92% 85%)',
+      40: 'hsl(220 94% 75%)',
+      50: 'hsl(220 92% 67%)',
+      60: 'hsl(220 84% 58%)',
+      70: 'hsl(220 79% 49%)',
+      80: 'hsl(220 86% 36%)',
+      90: 'hsl(220 80% 23%)',
+      95: 'hsl(220 84% 17%)',
+      100: 'hsl(220 61% 13%)',
+    },
+
+    //colors: dataColors.blue,
+    showAxis: false,
+    data: { min: 0, max: 100 },
+    tickCount: 5,
+    name: 'seq:blue',
+  },
+};
diff --git a/libs/shared/lib/components/colorComponents/colorLegend/index.tsx b/libs/shared/lib/components/colorComponents/colorLegend/index.tsx
new file mode 100644
index 000000000..bd2e3154e
--- /dev/null
+++ b/libs/shared/lib/components/colorComponents/colorLegend/index.tsx
@@ -0,0 +1,93 @@
+import React, { useEffect, useRef } from 'react';
+import * as d3 from 'd3';
+
+export type ColorLegendProps = {
+  colors: { [key: number]: string };
+  //colors: string;
+  data: { min: number; max: number };
+  name: string;
+  showAxis: boolean;
+  tickCount?: number; // Optional prop for specifying tick count
+};
+
+export const ColorLegend = ({ colors, data, tickCount = 5, name, showAxis }: ColorLegendProps) => {
+  const svgRef = useRef<SVGSVGElement | null>(null);
+  useEffect(() => {
+    if (!svgRef.current) return;
+
+    const widthSVG: number = +svgRef.current.clientWidth;
+    const heightSVG: number = +svgRef.current.clientHeight;
+
+    // Parse HSL strings
+
+    const hslValues = Object.values(colors)
+      .map((hslString) => {
+        return hslString;
+      })
+      .reverse();
+
+    // Set up SVG container
+    const svg = d3.select(svgRef.current);
+    svg.selectAll('*').remove(); // Clear previous content
+
+    //const marginPercentage = { top: 0.15, right: 0.1, bottom: 0.15, left: 0.1 };
+    const marginPercentage = { top: 0.0, right: 0.0, bottom: 0.0, left: 0.0 };
+    const margin = {
+      top: marginPercentage.top * heightSVG,
+      right: marginPercentage.right * widthSVG,
+      bottom: marginPercentage.bottom * heightSVG,
+      left: marginPercentage.left * widthSVG,
+    };
+
+    const groupMargin = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`);
+    const widthSVGwithinMargin: number = widthSVG - margin.left - margin.right;
+    const heightSVGwithinMargin: number = heightSVG - margin.top - margin.bottom;
+
+    const idGradient: string = `clrGradient_${name}`;
+    // Create a gradient
+    let gradient = groupMargin
+      .append('defs')
+      .append('linearGradient')
+      .attr('id', idGradient)
+      .attr('x1', '0%')
+      .attr('y1', '0%')
+      .attr('x2', '100%')
+      .attr('y2', '0%');
+
+    // Add color stops to the gradient
+    gradient.selectAll('stop').remove();
+
+    for (let i = 0; i < hslValues.length; i++) {
+      gradient
+        .append('stop')
+        .attr('offset', `${(i / (hslValues.length - 1)) * 100}%`)
+        .style('stop-color', hslValues[i] as string);
+    }
+
+    groupMargin
+      .append('rect')
+      .attr('width', widthSVGwithinMargin)
+      .attr('height', heightSVGwithinMargin)
+      .style('stroke', 'gray')
+      .style('fill', `url(#${idGradient})`);
+    //.style('fill', colors);
+
+    if (showAxis) {
+      const xScale = d3.scaleLinear().domain([data.min, data.max]).range([0, widthSVGwithinMargin]);
+
+      const xAxis = d3.axisBottom(xScale).ticks(tickCount);
+
+      groupMargin.append('g').attr('class', 'x-axis').attr('transform', `translate(0, ${heightSVGwithinMargin})`).call(xAxis);
+
+      svg.selectAll('.tick text').attr('class', 'font-sans text-primary font-semibold').style('stroke', 'none');
+    }
+  }, [colors, data, tickCount]);
+
+  return (
+    <div className="w-full h-full">
+      <svg ref={svgRef} className="container" width="100%" height="100%"></svg>
+    </div>
+  );
+};
+
+export default ColorLegend;
diff --git a/libs/shared/lib/components/forms/index.tsx b/libs/shared/lib/components/forms/index.tsx
index 194772150..643126cac 100644
--- a/libs/shared/lib/components/forms/index.tsx
+++ b/libs/shared/lib/components/forms/index.tsx
@@ -75,13 +75,13 @@ export const FormBody = ({
   children,
   ...props
 }: PropsWithChildren<React.DetailedHTMLProps<React.FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>>) => (
-  <form className="card-body px-0 w-72 py-5" {...props}>
+  <form className="card-body px-0 w-72 py-0" {...props}>
     {children}
   </form>
 );
 export const FormTitle = ({ children, title, onClose }: PropsWithChildren<{ title: string; onClose: () => void }>) => {
   return (
-    <div className="card-title p-5 py-0 flex w-full">
+    <div className="card-title p-5 py-0 mt-2 flex w-full">
       <h2 className="w-full">{title}</h2>
       <Button rounded variant="ghost" iconComponent={<Close />} onClick={() => onClose()} />
     </div>
@@ -90,7 +90,7 @@ export const FormTitle = ({ children, title, onClose }: PropsWithChildren<{ titl
 export const FormHBar = () => <div className="divider m-0"></div>;
 export const FormControl = ({ children }: PropsWithChildren) => <div className="form-control px-5">{children}</div>;
 export const FormActions = (props: { onClose: () => void }) => (
-  <div className="grid grid-cols-2 px-5 gap-2">
+  <div className="grid grid-cols-2 px-5 gap-2 mb-2">
     <Button
       type="secondary"
       variant="outline"
diff --git a/libs/shared/lib/components/icon/index.tsx b/libs/shared/lib/components/icon/index.tsx
index 937427828..68950afb5 100644
--- a/libs/shared/lib/components/icon/index.tsx
+++ b/libs/shared/lib/components/icon/index.tsx
@@ -1,7 +1,7 @@
 import React, { ReactElement } from 'react';
 import { SVGProps } from 'react';
 
-export type Sizes = 16 | 20 | 24 | 28 | 32 | 40;
+export type Sizes = 14 | 16 | 20 | 24 | 28 | 32 | 40;
 export type IconProps = SVGProps<SVGSVGElement> & {
   component: ReactElement<any>;
   size?: Sizes;
diff --git a/libs/shared/lib/components/info/index.tsx b/libs/shared/lib/components/info/index.tsx
new file mode 100644
index 000000000..a9a5f485e
--- /dev/null
+++ b/libs/shared/lib/components/info/index.tsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import Icon from '../icon';
+import Tooltip from '../tooltip';
+import { InfoOutlined } from '@mui/icons-material';
+
+type Props = {
+  tooltip: string;
+};
+
+export default function Info({ tooltip }: Props) {
+  return (
+    <Tooltip position="top" tooltip={tooltip}>
+      <Icon component={<InfoOutlined />} size={14} />
+    </Tooltip>
+  );
+}
diff --git a/libs/shared/lib/components/info/info.stories.tsx b/libs/shared/lib/components/info/info.stories.tsx
new file mode 100644
index 000000000..3104401ff
--- /dev/null
+++ b/libs/shared/lib/components/info/info.stories.tsx
@@ -0,0 +1,27 @@
+import { StoryObj, Meta } from '@storybook/react';
+import Info from '.';
+
+export default {
+  title: 'Components/Info',
+  component: Info,
+  decorators: [(Story) => <div className="flex m-5">{Story()}</div>],
+  argTypes: {
+    name: {
+      control: 'select',
+      options: ['ArrowBack', 'DeleteOutline', 'KeyboardArrowLeft', 'Settings'],
+    },
+    size: {
+      control: 'radio',
+      options: [16, 20, 24, 28, 32, 40],
+    },
+  },
+} as Meta;
+type Story = StoryObj<typeof Info>;
+
+const InfoButton: Story = {
+  args: {
+    tooltip: 'ArrowBack',
+  },
+};
+
+export const Default = { ...InfoButton };
diff --git a/libs/shared/lib/components/inputs/index.tsx b/libs/shared/lib/components/inputs/index.tsx
index 2595b8038..b06e1cdbb 100644
--- a/libs/shared/lib/components/inputs/index.tsx
+++ b/libs/shared/lib/components/inputs/index.tsx
@@ -58,12 +58,13 @@ type RadioProps = {
 
 type DropdownProps = {
   label?: string;
-  value: string | number;
+  value: string | number | undefined;
   type: 'dropdown';
   options: any;
   tooltip?: string;
   onChange?: (value: string | number) => void;
   required?: boolean;
+  disabled?: boolean;
 };
 
 export type InputProps = TextProps | SliderProps | CheckboxProps | DropdownProps | RadioProps | BooleanProps;
@@ -236,7 +237,7 @@ export const BooleanInput = ({ label, value, onChange, tooltip }: BooleanProps)
   );
 };
 
-export const DropDownInput = ({ label, value, options, onChange, required = false, tooltip }: DropdownProps) => {
+export const DropDownInput = ({ label, value, options, onChange, required = false, tooltip, disabled = false }: DropdownProps) => {
   const dropdownRef = React.useRef<HTMLDivElement>(null);
   const [isDropdownOpen, setIsDropdownOpen] = React.useState<boolean>(false);
 
@@ -266,6 +267,7 @@ export const DropDownInput = ({ label, value, options, onChange, required = fals
       <DropdownContainer className="w-full" ref={dropdownRef}>
         <DropdownButton
           title={value}
+          disabled={disabled}
           onClick={(e) => {
             e.stopPropagation();
             e.preventDefault();
diff --git a/libs/shared/lib/components/tooltip/index.tsx b/libs/shared/lib/components/tooltip/index.tsx
index f44efea2c..060cb73ea 100644
--- a/libs/shared/lib/components/tooltip/index.tsx
+++ b/libs/shared/lib/components/tooltip/index.tsx
@@ -1,12 +1,12 @@
-import React from 'react';
+import React, { ReactNode } from 'react';
 
-export interface TooltipProps {
+export interface BarTooltipProps {
   x: number;
   y: number;
   content: React.ReactNode;
 }
 
-const Tooltip: React.FC<TooltipProps> = ({ x, y, content }) => {
+export const BarplotTooltip: React.FC<BarTooltipProps> = ({ x, y, content }) => {
   return (
     <div className="absolute font-sans bg-light border border-secondary text-secondary p-2" style={{ left: `${x}px`, top: `${y}px` }}>
       {content}
@@ -14,4 +14,44 @@ const Tooltip: React.FC<TooltipProps> = ({ x, y, content }) => {
   );
 };
 
-export default Tooltip;
+export type TooltipProps = {
+  position?: 'top' | 'bottom' | 'left' | 'right';
+  children: ReactNode;
+  tooltip: string;
+};
+
+export default function Tooltip({ position = 'bottom', children, tooltip }: TooltipProps) {
+  return (
+    <div className="group relative cursor-pointer">
+      <div className="mx-2 my-2">{children}</div>
+      <span
+        className={`absolute hidden group-hover:inline-block bg-neutral-900 text-white text-xs p-2 whitespace-nowrap rounded ${
+          position === 'top' ? 'left-1/2 -translate-x-1/2 bottom-[calc(100%+5px)]' : ''
+        } ${position === 'bottom' ? 'left-1/2 -translate-x-1/2 top-[calc(100%+5px)]' : ''} ${
+          position === 'left' ? 'top-1/2 -translate-y-1/2 right-[calc(100%+5px)]' : ''
+        } ${position === 'right' ? 'top-1/2 -translate-y-1/2 left-[calc(100%+5px)]' : ''} `}
+      >
+        {tooltip}
+      </span>
+      <span
+        className={`absolute hidden group-hover:inline-block border-[6px] ${
+          position === 'top'
+            ? 'left-1/2 -translate-x-1/2 bottom-full border-l-transparent border-r-transparent border-b-0 border-t-neutral-900'
+            : ''
+        } ${
+          position === 'bottom'
+            ? 'left-1/2 -translate-x-1/2 top-full border-l-transparent border-r-transparent border-t-0 border-b-neutral-900'
+            : ''
+        } ${
+          position === 'left'
+            ? 'top-1/2 -translate-y-1/2 right-full border-t-transparent border-b-transparent border-r-0 border-l-neutral-900'
+            : ''
+        } ${
+          position === 'right'
+            ? 'top-1/2 -translate-y-1/2 left-full border-t-transparent border-b-transparent border-l-0 border-r-neutral-900'
+            : ''
+        } `}
+      ></span>
+    </div>
+  );
+}
diff --git a/libs/shared/lib/components/tooltip/tooltip.stories.tsx b/libs/shared/lib/components/tooltip/tooltip.stories.tsx
index 4a3ee29ab..1004f713a 100644
--- a/libs/shared/lib/components/tooltip/tooltip.stories.tsx
+++ b/libs/shared/lib/components/tooltip/tooltip.stories.tsx
@@ -7,22 +7,62 @@ export default {
   component: Tooltip,
 } as Meta<typeof Tooltip>;
 
-export const BarplotTooltip = (args: TooltipProps) => {
-  const tooltipContent = (
-    <div>
-      <span>{'VVD'}</span>: <span>{30}</span>
+export const TooltipBottom = (args: TooltipProps) => {
+  return (
+    <div className="flex m-5">
+      <Tooltip {...args}>
+        <h1>Hover over me</h1>
+      </Tooltip>
+    </div>
+  );
+};
+
+TooltipBottom.args = {
+  position: 'bottom',
+  tooltip: 'This is a tooltip',
+};
+
+export const TooltipTop = (args: TooltipProps) => {
+  return (
+    <div className="flex m-5">
+      <Tooltip {...args}>
+        <h1>Hover over me</h1>
+      </Tooltip>
+    </div>
+  );
+};
+
+TooltipTop.args = {
+  position: 'top',
+  tooltip: 'This is a tooltip',
+};
+
+export const TooltipLeft = (args: TooltipProps) => {
+  return (
+    <div className="flex m-5">
+      <Tooltip {...args}>
+        <h1>Hover over me</h1>
+      </Tooltip>
     </div>
   );
+};
+
+TooltipLeft.args = {
+  position: 'left',
+  tooltip: 'This is a tooltip',
+};
 
+export const TooltipRight = (args: TooltipProps) => {
   return (
-    <div style={{ position: 'relative' }}>
-      <Tooltip {...args} content={tooltipContent} />
+    <div className="flex m-5">
+      <Tooltip {...args}>
+        <h1>Hover over me</h1>
+      </Tooltip>
     </div>
   );
 };
 
-BarplotTooltip.args = {
-  x: 100,
-  y: 100,
-  content: 'This is a tooltip',
+TooltipRight.args = {
+  position: 'right',
+  tooltip: 'This is a tooltip',
 };
diff --git a/libs/shared/lib/data-access/api/eventBus.tsx b/libs/shared/lib/data-access/api/eventBus.tsx
index 0c7038aa2..0cb409ceb 100644
--- a/libs/shared/lib/data-access/api/eventBus.tsx
+++ b/libs/shared/lib/data-access/api/eventBus.tsx
@@ -3,11 +3,8 @@ import {
   useAuthorizationCache,
   useAppDispatch,
   useSessionCache,
-  useQuerybuilderGraph,
   useQuerybuilderHash,
-  useML,
   useMLEnabledHash,
-  useConfig,
   useQuerybuilderSettings,
   readInSchemaFromBackend,
   assignNewGraphQueryResult,
@@ -15,10 +12,15 @@ import {
   useVisualizationState,
   wsSchemaRequest,
   wsSchemaSubscription,
+  useQuerybuilderGraph,
+  useGraphQueryResult,
 } from '@graphpolaris/shared/lib/data-access';
 import { Broker, wsQuerySubscription, wsQueryTranslationSubscription } from '@graphpolaris/shared/lib/data-access/broker';
 import { addInfo } from '@graphpolaris/shared/lib/data-access/store/configSlice';
-import { GraphQueryResultFromBackendPayload, queryingBackend } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice';
+import {
+  GraphQueryResultFromBackendPayload,
+  addGraphQueryStatistics,
+} from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice';
 import { allMLTypes, LinkPredictionInstance, setMLResult } from '@graphpolaris/shared/lib/data-access/store/mlSlice';
 import { QueryBuilderText, setQueryText, setQuerybuilderNodes } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
 import { SchemaFromBackend } from '@graphpolaris/shared/lib/schema';
@@ -47,6 +49,8 @@ import {
 import { URLParams, getParam, deleteParam } from './url';
 import { setVisualizationState } from '../store/visualizationSlice';
 import { isEqual } from 'lodash-es';
+import { addSchemaAttributeDimensions } from '../store/schemaSlice';
+import { QueryMultiGraph } from '../../querybuilder';
 
 export const EventBus = (props: { onRunQuery: Function; onAuthorized: Function }) => {
   const { login } = useAuth();
@@ -57,7 +61,9 @@ export const EventBus = (props: { onRunQuery: Function; onAuthorized: Function }
   const queryBuilder = useQuerybuilder();
   const mlHash = useMLEnabledHash();
   const visState = useVisualizationState();
+  const query = useQuerybuilderGraph() as QueryMultiGraph;
   const queryBuilderSettings = useQuerybuilderSettings();
+  const graphQueryResult = useGraphQueryResult();
 
   function loadSaveState(saveStateID: string | undefined, saveStates: Record<string, SaveStateI>) {
     if (saveStateID && saveStates && saveStateID in saveStates) {
@@ -70,6 +76,12 @@ export const EventBus = (props: { onRunQuery: Function; onAuthorized: Function }
     }
   }
 
+  useEffect(() => {
+    if (graphQueryResult.nodes.length > 0 || graphQueryResult.edges.length > 0) {
+      dispatch(addGraphQueryStatistics(query));
+    }
+  }, [graphQueryResult.nodes, graphQueryResult.edges]);
+
   useEffect(() => {
     const unsubs: Function[] = [];
 
@@ -93,6 +105,11 @@ export const EventBus = (props: { onRunQuery: Function; onAuthorized: Function }
       }),
     );
 
+    Broker.instance().subscribe((data: any) => {
+      dispatch(addSchemaAttributeDimensions(data));
+      dispatch(addInfo('Attribute dimensions added'));
+    }, 'schema_inference');
+
     // Broker.instance().subscribe((data: QueryBuilderState) => dispatch(setQuerybuilderNodes(data)), 'query_builder_state');
 
     allMLTypes.forEach((mlType) => {
@@ -137,6 +154,12 @@ export const EventBus = (props: { onRunQuery: Function; onAuthorized: Function }
       }),
     );
 
+    Broker.instance().subscribe((response: QueryBuilderText) => {
+      if (response && response.result) {
+        dispatch(setQueryText(response));
+      }
+    }, 'query_translation_result');
+
     login();
 
     // Setup cleanup
@@ -145,6 +168,9 @@ export const EventBus = (props: { onRunQuery: Function; onAuthorized: Function }
       unsubs.forEach((unsub) => {
         unsub();
       });
+            Broker.instance().unSubscribeAll('query_translation_result');
+      Broker.instance().unSubscribeAll('schema_inference');
+
     };
   }, []);
 
diff --git a/libs/shared/lib/data-access/store/graphQueryResultSlice.ts b/libs/shared/lib/data-access/store/graphQueryResultSlice.ts
index 4c3668126..557afdaf3 100755
--- a/libs/shared/lib/data-access/store/graphQueryResultSlice.ts
+++ b/libs/shared/lib/data-access/store/graphQueryResultSlice.ts
@@ -1,5 +1,7 @@
 import { createSlice, PayloadAction } from '@reduxjs/toolkit';
 import type { RootState } from './store';
+import { GraphStatistics, retrieveGraphStatistics } from '../utils/result-statistics';
+import { QueryMultiGraph } from '../../querybuilder';
 
 export interface GraphQueryResultFromBackendPayload {
   queryID: string;
@@ -54,6 +56,7 @@ export interface GraphQueryResult {
   // Describes what entities there are in this graph query result.
   nodeTypes: string[];
   queryingBackend: boolean;
+  statistics?: GraphStatistics;
 }
 
 // Define the initial state using that type
@@ -118,14 +121,21 @@ export const graphQueryResultSlice = createSlice({
     queryingBackend: (state) => {
       state.queryingBackend = true;
     },
+    addGraphQueryStatistics: (state, action: PayloadAction<QueryMultiGraph>) => {
+      if (state.nodes.length > 0 && state.edges.length > 0) {
+        state.statistics = retrieveGraphStatistics({ nodes: state.nodes, edges: state.edges }, action.payload);
+      }
+    },
   },
 });
 
-export const { assignNewGraphQueryResult, resetGraphQueryResults, queryingBackend } = graphQueryResultSlice.actions;
+export const { assignNewGraphQueryResult, resetGraphQueryResults, queryingBackend, addGraphQueryStatistics } =
+  graphQueryResultSlice.actions;
 
 // Other code such as selectors can use the imported `RootState` type
 export const selectGraphQueryResult = (state: RootState) => state.graphQueryResult;
 export const selectGraphQueryResultNodes = (state: RootState) => state.graphQueryResult.nodes;
 export const selectGraphQueryResultLinks = (state: RootState) => state.graphQueryResult.edges;
+export const selectGraphQueryResultStats = (state: RootState) => state.graphQueryResult.statistics || {};
 
 export default graphQueryResultSlice.reducer;
diff --git a/libs/shared/lib/data-access/store/hooks.ts b/libs/shared/lib/data-access/store/hooks.ts
index 282db06af..920170edf 100644
--- a/libs/shared/lib/data-access/store/hooks.ts
+++ b/libs/shared/lib/data-access/store/hooks.ts
@@ -1,5 +1,5 @@
 import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
-import { selectGraphQueryResult } from './graphQueryResultSlice';
+import { selectGraphQueryResult, selectGraphQueryResultStats } from './graphQueryResultSlice';
 import { schemaGraph, selectSchemaLayout, schemaSettingsState } from './schemaSlice';
 import type { RootState, AppDispatch } from './store';
 import { configState } from '@graphpolaris/shared/lib/data-access/store/configSlice';
@@ -11,7 +11,14 @@ import {
 } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
 import { sessionCacheState } from './sessionSlice';
 import { authState } from './authSlice';
-import { visualizationState, visualizationSettings } from './visualizationSlice';
+import {
+  visualizationState,
+  visualizationSettings,
+  visSettingsState,
+  visGeneralSettings,
+  visEncodingsState,
+  visInteractionsState,
+} from './visualizationSlice';
 import { allMLEnabled, selectML } from './mlSlice';
 import { searchResultState, searchResultData, searchResultSchema, searchResultQB, recentSearches } from './searchResultSlice';
 
@@ -22,6 +29,7 @@ export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
 
 /** Gives the graphQueryResult from the store */
 export const useGraphQueryResult = () => useAppSelector(selectGraphQueryResult);
+export const useGraphQueryResultStats = () => useAppSelector(selectGraphQueryResultStats);
 
 // Gives the schema
 export const useSchemaGraph = () => useAppSelector(schemaGraph);
@@ -53,3 +61,7 @@ export const useRecentSearches = () => useAppSelector(recentSearches);
 // Visualization Slices
 export const useVisualizationState = () => useAppSelector(visualizationState);
 export const useVisualizationSettings = () => useAppSelector(visualizationSettings);
+export const useGeneralSettings = () => useAppSelector(visGeneralSettings);
+export const useVisSettings = () => useAppSelector(visSettingsState);
+export const useVisEncodings = () => useAppSelector(visEncodingsState);
+export const useVisInteractions = () => useAppSelector(visInteractionsState);
diff --git a/libs/shared/lib/data-access/store/schemaSlice.ts b/libs/shared/lib/data-access/store/schemaSlice.ts
index 85097c6d0..3f696c4ae 100644
--- a/libs/shared/lib/data-access/store/schemaSlice.ts
+++ b/libs/shared/lib/data-access/store/schemaSlice.ts
@@ -46,9 +46,13 @@ export const schemaSlice = createSlice({
     setSchemaSettings: (state, action: PayloadAction<SchemaSettings>) => {
       state.settings = action.payload;
     },
+
+    addSchemaAttributeDimensions: (state, action: PayloadAction<any>) => {
+      state.graph = SchemaUtils.addAttributeDimensionsToGraph(state.graph, JSON.parse(action.payload));
+    },
   },
 });
-export const { readInSchemaFromBackend, setSchema, setSchemaSettings, clearSchema } = schemaSlice.actions;
+export const { readInSchemaFromBackend, setSchema, setSchemaSettings, clearSchema, addSchemaAttributeDimensions } = schemaSlice.actions;
 
 export const schemaSettingsState = (state: RootState) => state.schema.settings;
 
diff --git a/libs/shared/lib/data-access/store/visualizationSlice.ts b/libs/shared/lib/data-access/store/visualizationSlice.ts
index c5b1d281c..c2a5ce70b 100644
--- a/libs/shared/lib/data-access/store/visualizationSlice.ts
+++ b/libs/shared/lib/data-access/store/visualizationSlice.ts
@@ -1,35 +1,24 @@
 import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit';
 import type { RootState } from './store';
-import { globalConfigSchemaTypes, localConfigSchemaType } from '../../vis/types';
+import { globalConfigTypes, VisualizationConfiguration } from '../../vis/types';
 import { isEqual } from 'lodash-es';
-
-export enum Visualizations {
-  NodeLink = 'NodeLinkVis',
-  Paohvis = 'PaohVis',
-  RawJSON = 'RawJSONVis',
-  Table = 'TableVis',
-  Matrix = 'MatrixVis',
-}
+import { EncodingTypes } from '../../vis/configuration/encodings';
+import { SettingTypes } from '../../vis/configuration/settings';
+import { InteractionTypes } from '../../vis/configuration/interactions';
 
 export type VisState = {
-  activeVisualization: Visualizations;
+  activeVisualization: string;
   settings: {
-    general: globalConfigSchemaTypes;
-    [id: string]: localConfigSchemaType;
+    general: globalConfigTypes;
+  } & {
+    [id: string]: VisualizationConfiguration;
   };
 };
 
 export const initialState: VisState = {
-  activeVisualization: Visualizations.NodeLink,
+  activeVisualization: 'NodeLink',
   settings: {
-    general: {
-      // Example general setting
-      showLegend: {
-        value: false,
-        label: 'Show legend',
-        type: 'boolean',
-      },
-    },
+    general: {},
   },
 };
 
@@ -37,34 +26,60 @@ export const visualizationSlice = createSlice({
   name: 'visualization',
   initialState,
   reducers: {
-    addVisualization: (state, action: PayloadAction<{ id: string; settings: localConfigSchemaType }>) => {
-      const { id, settings } = action.payload;
-      state.settings[id] = settings;
+    addVisualization: (
+      state,
+      action: PayloadAction<{
+        id: string;
+        settings: SettingTypes;
+        encodings: EncodingTypes;
+        interactions: InteractionTypes;
+      }>,
+    ) => {
+      const { id, settings, encodings, interactions } = action.payload;
+      state.settings[id] = { settings, encodings, interactions };
     },
     removeVisualization: (state, action: PayloadAction<string>) => {
       if (state.settings[action.payload]) {
         delete state.settings[action.payload];
       }
     },
-    updateVisualizationSettings: (state, action: PayloadAction<{ id: string; settings: any }>) => {
-      const { id, settings } = action.payload;
-      state.settings[id] = { ...state.settings[id], ...settings };
+    updateVisualizationSettings: (state, action: PayloadAction<{ id: string; configuration: any }>) => {
+      const { id, configuration } = action.payload;
+      state.settings[id] = { ...state.settings[id], ...configuration };
     },
-    updateGeneralSettings: (state, action: PayloadAction<any>) => {
+    updateGeneralSettings: (state, action: PayloadAction<globalConfigTypes>) => {
       state.settings.general = action.payload;
     },
-    setActiveVisualization: (state, action: PayloadAction<Visualizations>) => {
+    setActiveVisualization: (state, action: PayloadAction<string>) => {
       state.activeVisualization = action.payload;
     },
-    updateSettings: (state, action: PayloadAction<any>) => {
-      state.settings.visualization = action.payload;
-    },
     setVisualizationState: (state, action: PayloadAction<VisState>) => {
       if (action.payload.activeVisualization && !isEqual(action.payload, state)) {
         state.activeVisualization = action.payload.activeVisualization;
         state.settings = action.payload.settings;
       }
     },
+    updateGeneralConfiguration: (state, action: PayloadAction<any>) => {
+      state.settings.general = action.payload;
+    },
+    updateVisConfiguration: (state, action: PayloadAction<any>) => {
+      state.settings[state.activeVisualization] = action.payload;
+    },
+    updateVisSettings: (state, action: PayloadAction<any>) => {
+      if (state.activeVisualization && state.settings[state.activeVisualization]) {
+        state.settings[state.activeVisualization]!.settings = action.payload;
+      }
+    },
+    updateVisEncodings: (state, action: PayloadAction<any>) => {
+      if (state.activeVisualization && state.settings[state.activeVisualization]) {
+        state.settings[state.activeVisualization]!.encodings = action.payload;
+      }
+    },
+    updateVisInteractions: (state, action: PayloadAction<any>) => {
+      if (state.activeVisualization && state.settings[state.activeVisualization]) {
+        state.settings[state.activeVisualization]!.interactions = action.payload;
+      }
+    },
   },
 });
 
@@ -74,15 +89,24 @@ export const {
   updateVisualizationSettings,
   updateGeneralSettings,
   setActiveVisualization,
-  updateSettings,
   setVisualizationState,
+  updateGeneralConfiguration,
+  updateVisConfiguration,
+  updateVisSettings,
+  updateVisEncodings,
+  updateVisInteractions,
 } = visualizationSlice.actions;
 
 export const visualizationState = (state: RootState) => state.visualize;
 export const visualizationAllSettings = (state: RootState) => state.visualize.settings;
 export const visualizationSettings = createSelector([visualizationAllSettings, visualizationState], (settings, visualize) => ({
   general: settings.general,
-  [visualize.activeVisualization]: settings[visualize.activeVisualization],
+  [visualize.activeVisualization]: visualize.settings[visualize.activeVisualization],
 }));
 
+export const visGeneralSettings = (state: RootState) => state.visualize.settings.general ?? {};
+export const visSettingsState = (state: RootState) => state.visualize.settings[state.visualize.activeVisualization]?.settings ?? {};
+export const visEncodingsState = (state: RootState) => state.visualize.settings[state.visualize.activeVisualization]?.encodings ?? {};
+export const visInteractionsState = (state: RootState) => state.visualize.settings[state.visualize.activeVisualization]?.interactions ?? {};
+
 export default visualizationSlice.reducer;
diff --git a/libs/shared/lib/data-access/utils/result-statistics/compress-graph.ts b/libs/shared/lib/data-access/utils/result-statistics/compress-graph.ts
new file mode 100644
index 000000000..5b12b06b3
--- /dev/null
+++ b/libs/shared/lib/data-access/utils/result-statistics/compress-graph.ts
@@ -0,0 +1,32 @@
+import { Edge, Node } from '../../store';
+import { CompressedElement, CompressedGraph } from './result-statistics.types';
+
+const compressElement = (element: Node[] | Edge[]) => {
+  const compressed: CompressedElement = {};
+
+  element.forEach((item) => {
+    const { label, attributes } = item;
+    if (!compressed[label]) {
+      compressed[label] = {};
+    }
+    Object.entries(attributes).forEach(([attributeId, attributeValue]) => {
+      if (!compressed[label][attributeId]) {
+        compressed[label][attributeId] = [];
+      }
+      compressed[label][attributeId].push(attributeValue);
+    });
+  });
+
+  return compressed;
+};
+
+export const compressGraph = (graph: { nodes: Node[]; edges: Edge[] }): CompressedGraph => {
+  try {
+    return {
+      nodes: compressElement(graph.nodes),
+      edges: compressElement(graph.edges),
+    };
+  } catch (error) {
+    return { nodes: {}, edges: {} };
+  }
+};
diff --git a/libs/shared/lib/data-access/utils/result-statistics/dimensions.ts b/libs/shared/lib/data-access/utils/result-statistics/dimensions.ts
new file mode 100644
index 000000000..753939111
--- /dev/null
+++ b/libs/shared/lib/data-access/utils/result-statistics/dimensions.ts
@@ -0,0 +1,39 @@
+import { CategoricalStats, NumericalStats, SpatialStats, TemporalStats } from './result-statistics.types';
+
+export function extractCategoricalStats(data: any[]): CategoricalStats {
+  const uniqueItems = new Set(data.map((item) => JSON.stringify(item)));
+  return {
+    uniqueItems: uniqueItems.size,
+  };
+}
+
+export function extractNumericalStats(data: number[]): NumericalStats {
+  const minValue = Math.min(...data);
+  const maxValue = Math.max(...data);
+  const sum = data.reduce((acc, curr) => acc + curr, 0);
+  const avgValue = sum / data.length;
+  return {
+    minValue,
+    maxValue,
+    avgValue,
+  };
+}
+
+export function extractTemporalStats(data: Date[]): TemporalStats {
+  const sortedDates = data.slice().sort((a, b) => a.getTime() - b.getTime());
+  const minDate = sortedDates[0];
+  const maxDate = sortedDates[sortedDates.length - 1];
+  return {
+    minDate,
+    maxDate,
+  };
+}
+
+export function extractSpatialStats(data: [number, number][]): SpatialStats {
+  const totalPoints = data.length;
+  const sumCoordinates = data.reduce(([x1, y1], [x2, y2]) => [x1 + x2, y1 + y2], [0, 0]);
+  const middlePoint: [number, number] = [sumCoordinates[0] / totalPoints, sumCoordinates[1] / totalPoints];
+  return {
+    middlePoint,
+  };
+}
diff --git a/libs/shared/lib/data-access/utils/result-statistics/extract-dimensions.ts b/libs/shared/lib/data-access/utils/result-statistics/extract-dimensions.ts
new file mode 100644
index 000000000..e87898649
--- /dev/null
+++ b/libs/shared/lib/data-access/utils/result-statistics/extract-dimensions.ts
@@ -0,0 +1,17 @@
+import { QueryMultiGraph } from '@graphpolaris/shared/lib/querybuilder';
+import { DimensionType } from '@graphpolaris/shared/lib/schema';
+
+export const extractAttributeDimensions = (qbGraph: QueryMultiGraph, elementType: 'entity' | 'relation') => {
+  return qbGraph.nodes
+    .filter((item) => item.attributes?.type === elementType)
+    .reduce((acc: { [key: string]: { [key: string]: DimensionType } }, item) => {
+      item.attributes?.attributes?.forEach((attribute) => {
+        if (attribute.handleData && attribute.handleData.attributeName && attribute.handleData.attributeDimension) {
+          const name: string = item.attributes?.name as string;
+          acc[name] = acc[name] || {};
+          acc[name][attribute.handleData.attributeName] = attribute.handleData.attributeDimension;
+        }
+      });
+      return acc;
+    }, {});
+};
diff --git a/libs/shared/lib/data-access/utils/result-statistics/extract-stats.ts b/libs/shared/lib/data-access/utils/result-statistics/extract-stats.ts
new file mode 100644
index 000000000..10c8e26e6
--- /dev/null
+++ b/libs/shared/lib/data-access/utils/result-statistics/extract-stats.ts
@@ -0,0 +1,55 @@
+import { extractCategoricalStats, extractNumericalStats, extractSpatialStats, extractTemporalStats } from './dimensions';
+import { CompressedElement, DimensionInfoType, DimensionStatistics, ElementStatisticsType } from './result-statistics.types';
+
+export const extractStatistics = (elements: CompressedElement, dimensions: DimensionInfoType) => {
+  const statistics: ElementStatisticsType = {};
+
+  Object.keys(elements).forEach((label) => {
+    statistics[label] = {};
+    Object.entries(elements[label]).forEach(([attributeId, attributeValues]) => {
+      const dimensionType = dimensions[label][attributeId];
+      statistics[label][attributeId] = { dimension: dimensionType };
+      statistics[label][attributeId].stats = calculateStatistics(attributeValues, dimensionType, statistics[label][attributeId].stats);
+    });
+  });
+
+  return statistics;
+};
+
+export const calculateStatistics = (item: any[], dimensionType: string, statsField: any) => {
+  let stats: DimensionStatistics | undefined;
+  switch (dimensionType) {
+    case 'categorical':
+      stats = extractCategoricalStats(item);
+      break;
+    case 'numerical':
+      stats = extractNumericalStats(item);
+      break;
+    case 'spatial':
+      stats = extractSpatialStats(item);
+      break;
+    case 'temporal':
+      stats = extractTemporalStats(item);
+      break;
+    default:
+      break;
+  }
+
+  if (stats !== undefined) {
+    statsField = statsField || {};
+    for (const key in stats) {
+      if (Object.prototype.hasOwnProperty.call(stats, key)) {
+        const statsKey = key as keyof DimensionStatistics;
+        if (typeof stats[statsKey] === 'number') {
+          statsField[statsKey] = (statsField[statsKey] || 0) + stats[statsKey];
+        } else if (Array.isArray(stats[statsKey])) {
+          statsField[statsKey] = (statsField[statsKey] || []).concat(stats[statsKey] as any[]);
+        } else {
+          throw new Error('Invalid stats type');
+        }
+      }
+    }
+  }
+
+  return statsField || {};
+};
diff --git a/libs/shared/lib/data-access/utils/result-statistics/graph-statistics.ts b/libs/shared/lib/data-access/utils/result-statistics/graph-statistics.ts
new file mode 100644
index 000000000..e29bfec3a
--- /dev/null
+++ b/libs/shared/lib/data-access/utils/result-statistics/graph-statistics.ts
@@ -0,0 +1,26 @@
+import { Node, Edge } from '../../store/graphQueryResultSlice';
+import { QueryMultiGraph } from '../../../querybuilder';
+import { GraphStatistics, DimensionMap, CompressedGraph } from './result-statistics.types';
+import { compressGraph } from './compress-graph';
+import { extractStatistics } from './extract-stats';
+import { extractAttributeDimensions } from './extract-dimensions';
+
+export const retrieveGraphStatistics = (graph: { nodes: Node[]; edges: Edge[] }, qbGraph: QueryMultiGraph): GraphStatistics => {
+  try {
+    const compressedGraph: CompressedGraph = compressGraph(graph);
+
+    const nodeAttributes = extractAttributeDimensions(qbGraph, 'entity');
+    const edgeAttributes = extractAttributeDimensions(qbGraph, 'relation');
+    const dimensionMap: DimensionMap = { nodes: nodeAttributes, edges: edgeAttributes };
+
+    return {
+      nodes: extractStatistics(compressedGraph.nodes, dimensionMap.nodes),
+      edges: extractStatistics(compressedGraph.edges, dimensionMap.edges),
+    };
+  } catch (error) {
+    return {
+      nodes: {},
+      edges: {},
+    };
+  }
+};
diff --git a/libs/shared/lib/data-access/utils/result-statistics/index.ts b/libs/shared/lib/data-access/utils/result-statistics/index.ts
new file mode 100644
index 000000000..9d4505f3f
--- /dev/null
+++ b/libs/shared/lib/data-access/utils/result-statistics/index.ts
@@ -0,0 +1,2 @@
+export { retrieveGraphStatistics } from './graph-statistics';
+export * from './result-statistics.types';
diff --git a/libs/shared/lib/data-access/utils/result-statistics/result-statistics.types.ts b/libs/shared/lib/data-access/utils/result-statistics/result-statistics.types.ts
new file mode 100644
index 000000000..ad48b7823
--- /dev/null
+++ b/libs/shared/lib/data-access/utils/result-statistics/result-statistics.types.ts
@@ -0,0 +1,52 @@
+import { DimensionType } from '../../../schema';
+
+export type CategoricalStats = {
+  uniqueItems: number;
+};
+
+export type NumericalStats = {
+  minValue: number;
+  maxValue: number;
+  avgValue: number;
+};
+
+export type TemporalStats = {
+  minDate: Date;
+  maxDate: Date;
+};
+
+export type SpatialStats = {
+  middlePoint: [number, number];
+};
+
+export type DimensionStatistics = CategoricalStats | NumericalStats | TemporalStats | SpatialStats;
+
+export type DimensionInfoType = { [label: string]: { [attribute: string]: DimensionType } };
+
+export type DimensionMap = {
+  nodes: DimensionInfoType;
+  edges: DimensionInfoType;
+};
+
+export type ElementStats = {
+  dimension: DimensionType;
+  stats?: DimensionStatistics;
+};
+
+export type ElementStatisticsType = {
+  [label: string]: { [attribute: string]: ElementStats };
+};
+
+export interface GraphStatistics {
+  nodes: ElementStatisticsType;
+  edges: ElementStatisticsType;
+}
+
+export type CompressedElement = {
+  [label: string]: { [attribute: string]: any };
+};
+
+export type CompressedGraph = {
+  nodes: CompressedElement;
+  edges: CompressedElement;
+};
diff --git a/libs/shared/lib/querybuilder/model/graphology/model.ts b/libs/shared/lib/querybuilder/model/graphology/model.ts
index 1f851ab78..dc4a2bc1a 100644
--- a/libs/shared/lib/querybuilder/model/graphology/model.ts
+++ b/libs/shared/lib/querybuilder/model/graphology/model.ts
@@ -2,7 +2,7 @@ import { Attributes as GAttributes } from 'graphology-types';
 import { XYPosition } from 'reactflow';
 import { MultiGraph } from 'graphology';
 import './utils';
-import { GeneralDescription, InputNodeType, InputNodeTypeTypes } from '../logic/general';
+import { GeneralDescription, InputNodeDimension, InputNodeType, InputNodeTypeTypes } from '../logic/general';
 import { AllLogicTypes } from '../logic';
 import { Handles, QueryElementTypes } from '../reactflow';
 
@@ -64,6 +64,7 @@ export type QueryGraphNodes = EntityNodeAttributes | RelationNodeAttributes | Lo
 export type QueryGraphEdgeAttribute = {
   attributeName?: string;
   attributeType?: InputNodeType;
+  attributeDimension?: InputNodeDimension;
 };
 
 export type QueryGraphEdgeHandle = {
diff --git a/libs/shared/lib/querybuilder/model/graphology/utils.ts b/libs/shared/lib/querybuilder/model/graphology/utils.ts
index d97b7fada..7c2878826 100644
--- a/libs/shared/lib/querybuilder/model/graphology/utils.ts
+++ b/libs/shared/lib/querybuilder/model/graphology/utils.ts
@@ -79,6 +79,7 @@ export class QueryMultiGraphology extends Graph<QueryGraphNodes, QueryGraphEdges
             ...defaultHandleData,
             attributeName: optAttribute.name,
             attributeType: optAttribute.type,
+            attributeDimension: optAttribute.dimension,
             handleType: Handles.EntityAttribute,
           },
         });
@@ -94,6 +95,7 @@ export class QueryMultiGraphology extends Graph<QueryGraphNodes, QueryGraphEdges
             ...defaultHandleData,
             attributeName: optAttribute.name,
             attributeType: optAttribute.type,
+            attributeDimension: optAttribute.dimension,
             handleType: Handles.RelationAttribute,
           },
         });
@@ -140,7 +142,7 @@ export class QueryMultiGraphology extends Graph<QueryGraphNodes, QueryGraphEdges
     source: QueryGraphNodes,
     target: QueryGraphNodes,
     attributes: QueryGraphEdgesOpt = {},
-    options: AddEdge2GraphologyOptions = {}
+    options: AddEdge2GraphologyOptions = {},
   ): string | null {
     let sourceAttributeName = '';
     let sourceAttributeType: InputNodeType | undefined;
diff --git a/libs/shared/lib/querybuilder/model/logic/general.ts b/libs/shared/lib/querybuilder/model/logic/general.ts
index 9d2337393..16e81c19e 100644
--- a/libs/shared/lib/querybuilder/model/logic/general.ts
+++ b/libs/shared/lib/querybuilder/model/logic/general.ts
@@ -1,5 +1,6 @@
 export type InputNodeTypeTypes = string | number | boolean;
 export type InputNodeType = 'string' | 'float' | 'int' | 'bool' | 'date' | 'time' | 'datetime' | 'duration';
+export type InputNodeDimension = 'categorical' | 'numerical' | 'temporal' | 'spatial';
 
 export enum NumberFilterTypes {
   EQUAL = '==',
diff --git a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx
index 7f2cac788..d8f27749d 100644
--- a/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx
+++ b/libs/shared/lib/querybuilder/pills/customFlowPills/entitypill/entitypill.tsx
@@ -49,12 +49,12 @@ export const EntityFlowElement = React.memo((node: SchemaReactflowEntityNode) =>
   return (
     <div className="p-3 bg-transparent" onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
       <div className={`rounded-sm shadow min-w-[8rem] text-[0.8rem] bg-gradient-to-r pt-1 from-[#FFA952] to-[#D66700]`}>
-        <div className={`py-1 ${data.selected ? 'bg-secondary-400' : 'bg-secondary-50'}`}>
+        <div className={`pt-1 ${data.selected ? 'bg-secondary-400' : 'bg-secondary-50'}`}>
           <FilterHandle
             handle={data.leftRelationHandleId}
             type="target"
             position={Position.Left}
-            className={'!top-8 !left-2 !bg-accent-700 !rounded-none'}
+            className={'!top-8 !left-2 !bg-danger-700 !rounded-none'}
           />
           <FilterHandle
             handle={data.rightRelationHandleId}
diff --git a/libs/shared/lib/querybuilder/pills/pilldropdown/pilldropdown.tsx b/libs/shared/lib/querybuilder/pills/pilldropdown/pilldropdown.tsx
index 315b9c87c..5e704d6d2 100644
--- a/libs/shared/lib/querybuilder/pills/pilldropdown/pilldropdown.tsx
+++ b/libs/shared/lib/querybuilder/pills/pilldropdown/pilldropdown.tsx
@@ -1,10 +1,10 @@
-import { useMemo } from 'react';
+import { useMemo, ReactNode, ReactElement } from 'react';
 import { NodeAttribute, QueryGraphEdges, SchemaReactflowEntityNode, handleDataFromReactflowToDataId, toHandleId } from '../../model';
-import { sortedIndex } from 'lodash-es';
-import { Handle, Position } from 'reactflow';
+import { Position } from 'reactflow';
 import styles from './pilldropdown.module.scss';
-import { styleHandleMap } from '../utils';
 import { FilterHandle } from '../FilterHandle';
+import { Abc, CalendarToday, Map, Numbers, Place, QuestionMarkOutlined } from '@mui/icons-material';
+import Icon from '@graphpolaris/shared/lib/components/icon';
 
 type PillDropdownProps = {
   node: SchemaReactflowEntityNode;
@@ -15,6 +15,18 @@ type PillDropdownProps = {
   onHandleMouseDown: (attribute: NodeAttribute, i: number, event: React.MouseEvent) => void;
 };
 
+type IconMapType = {
+  [key: string]: ReactElement;
+};
+
+const IconMap: IconMapType = {
+  temporal: <CalendarToday />,
+  spatial: <Map />,
+  numerical: <Numbers />,
+  categorical: <Abc />,
+  default: <Place />,
+};
+
 export const PillDropdown = (props: PillDropdownProps) => {
   const forceOpen = false;
   const openNumbers: number[] = useMemo(() => {
@@ -36,7 +48,7 @@ export const PillDropdown = (props: PillDropdownProps) => {
 
   return (
     <>
-      <div className={'' + (openNumbers.length > 0 ? 'animate-openmenu ' : 'animate-closemenu ')}>
+      <div className={'divide-y divide-secondary-100' + (openNumbers.length > 0 ? 'animate-openmenu ' : 'animate-closemenu ')}>
         {openNumbers.map((i) => {
           const attribute = props.attributes[i];
           if (attribute.handleData.attributeName === undefined) {
@@ -45,23 +57,24 @@ export const PillDropdown = (props: PillDropdownProps) => {
 
           return (
             <div
-              className="px-5 py-1 font-semibold"
+              className="px-2 py-1 bg-white flex justify-between items-center"
               key={(attribute.handleData.attributeName || '') + i}
               onMouseDown={(event: React.MouseEvent) => {
                 props.onHandleMouseDown(attribute, i, event);
               }}
             >
-              {attribute.handleData.attributeName}
+              <p>{attribute.handleData.attributeName}</p>
+              {attribute.handleData?.attributeDimension && (
+                // <div className="!text-xs text-secondary-500">{IconMap[attribute.handleData.attributeDimension]}</div>
+                // <div className="!text-xs text-secondary-500">
+                <Icon component={IconMap[attribute.handleData.attributeDimension]} size={16} />
+                // </div>
+              )}
               <FilterHandle
                 handle={handleDataFromReactflowToDataId(props.node, attribute)}
                 type="source"
                 position={Position.Right}
-                className={
-                  styles.handle +
-                  ' ' +
-                  (attribute.handleData.attributeType ? styleHandleMap[attribute.handleData.attributeType] : '') +
-                  ' !top-auto !-translate-y-[145%] !right-[1.2rem]'
-                }
+                className={styles.handle + ' ' + '!top-auto mt-2 !right-[0.5rem] bg-secondary-500'}
               ></FilterHandle>
             </div>
           );
diff --git a/libs/shared/lib/schema/model/FromBackend.ts b/libs/shared/lib/schema/model/FromBackend.ts
index c080591de..b5427dbf6 100644
--- a/libs/shared/lib/schema/model/FromBackend.ts
+++ b/libs/shared/lib/schema/model/FromBackend.ts
@@ -10,10 +10,13 @@ export type SchemaFromBackend = {
 
 export type SchemaAttributeTypes = 'string' | 'float' | 'int' | 'bool' | 'date' | 'time' | 'datetime' | 'duration';
 
+export type DimensionType = 'categorical' | 'numerical' | 'temporal' | 'spatial';
+
 /** Attribute type, consist of a name */
 export type SchemaAttribute = {
   name: string;
   type: SchemaAttributeTypes;
+  dimension?: DimensionType;
 };
 
 /** Node type, consist of a name and a list of attributes */
@@ -33,3 +36,8 @@ export type SchemaEdge = {
   label: string;
   type?: string;
 };
+
+export type GraphAttributeDimensions = {
+  nodes: { [id: string]: { [id: string]: string } };
+  edges: { [id: string]: { [id: string]: string } };
+};
diff --git a/libs/shared/lib/schema/schema-utils/schema-utils.ts b/libs/shared/lib/schema/schema-utils/schema-utils.ts
index 7d8dfa699..6a26bc74d 100644
--- a/libs/shared/lib/schema/schema-utils/schema-utils.ts
+++ b/libs/shared/lib/schema/schema-utils/schema-utils.ts
@@ -1,4 +1,4 @@
-import { SchemaAttribute, SchemaFromBackend, SchemaGraphology, SchemaGraphologyNode } from '../model';
+import { DimensionType, GraphAttributeDimensions, SchemaFromBackend, SchemaGraph, SchemaGraphology, SchemaGraphologyNode } from '../model';
 
 export class SchemaUtils {
   public static schemaBackend2Graphology(schemaFromBackend: SchemaFromBackend): SchemaGraphology {
@@ -42,4 +42,34 @@ export class SchemaUtils {
     });
     return schemaGraphology;
   }
+
+  public static addAttributeDimensionsToGraph(graph: SchemaGraph, inference: GraphAttributeDimensions): SchemaGraph {
+    const { nodes, edges } = inference;
+
+    graph.nodes.forEach((node) => {
+      const dimensions = nodes[node.key];
+      if (dimensions) {
+        node.attributes?.attributes.forEach((attribute) => {
+          const dimension = dimensions[attribute.name] as DimensionType | undefined;
+          if (dimension) {
+            attribute.dimension = dimension;
+          }
+        });
+      }
+    });
+
+    graph.edges.forEach((edge) => {
+      const dimensions = edges[edge.attributes?.name];
+      if (dimensions) {
+        edge.attributes?.attributes.forEach((attribute: any) => {
+          const dimension = dimensions[attribute.name] as DimensionType | undefined;
+          if (dimension) {
+            attribute.dimension = dimension;
+          }
+        });
+      }
+    });
+
+    return graph;
+  }
 }
diff --git a/libs/shared/lib/vis/configuration/advanced/advanced.tsx b/libs/shared/lib/vis/configuration/advanced/advanced.tsx
new file mode 100644
index 000000000..d7bc88177
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/advanced/advanced.tsx
@@ -0,0 +1,32 @@
+import React, { useState, useEffect } from 'react';
+import { useAppDispatch, useVisualizationState, useVisualizationSettings } from '@graphpolaris/shared/lib/data-access';
+import ReactJSONView from 'react-json-view';
+import { updateVisConfiguration } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+
+export default function AdvancedPanel() {
+  const dispatch = useAppDispatch();
+  const vis = useVisualizationState();
+  const settings = useVisualizationSettings();
+  const [updatedSettings, setUpdatedSettings] = useState(settings);
+
+  useEffect(() => {
+    if (settings !== updatedSettings) {
+      dispatch(updateVisConfiguration(updatedSettings));
+    }
+  }, [updatedSettings]);
+
+  return (
+    settings && (
+      <div className="m-2">
+        <ReactJSONView
+          src={settings[vis.activeVisualization] || {}}
+          name={false}
+          collapsed={1}
+          quotesOnKeys={false}
+          displayDataTypes={false}
+          onEdit={(v) => setUpdatedSettings(v.updated_src as any)}
+        />
+      </div>
+    )
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/advanced/index.ts b/libs/shared/lib/vis/configuration/advanced/index.ts
new file mode 100644
index 000000000..a7584d4b2
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/advanced/index.ts
@@ -0,0 +1 @@
+export { default as AdvancedPanel } from './advanced';
diff --git a/libs/shared/lib/vis/configuration/encodings/accessor.tsx b/libs/shared/lib/vis/configuration/encodings/accessor.tsx
new file mode 100644
index 000000000..66651f931
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/accessor.tsx
@@ -0,0 +1,62 @@
+import React, { useState, useEffect } from 'react';
+import Input from '@graphpolaris/shared/lib/components/inputs';
+import { useQuerybuilderGraph } from '@graphpolaris/shared/lib/data-access';
+
+type Props = {
+  value: string | undefined;
+  onChange: (val: string | number) => void;
+  element: 'node' | 'edge';
+  dimension?: string[];
+  disabled?: boolean;
+};
+
+export default function Accessor({ value, onChange, element, dimension, disabled = false }: Props) {
+  const qbGraph = useQuerybuilderGraph();
+  const [options, setOptions] = useState<string[]>([]);
+
+  useEffect(() => {
+    let commonAttributes: Set<string> = new Set();
+
+    if (element === 'node') {
+      qbGraph.nodes
+        .filter((node) => node.attributes?.type === 'entity')
+        .forEach((node) => {
+          const nodeAttributes = new Set<string>();
+          node.attributes?.attributes?.forEach((attr) => {
+            if (attr.handleData?.attributeDimension && dimension?.includes(attr.handleData?.attributeDimension)) {
+              attr.handleData.attributeName && nodeAttributes.add(`attributes.${attr.handleData.attributeName}`);
+            }
+          });
+          if (commonAttributes.size === 0) {
+            commonAttributes = nodeAttributes;
+          } else {
+            commonAttributes = new Set([...commonAttributes].filter((x) => nodeAttributes.has(x)));
+          }
+        });
+    } else {
+      qbGraph.nodes
+        .filter((edge) => edge.attributes?.type === 'relation')
+        .forEach((edge) => {
+          const edgeAttributes = new Set<string>();
+          edge.attributes?.attributes?.forEach((attr) => {
+            if (attr.handleData?.attributeDimension && dimension?.includes(attr.handleData?.attributeDimension)) {
+              attr.handleData.attributeName && edgeAttributes.add(`attributes.${attr.handleData.attributeName}`);
+            }
+          });
+          if (commonAttributes.size === 0) {
+            commonAttributes = edgeAttributes;
+          } else {
+            commonAttributes = new Set([...commonAttributes].filter((x) => edgeAttributes.has(x)));
+          }
+        });
+    }
+
+    setOptions(Array.from(commonAttributes));
+  }, [element, dimension]);
+
+  return (
+    <div className="mb-2">
+      <Input type="dropdown" value={value} onChange={(val: string | number) => onChange(val)} options={options} disabled={disabled} />
+    </div>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/encodings/encoding.tsx b/libs/shared/lib/vis/configuration/encodings/encoding.tsx
new file mode 100644
index 000000000..c51774780
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/encoding.tsx
@@ -0,0 +1,86 @@
+import React, { useState, useEffect } from 'react';
+import { useAppDispatch, useVisEncodings } from '@graphpolaris/shared/lib/data-access';
+import { updateVisEncodings } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import Accessor from './accessor';
+import Info from '@graphpolaris/shared/lib/components/info';
+import { isEqual } from 'lodash-es';
+import Selector from './selector';
+import { EncodingTypes } from './encodings.types';
+
+export default function EncodingPanel() {
+  const dispatch = useAppDispatch();
+  const encodings = useVisEncodings();
+  const [encodingUpdates, setEncodingUpdates] = useState<{
+    [id: string]: {
+      marking: any;
+      accessorPath: string;
+    };
+  }>({});
+
+  useEffect(() => {
+    const newEncodings: EncodingTypes = Object.fromEntries(
+      Object.entries(encodings).map(([key, immutableEncoding]) => [
+        key,
+        {
+          ...(immutableEncoding || {}),
+          ...(encodingUpdates[key] || {}),
+        },
+      ])
+    );
+
+    if (!isEqual(encodings, newEncodings)) {
+      dispatch(updateVisEncodings(newEncodings));
+    }
+  }, [encodingUpdates, encodings, dispatch]);
+
+  const updateEncoding = (encoding: string, val: any, type: 'accessorPath' | 'marking') => {
+    setEncodingUpdates((prevState) => {
+      if (prevState) {
+        return {
+          ...prevState,
+          [encoding]: {
+            ...(prevState[encoding] || {}),
+            [type]: val,
+          },
+        };
+      } else {
+        return prevState;
+      }
+    });
+  };
+
+  return (
+    encodings && (
+      <div>
+        {Object.keys(encodings).map((k) => {
+          const item = encodings[k];
+          const value = encodingUpdates[k] ? encodingUpdates[k] : { marking: undefined, accessorPath: undefined };
+
+          return (
+            <div key={k} className="bg-secondary-50 p-2 m-1">
+              <div className="flex items-center justify-between">
+                <h1 className="text-xs">{item.label ? item.label : k}</h1>
+                {item.description && <Info tooltip={item.description} />}
+              </div>
+              <Accessor
+                value={value.accessorPath}
+                onChange={(value: string | number) => updateEncoding(k, value, 'accessorPath')}
+                element={item.element}
+                dimension={item.dimension ?? []}
+              />
+              {value.accessorPath && (
+                <Selector
+                  key={k}
+                  selectorType={item.selector}
+                  marking={value.marking}
+                  onChange={(value: any) => updateEncoding(k, value, 'marking')}
+                  accessorPath={value.accessorPath}
+                />
+              )}
+            </div>
+          );
+        })}
+      </div>
+    )
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/encodings/encodings.types.ts b/libs/shared/lib/vis/configuration/encodings/encodings.types.ts
new file mode 100644
index 000000000..ef1bc5b36
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/encodings.types.ts
@@ -0,0 +1,31 @@
+import { DimensionType } from '@graphpolaris/shared/lib/schema';
+import { EncodingSelector } from './selectors';
+import { ElementStats } from '@graphpolaris/shared/lib/data-access/utils/result-statistics';
+
+export type SelectorType = keyof typeof EncodingSelector;
+
+export type ElementType = 'node' | 'edge';
+
+export type EncodingType = 'label' | 'attribute';
+
+export type Encoding = {
+  label?: string;
+  marking?: any;
+  accessorPath?: string;
+  onChange?: (value: any) => void;
+  element: ElementType;
+  dimension?: DimensionType[];
+  selector: SelectorType;
+  description?: string;
+  condition?: () => boolean;
+};
+
+export type SelectorProps = {
+  marking: any;
+  onChange: (marking: any) => void;
+  statistics: ElementStats | undefined;
+};
+
+export type EncodingTypes = { [id: string]: Encoding };
+
+export type EncodingProps = { [K in keyof EncodingTypes]?: any };
diff --git a/libs/shared/lib/vis/configuration/encodings/index.ts b/libs/shared/lib/vis/configuration/encodings/index.ts
new file mode 100644
index 000000000..056c6ae4d
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/index.ts
@@ -0,0 +1,2 @@
+export type { Encoding, EncodingTypes, EncodingProps } from './encodings.types';
+export { default as EncodingPanel } from './encoding';
diff --git a/libs/shared/lib/vis/configuration/encodings/selector.tsx b/libs/shared/lib/vis/configuration/encodings/selector.tsx
new file mode 100644
index 000000000..7acf16bba
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selector.tsx
@@ -0,0 +1,29 @@
+import React, { useState, useEffect } from 'react';
+import { EncodingSelector } from './selectors';
+import { useGraphQueryResultStats } from '@graphpolaris/shared/lib/data-access';
+import { ElementStats } from '@graphpolaris/shared/lib/data-access/utils/result-statistics';
+
+type Props = {
+  selectorType: keyof typeof EncodingSelector;
+  marking: any;
+  onChange: (val: any) => void;
+  accessorPath: string;
+};
+
+export default function Selector({ selectorType, marking, onChange, accessorPath }: Props) {
+  const graphStatistics = useGraphQueryResultStats();
+  const [statistics, setStatistics] = useState<ElementStats | undefined>();
+
+  const SelectorComponent = EncodingSelector[selectorType];
+
+  const getStatisticsFromAccessorPath = (accessorPath: string): ElementStats | undefined => {
+    return undefined;
+  };
+
+  useEffect(() => {
+    const attributeStatistics = getStatisticsFromAccessorPath(accessorPath);
+    setStatistics(attributeStatistics);
+  }, []);
+
+  return <SelectorComponent marking={marking} onChange={onChange} statistics={statistics} />;
+}
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/axis.stories.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/axis.stories.tsx
new file mode 100644
index 000000000..bdd786e4f
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/axis.stories.tsx
@@ -0,0 +1,21 @@
+import React, { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import Axis from './axis';
+
+const Component: Meta<typeof Axis> = {
+  title: 'Components/Selectors/axis',
+  component: Axis,
+  argTypes: { onChange: { action: 'changed' } },
+  decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
+};
+
+export default Component;
+
+type Story = StoryObj<typeof Component>;
+
+export const AxisStory: Story = (args: any) => {
+  const [value, setValue] = useState<string>('');
+  return <Axis {...args} marking={value} onChange={setValue} />;
+};
+
+AxisStory.args = {};
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/axis.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/axis.tsx
new file mode 100644
index 000000000..bd987f71d
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/axis.tsx
@@ -0,0 +1,11 @@
+import React from 'react';
+import Input from '@graphpolaris/shared/lib/components/inputs';
+import { SelectorProps } from '../encodings.types';
+
+export default function Axis({ marking, onChange }: SelectorProps) {
+  return (
+    <div>
+      <Input type="dropdown" label="Select axis" options={[]} value={marking} onChange={onChange} />
+    </div>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/color.stories.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/color.stories.tsx
new file mode 100644
index 000000000..43169984c
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/color.stories.tsx
@@ -0,0 +1,21 @@
+import React, { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import Color from './color';
+
+const Component: Meta<typeof Color> = {
+  title: 'Components/Selectors/color',
+  component: Color,
+  argTypes: { onChange: { action: 'changed' } },
+  decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
+};
+
+export default Component;
+
+type Story = StoryObj<typeof Component>;
+
+export const ColorStory: Story = (args: any) => {
+  const [value, setValue] = useState<string>('');
+  return <Color {...args} marking={value} onChange={setValue} />;
+};
+
+ColorStory.args = {};
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/color.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/color.tsx
new file mode 100644
index 000000000..4a21d5698
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/color.tsx
@@ -0,0 +1,16 @@
+import React, { useState } from 'react';
+import { SelectorProps } from '../encodings.types';
+import DropdownColorLegend from '@graphpolaris/shared/lib/components/colorComponents/colorDropdown';
+import Input from '@graphpolaris/shared/lib/components/inputs';
+
+export default function Color({ marking, onChange }: SelectorProps) {
+  const [colors, setColors] = useState<any>(null);
+  const [custom, setCustom] = useState<boolean>(false);
+
+  return (
+    <div>
+      <Input type="boolean" label="Custom palette?" value={custom} onChange={(val: boolean) => setCustom(val)} />
+      <DropdownColorLegend value={colors} onChange={setColors} />
+    </div>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/index.ts b/libs/shared/lib/vis/configuration/encodings/selectors/index.ts
new file mode 100644
index 000000000..14964560c
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/index.ts
@@ -0,0 +1,15 @@
+import Color from './color';
+import Shape from './shape';
+import Size from './size';
+import Axis from './axis';
+import Opacity from './opacity';
+
+export const EncodingSelector = {
+  Color: Color,
+  Shape: Shape,
+  Size: Size,
+  Axis: Axis,
+  Opacity: Opacity,
+};
+
+// Inspiration: https://uwdata.github.io/visualization-curriculum/altair_marks_encoding.html
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/opacity.stories.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/opacity.stories.tsx
new file mode 100644
index 000000000..2f3f3a67c
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/opacity.stories.tsx
@@ -0,0 +1,21 @@
+import React, { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import Opacity from './opacity';
+
+const Component: Meta<typeof Opacity> = {
+  title: 'Components/Selectors/opacity',
+  component: Opacity,
+  argTypes: { onChange: { action: 'changed' } },
+  decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
+};
+
+export default Component;
+
+type Story = StoryObj<typeof Component>;
+
+export const OpacityStory: Story = (args: any) => {
+  const [value, setValue] = useState<string>('');
+  return <Opacity {...args} marking={value} onChange={setValue} />;
+};
+
+OpacityStory.args = {};
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/opacity.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/opacity.tsx
new file mode 100644
index 000000000..40d85e5c8
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/opacity.tsx
@@ -0,0 +1,15 @@
+import React, { useState } from 'react';
+import Input from '@graphpolaris/shared/lib/components/inputs';
+import { SelectorProps } from '../encodings.types';
+
+export default function Opacity({ marking, onChange }: SelectorProps) {
+  const [custom, setCustom] = useState<boolean>(false);
+
+  return (
+    <div>
+      <Input type="dropdown" label="Select axis" options={[]} value={marking} onChange={onChange} />
+      <Input type="boolean" label="Custom input" value={custom} onChange={(value) => setCustom(value)} />
+      {custom && <Input type="slider" label="Select value" value={marking} onChange={onChange} min={0} max={100} step={10} />}
+    </div>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/shape.stories.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/shape.stories.tsx
new file mode 100644
index 000000000..f2a1c62ca
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/shape.stories.tsx
@@ -0,0 +1,21 @@
+import React, { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import Shape from './shape';
+
+const Component: Meta<typeof Shape> = {
+  title: 'Components/Selectors/shape',
+  component: Shape,
+  argTypes: { onChange: { action: 'changed' } },
+  decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
+};
+
+export default Component;
+
+type Story = StoryObj<typeof Component>;
+
+export const ShapeStory: Story = (args: any) => {
+  const [value, setValue] = useState<string>('');
+  return <Shape {...args} marking={value} onChange={setValue} />;
+};
+
+ShapeStory.args = {};
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/shape.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/shape.tsx
new file mode 100644
index 000000000..ae3b6ae45
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/shape.tsx
@@ -0,0 +1,6 @@
+import React from 'react';
+import { SelectorProps } from '../encodings.types';
+
+export default function Shape({ marking }: SelectorProps) {
+  return <div>shape</div>;
+}
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/size.stories.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/size.stories.tsx
new file mode 100644
index 000000000..fc3d1ea2d
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/size.stories.tsx
@@ -0,0 +1,22 @@
+import React, { useState } from 'react';
+import type { Meta, StoryObj } from '@storybook/react';
+import Size from './size';
+
+const Component: Meta<typeof Size> = {
+  title: 'Components/Selectors/size',
+  component: Size,
+  argTypes: { onChange: { action: 'changed' } },
+  decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
+};
+
+export default Component;
+
+type Story = StoryObj<typeof Component>;
+
+export const SizeStory: Story = (args: any) => {
+  const [value, setValue] = useState<string>('');
+
+  return <Size {...args} marking={value} onChange={setValue} />;
+};
+
+SizeStory.args = {};
diff --git a/libs/shared/lib/vis/configuration/encodings/selectors/size.tsx b/libs/shared/lib/vis/configuration/encodings/selectors/size.tsx
new file mode 100644
index 000000000..aa7e1de99
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/encodings/selectors/size.tsx
@@ -0,0 +1,15 @@
+import React, { useState } from 'react';
+import Input from '@graphpolaris/shared/lib/components/inputs';
+import { SelectorProps } from '../encodings.types';
+
+export default function Size({ marking, onChange }: SelectorProps) {
+  const [item, setItem] = useState<string>('');
+  const [custom, setCustom] = useState<boolean>(false);
+
+  return (
+    <div>
+      <Input type="boolean" label="Custom input" value={custom} onChange={(value) => setCustom(value)} />
+      {custom && <Input type="slider" label="Select value" value={marking} onChange={onChange} min={0} max={100} step={10} />}
+    </div>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/index.ts b/libs/shared/lib/vis/configuration/index.ts
new file mode 100644
index 000000000..3ded492f1
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/index.ts
@@ -0,0 +1 @@
+export { default as VisualizationDialog } from './panel/panel';
diff --git a/libs/shared/lib/vis/configuration/interactions/index.ts b/libs/shared/lib/vis/configuration/interactions/index.ts
new file mode 100644
index 000000000..61687e06e
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/interactions/index.ts
@@ -0,0 +1,2 @@
+export type { Interaction, InteractionTypes, InteractionProps } from './interaction.types';
+export { default as InteractionPanel } from './interaction';
diff --git a/libs/shared/lib/vis/configuration/interactions/interaction.tsx b/libs/shared/lib/vis/configuration/interactions/interaction.tsx
new file mode 100644
index 000000000..cd40f451b
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/interactions/interaction.tsx
@@ -0,0 +1,24 @@
+import React, { useEffect, useState } from 'react';
+import { useAppDispatch, useVisInteractions } from '@graphpolaris/shared/lib/data-access';
+import { updateVisInteractions } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+
+export default function InteractionPanel() {
+  const dispatch = useAppDispatch();
+  const interactions = useVisInteractions();
+  const [interactionUpdates, setInteractionUpdates] = useState(interactions);
+
+  useEffect(() => {
+    if (interactions !== interactionUpdates) {
+      dispatch(updateVisInteractions(interactionUpdates));
+      console.debug('Interactions updated');
+    }
+  }, [interactionUpdates]);
+
+  return (
+    interactions && (
+      <div>
+        <p>Interaction settings</p>
+      </div>
+    )
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/interactions/interaction.types.ts b/libs/shared/lib/vis/configuration/interactions/interaction.types.ts
new file mode 100644
index 000000000..bb81b4909
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/interactions/interaction.types.ts
@@ -0,0 +1,7 @@
+import { InputProps } from '@graphpolaris/shared/lib/components/inputs';
+
+export type Interaction = InputProps & { condition?: (config: Record<string, any>) => boolean };
+
+export type InteractionTypes = { [id: string]: Interaction };
+
+export type InteractionProps = { [K in keyof InteractionTypes]?: any };
diff --git a/libs/shared/lib/vis/configuration/panel/panel-header.tsx b/libs/shared/lib/vis/configuration/panel/panel-header.tsx
new file mode 100644
index 000000000..86e434095
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/panel/panel-header.tsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import { FormTitle } from '@graphpolaris/shared/lib/components/forms';
+import { AutoAwesome, CandlestickChart, LockOpen, Settings } from '@mui/icons-material';
+import { useVisualizationState } from '@graphpolaris/shared/lib/data-access';
+import TabItem from './tab-item';
+
+type Props = {
+  onClose: any;
+  activeTab: number;
+  setActiveTab: any;
+};
+
+export default function PanelHeader({ onClose, activeTab, setActiveTab }: Props) {
+  const vis = useVisualizationState();
+
+  const encodings = vis.settings[vis.activeVisualization]?.encodings;
+  const encodingsExist = encodings && Object.keys(encodings).length > 0;
+  const settings = vis.settings[vis.activeVisualization]?.settings;
+  const settingsExist = settings && Object.keys(settings).length > 0;
+  const interactions = vis.settings[vis.activeVisualization]?.interactions;
+  const interactionsExist = interactions && Object.keys(interactions).length > 0;
+
+  return (
+    <div className="flex flex-col pt-2 bg-secondary-100">
+      <FormTitle title="Settings" onClose={onClose} />
+      <ul className="flex flex-wrap pt-4 pl-5 text-sm font-medium text-center text-gray-500 dark:text-gray-400">
+        {encodingsExist && <TabItem active={activeTab === 0} onClick={() => setActiveTab(0)} icon={<AutoAwesome />} tooltip="Encodings" />}
+        {settingsExist && <TabItem active={activeTab === 1} onClick={() => setActiveTab(1)} icon={<Settings />} tooltip="Settings" />}
+        {interactionsExist && (
+          <TabItem active={activeTab === 2} onClick={() => setActiveTab(2)} icon={<CandlestickChart />} tooltip="Interactions" />
+        )}
+        {(settingsExist || encodingsExist || interactionsExist) && (
+          <TabItem active={activeTab === 3} onClick={() => setActiveTab(3)} icon={<LockOpen />} tooltip="Advanced" />
+        )}
+      </ul>
+    </div>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/panel/panel.tsx b/libs/shared/lib/vis/configuration/panel/panel.tsx
new file mode 100644
index 000000000..b25794983
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/panel/panel.tsx
@@ -0,0 +1,37 @@
+import React, { useState } from 'react';
+import { FormActions, FormBody, FormCard, FormDiv, FormHBar } from '@graphpolaris/shared/lib/components/forms';
+import { DialogProps } from '@graphpolaris/shared/lib/components/Dialog';
+import PanelHeader from './panel-header';
+import { SettingsPanel } from '../settings';
+import { AdvancedPanel } from '../advanced';
+import { EncodingPanel } from '../encodings';
+import { InteractionPanel } from '../interactions';
+
+export default function VisualizationDialog(props: DialogProps) {
+  const [activeTab, setActiveTab] = useState<number>(1);
+
+  return (
+    <>
+      {props.open && (
+        <FormDiv>
+          <FormCard>
+            <FormBody
+              onSubmit={(e) => {
+                e.preventDefault();
+                props.onClose();
+              }}
+            >
+              <PanelHeader onClose={props.onClose} activeTab={activeTab} setActiveTab={setActiveTab} />
+              {activeTab === 0 && <EncodingPanel />}
+              {activeTab === 1 && <SettingsPanel />}
+              {activeTab === 2 && <InteractionPanel />}
+              {activeTab === 3 && <AdvancedPanel />}
+              <FormHBar />
+              <FormActions onClose={props.onClose} />
+            </FormBody>
+          </FormCard>
+        </FormDiv>
+      )}
+    </>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/panel/tab-item.tsx b/libs/shared/lib/vis/configuration/panel/tab-item.tsx
new file mode 100644
index 000000000..72d055dcd
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/panel/tab-item.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import Tooltip from '@graphpolaris/shared/lib/components/tooltip';
+
+type TabItemProps = {
+  active: boolean;
+  onClick: () => void;
+  icon: JSX.Element;
+  tooltip: string;
+};
+
+export default function TabItem({ active, onClick, icon, tooltip }: TabItemProps) {
+  return (
+    <li
+      className={`me-2 inline-block bg-secondary-100 cursor-pointer ${active && 'border-b-2 border-primary-200 text-primary-200'}`}
+      onClick={onClick}
+    >
+      <Tooltip tooltip={tooltip}>{icon}</Tooltip>
+    </li>
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/settings/index.ts b/libs/shared/lib/vis/configuration/settings/index.ts
new file mode 100644
index 000000000..825e0031f
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/settings/index.ts
@@ -0,0 +1,2 @@
+export type { Setting, SettingTypes, SettingProps } from './settings.types';
+export { default as SettingsPanel } from './settings';
diff --git a/libs/shared/lib/vis/configuration/settings/settings.tsx b/libs/shared/lib/vis/configuration/settings/settings.tsx
new file mode 100644
index 000000000..bf4a9408a
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/settings/settings.tsx
@@ -0,0 +1,84 @@
+import React, { useState, useEffect } from 'react';
+import { useAppDispatch, useGeneralSettings, useVisSettings, useVisualizationState } from '@graphpolaris/shared/lib/data-access';
+import { globalConfigTypes } from '../../types';
+import { FormControl } from '@graphpolaris/shared/lib/components/forms';
+import Input from '@graphpolaris/shared/lib/components/inputs';
+import { updateGeneralSettings, updateVisSettings } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+
+export default function SettingsPanel() {
+  const dispatch = useAppDispatch();
+  const vis = useVisualizationState();
+
+  const generalSettings = useGeneralSettings();
+  const [settingsGeneral, setSettingsGeneral] = useState<globalConfigTypes>(generalSettings);
+
+  const visSettings = useVisSettings();
+  const [settingsSpecific, setSettingsSpecific] = useState(visSettings);
+
+  useEffect(() => {
+    if (settingsGeneral !== generalSettings) {
+      dispatch(updateGeneralSettings(settingsGeneral));
+    }
+  }, [settingsGeneral]);
+
+  useEffect(() => {
+    if (settingsSpecific !== visSettings) {
+      dispatch(updateVisSettings(settingsSpecific));
+    }
+  }, [settingsSpecific]);
+
+  useEffect(() => {
+    if (visSettings !== settingsSpecific) {
+      setSettingsSpecific(visSettings);
+    }
+  }, [vis.activeVisualization]);
+
+  return (
+    generalSettings &&
+    visSettings && (
+      <div>
+        <div>
+          {Object.keys(settingsGeneral).map((val) => (
+            <FormControl key={val}>
+              <Input
+                {...settingsGeneral[val]}
+                value={vis.settings.general[val].value as any}
+                onChange={(value: any) => {
+                  setSettingsGeneral((prevSettings) => ({
+                    ...prevSettings,
+                    [val]: { ...prevSettings[val], value: value },
+                  }));
+                }}
+              />
+            </FormControl>
+          ))}
+        </div>
+
+        <div>
+          {Object.keys(settingsSpecific).map((val) => {
+            const currentSetting = settingsSpecific[val];
+            const shouldShowSetting = currentSetting.condition ? currentSetting.condition?.(settingsSpecific.settings) : true;
+            return (
+              shouldShowSetting && (
+                <div key={val} className="bg-secondary-50 p-2 m-2">
+                  <FormControl>
+                    <Input
+                      {...settingsSpecific[val]}
+                      value={settingsSpecific[val]?.value as any}
+                      onChange={(value: any) => {
+                        setSettingsSpecific((prevSettings) => ({
+                          ...prevSettings,
+                          [val]: { ...prevSettings[val], value: value },
+                        }));
+                      }}
+                    />
+                  </FormControl>
+                </div>
+              )
+            );
+          })}
+        </div>
+      </div>
+    )
+  );
+}
diff --git a/libs/shared/lib/vis/configuration/settings/settings.types.ts b/libs/shared/lib/vis/configuration/settings/settings.types.ts
new file mode 100644
index 000000000..9c93ba813
--- /dev/null
+++ b/libs/shared/lib/vis/configuration/settings/settings.types.ts
@@ -0,0 +1,10 @@
+import { InputProps } from '@graphpolaris/shared/lib/components/inputs';
+
+export type Setting = InputProps & {
+  condition?: (config: Record<string, any>) => boolean;
+  description?: string;
+};
+
+export type SettingTypes = { [id: string]: Setting };
+
+export type SettingProps = { [K in keyof SettingTypes]: any };
diff --git a/libs/shared/lib/vis/documentation.mdx b/libs/shared/lib/vis/documentation.mdx
deleted file mode 100644
index 34ce016ac..000000000
--- a/libs/shared/lib/vis/documentation.mdx
+++ /dev/null
@@ -1,152 +0,0 @@
-import { Meta, Unstyled } from '@storybook/blocks';
-
-<Meta title="Visualizations/Implementation" />
-
-# Visualization documentation
-
-## Contents
-
-1. [Introduction](#Introduction)
-2. [Overview](#Overview)
-3. [Implementation](#Implementation)
-4. [Props](#Props)
-
-## Introduction
-
-This is the documentation for implementing visualizations within the GraphPolaris system.
-
-## Overview
-
-The basic code implementation looks like this:
-
-```jsx
-import React from 'react';
-import { VisualizationPropTypes, VISComponentType, localConfigSchemaType } from '@graphpolaris/shared/lib/vis/Types';
-
-/**
- * Props specific to the Visualization component.
- */
-export type VisualizationProps = {};
-
-/**
- * Visualization Component
- * @param {object} props - Props for the Visualization component.
- * @param {GraphQueryResult} props.data - Queried data from the database.
- * @param {object} props.schema - Database schema.
- * @param {any} props.ml - Machine-learning results.
- * @param {function} props.dispatch - Redux dispatch for state management.
- * @param {object} props.globalConfig - Global configurations for the panel (applicable to all visualizations).
- * @param {object} props.localConfig - Local configuration props unique to this visualization.
- * @returns {JSX.Element} - Rendered content of the visualization.
- */
-export const Visualization = ({ data, schema, ml, dispatch, globalConfig, localConfig }: VisualizationPropTypes) => {
-  // Perform additional processing specific to this visualization using the provided props
-
-  return (
-    // Rendered visualization content
-  );
-};
-
-/**
- * Schema for local configuration attributes.
- */
-const localConfigSchema: localConfigSchemaType = {};
-
-/**
- * Visualization Component Object
- * Contains information and configurations for the exported visualization component.
- */
-export const VisualizationComponent: VISComponentType = {
-  displayName: 'Visualization',
-  VIS: Visualization,
-  localConfigSchema: localConfigSchema,
-};
-```
-
-## Implementation
-
-### VisualizationProps
-
-This is where the types are defined for the local configuration of the visualization. It has to define all values that are in the localConfigSchema. This is exported and used in the visualization factory for the local config types.
-
-Example:
-
-```jsx
-export type TableProps = {
-  showBarplot: boolean,
-  itemsPerPage: number,
-};
-```
-
-### localConfigSchema
-
-Here are the parameters set that can be changed by a user. These values are displayed in the settings panel. It needs to be in the form of a reusable input component.
-
-Example:
-
-```jsx
-const localConfigSchema: localConfigSchemaType = {
-  showBarplot: {
-    value: true,
-    type: 'boolean',
-    label: 'Show barplot',
-  },
-  itemsPerPage: {
-    value: 10,
-    type: 'dropdown',
-    label: 'Items per page',
-    options: [10, 20, 30],
-  },
-};
-```
-
-#### Conditional rendering
-
-It is possible to only show certain settings based on a condition.
-
-```jsx
-const localConfigSchema: localConfigSchemaType = {
-  showBarplot: {
-    value: true,
-    type: 'boolean',
-    label: 'Show barplot',
-  },
-  itemsPerPage: {
-    value: 10,
-    type: 'dropdown',
-    label: 'Items per page',
-    options: [10, 20, 30],
-    condition: (localConf) => localConf.showBarplot.value === false,
-  },
-};
-```
-
-### VisualizationComponent
-
-The visualization component is what gets exported and is used in the factory. It must have a displayName, VIS and localConfigSchema.
-
-## Props
-
-### Data prop
-
-Contains the queried data from the database.
-
-### Schema prop
-
-Contains the database schema.
-
-### ML prop
-
-Contains the Machine-Learning results.
-
-### Dispatch prop
-
-Contains the dispatch for the redux state.
-
-### Global config prop
-
-Contains the global configurations for the panel that is the same for every visualization.
-
-### Local config prop
-
-Contains the local config props that are unique per visualization.
diff --git a/libs/shared/lib/vis/index.ts b/libs/shared/lib/vis/index.ts
new file mode 100644
index 000000000..92ed4302d
--- /dev/null
+++ b/libs/shared/lib/vis/index.ts
@@ -0,0 +1 @@
+export * from './visualizationPanel';
diff --git a/libs/shared/lib/vis/index.tsx b/libs/shared/lib/vis/index.tsx
deleted file mode 100644
index cb72178b7..000000000
--- a/libs/shared/lib/vis/index.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import React, { Suspense, useEffect, useState } from 'react';
-import { useAppDispatch } from '@graphpolaris/shared/lib/data-access';
-import { addVisualization } from '../data-access/store/visualizationSlice';
-import {
-  useGraphQueryResult,
-  useML,
-  useSchemaGraph,
-  useVisualizationSettings,
-  useVisualizationState,
-} from '@graphpolaris/shared/lib/data-access/store/hooks';
-import { localConfigPropTypes, globalConfigPropTypes, VISComponentType } from './types';
-
-// export * from './rawjsonvis';
-// export * from './nodelink/nodelinkvis';
-// export * from './paohvis/paohvis';
-// export * from './semanticsubstrates/semanticsubstrates';
-// export * from './table_vis/tableVis';
-// export * from './mapvis/mapvis';
-
-export const Visualizations: Record<string, Function> = {
-  TableVis: () => import('./visualizations/table_vis/tableVis'),
-  PaohVis: () => import('./visualizations/paohvis/paohvis'),
-  RawJSONVis: () => import('./visualizations/rawjsonvis/rawjsonvis'),
-  NodeLinkVis: () => import('./visualizations/nodelink/index'),
-  // MapVis: () => import(''),
-  MatrixVis: () => import('./visualizations/matrix/matrixvis'),
-};
-
-export const VisualizationComponent = () => {
-  const dispatch = useAppDispatch();
-  const vis = useVisualizationState();
-  const settings = useVisualizationSettings();
-  const graphQueryResult = useGraphQueryResult();
-  const schema = useSchemaGraph();
-  const ml = useML();
-
-  const [visualizationComponent, setVisualizationComponent] = useState<VISComponentType>();
-
-  useEffect(() => {
-    if (vis.activeVisualization && vis.activeVisualization in Visualizations) {
-      Visualizations[vis.activeVisualization]().then((r: any) => {
-        setVisualizationComponent(r.default);
-      });
-    }
-  }, [vis.activeVisualization]);
-
-  useEffect(() => {
-    if (visualizationComponent) {
-      dispatch(addVisualization({ id: visualizationComponent.displayName, settings: visualizationComponent.localConfigSchema }));
-    }
-  }, [vis.activeVisualization]);
-
-  if (!VisualizationComponent) {
-    return <div className="w-full h-full flex items-center justify-center">Visualization not found</div>;
-  }
-
-  const globalConfig: globalConfigPropTypes =
-    settings.general &&
-    Object.keys(settings.general).reduce((propsObject, val) => {
-      return {
-        ...propsObject,
-        [val]: vis.settings.general[val].value,
-      };
-    }, {});
-
-  const displayName = vis.activeVisualization as keyof typeof Visualizations;
-
-  const localConfig: localConfigPropTypes<typeof displayName> =
-    settings[vis.activeVisualization] &&
-    (Object.keys(settings[vis.activeVisualization]).reduce((propsObject, val) => {
-      return {
-        ...propsObject,
-        [val]: vis.settings[vis.activeVisualization][val].value,
-      };
-    }, {}) as localConfigPropTypes<typeof displayName>);
-
-  return (
-    localConfig &&
-    visualizationComponent && (
-      <div className="w-full h-full">
-        <visualizationComponent.VIS
-          data={graphQueryResult}
-          schema={schema}
-          ml={ml}
-          dispatch={dispatch}
-          globalConfig={globalConfig}
-          localConfig={localConfig}
-        />
-      </div>
-    )
-  );
-};
diff --git a/libs/shared/lib/vis/panel/dialog.tsx b/libs/shared/lib/vis/panel/dialog.tsx
deleted file mode 100644
index 5902931be..000000000
--- a/libs/shared/lib/vis/panel/dialog.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import { FormActions, FormBody, FormCard, FormControl, FormDiv, FormHBar, FormTitle } from '@graphpolaris/shared/lib/components/forms';
-import { DialogProps } from '@graphpolaris/shared/lib/components/Dialog';
-import { useAppDispatch, useVisualizationState } from '@graphpolaris/shared/lib/data-access';
-import { updateGeneralSettings, updateVisualizationSettings } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
-import Input from '@graphpolaris/shared/lib/components/inputs';
-import { globalConfigSchemaTypes, localConfigSchemaType } from '@graphpolaris/shared/lib/vis/types';
-
-export default function VisualizationDialog(props: DialogProps) {
-  const dispatch = useAppDispatch();
-  const vis = useVisualizationState();
-  const [settingsGeneral, setSettingsGeneral] = useState<globalConfigSchemaTypes>(vis.settings.general);
-  const [settingsSpecific, setSettingsSpecific] = useState<localConfigSchemaType>(vis.settings[vis.activeVisualization]);
-
-  useEffect(() => {
-    if (vis.settings.general !== settingsGeneral) {
-      dispatch(updateGeneralSettings(settingsGeneral));
-    }
-  }, [settingsGeneral]);
-
-  useEffect(() => {
-    if (vis.settings[vis.activeVisualization] !== settingsSpecific) {
-      dispatch(
-        updateVisualizationSettings({
-          id: vis.activeVisualization,
-          settings: settingsSpecific,
-        }),
-      );
-    }
-  }, [settingsSpecific]);
-
-  useEffect(() => {
-    setSettingsSpecific(vis.settings[vis.activeVisualization]);
-  }, [vis.settings[vis.activeVisualization]]);
-
-  return (
-    <>
-      {props.open && (
-        <FormDiv>
-          <FormCard>
-            <FormBody
-              onSubmit={(e) => {
-                e.preventDefault();
-                props.onClose();
-              }}
-            >
-              <FormTitle title="Settings" onClose={props.onClose} />
-              <FormHBar />
-
-              {settingsGeneral && (
-                <>
-                  {Object.keys(settingsGeneral).map((val) => (
-                    <FormControl key={val}>
-                      <Input
-                        {...settingsGeneral[val]}
-                        value={vis.settings.general[val].value as any}
-                        onChange={(value: any) => {
-                          setSettingsGeneral((prevSettings) => ({
-                            ...prevSettings,
-                            [val]: { ...prevSettings[val], value: value },
-                          }));
-                        }}
-                      />
-                    </FormControl>
-                  ))}
-                </>
-              )}
-
-              {Object.keys(settingsSpecific).length > 0 && (
-                <>
-                  <FormHBar />
-                  {Object.keys(settingsSpecific).map((val) => {
-                    const currentSetting = settingsSpecific[val];
-                    const shouldShowSetting = currentSetting.condition ? currentSetting.condition?.(settingsSpecific) : true;
-                    return (
-                      shouldShowSetting && (
-                        <FormControl key={val}>
-                          <Input
-                            {...settingsSpecific[val]}
-                            value={settingsSpecific[val]?.value as any}
-                            onChange={(value: any) => {
-                              setSettingsSpecific((prevSettings) => ({
-                                ...prevSettings,
-                                [val]: { ...prevSettings[val], value: value },
-                              }));
-                            }}
-                          />
-                        </FormControl>
-                      )
-                    );
-                  })}
-                </>
-              )}
-
-              <FormHBar />
-              <FormActions onClose={props.onClose} />
-            </FormBody>
-          </FormCard>
-        </FormDiv>
-      )}
-    </>
-  );
-}
diff --git a/libs/shared/lib/vis/panel/index.ts b/libs/shared/lib/vis/panel/index.ts
deleted file mode 100644
index bda371666..000000000
--- a/libs/shared/lib/vis/panel/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './visualization';
-
-import { VisualizationPanel } from './visualization';
-export default VisualizationPanel;
diff --git a/libs/shared/lib/vis/panel/visualization.tsx b/libs/shared/lib/vis/panel/visualization.tsx
deleted file mode 100644
index 67557ff45..000000000
--- a/libs/shared/lib/vis/panel/visualization.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import React, { useState } from 'react';
-import { useAppDispatch, useGraphQueryResult, useQuerybuilderGraph, useVisualizationState } from '@graphpolaris/shared/lib/data-access';
-import { LoadingSpinner } from '@graphpolaris/shared/lib/components/LoadingSpinner';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
-import { DropdownItem, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns';
-import ControlContainer from '@graphpolaris/shared/lib/components/controls';
-import { Button } from '@graphpolaris/shared/lib/components/buttons';
-import { VisualizationComponent } from '../';
-import VisualizationDialog from './dialog';
-import { Settings, Apps } from '@mui/icons-material';
-
-export const VisualizationPanel = () => {
-  const vis = useVisualizationState();
-  const graphQueryResult = useGraphQueryResult();
-  const query = useQuerybuilderGraph();
-  const dispatch = useAppDispatch();
-  const [visDropdownOpen, setVisDropdownOpen] = useState<boolean>(false);
-  const [showVisSettings, setShowVisSettings] = useState<boolean>(false);
-
-  return (
-    <div className="vis-panel h-full w-full overflow-y-auto" style={graphQueryResult.nodes.length === 0 ? { overflow: 'hidden' } : {}}>
-      <VisualizationDialog open={showVisSettings} onClose={() => setShowVisSettings(false)} />
-      <div className="sticky top-0 flex items-center justify-between z-[2] py-0 px-2 bg-secondary-100  border-b border-secondary-200">
-        <h1 className="text-xs font-semibold text-secondary-800">{vis.activeVisualization} visualization</h1>
-        <ControlContainer>
-          <Button
-            type="secondary"
-            variant="ghost"
-            size="xs"
-            iconComponent={<Settings />}
-            onClick={() => {
-              setShowVisSettings(!showVisSettings);
-            }}
-          />
-          <Button
-            type="secondary"
-            variant="ghost"
-            size="xs"
-            iconComponent={<Apps />}
-            onClick={() => {
-              setVisDropdownOpen(!visDropdownOpen);
-            }}
-          />
-          {visDropdownOpen && (
-            <DropdownItemContainer align="top-6 right-6">
-              {Object.keys(Visualizations).map((key) => (
-                <DropdownItem
-                  key={key}
-                  value={key}
-                  onClick={() => {
-                    setVisDropdownOpen(false);
-                    dispatch(setActiveVisualization(Visualizations[key as keyof typeof Visualizations]));
-                  }}
-                />
-              ))}
-            </DropdownItemContainer>
-          )}
-        </ControlContainer>
-      </div>
-
-      {graphQueryResult.queryingBackend ? (
-        <div className="w-full h-full flex flex-col items-center justify-center overflow-hidden">
-          <LoadingSpinner>Querying backend...</LoadingSpinner>
-        </div>
-      ) : graphQueryResult.nodes.length === 0 ? (
-        <div className="w-full h-full flex flex-col items-center justify-center">
-          <p>No data available to be shown</p>
-          {query.nodes.length > 0 ? <p>Query resulted in empty dataset</p> : <p>Query for data to visualize</p>}
-        </div>
-      ) : (
-        <div className="w-full h-full">
-          <VisualizationComponent />
-        </div>
-      )}
-    </div>
-  );
-};
diff --git a/libs/shared/lib/vis/shared/ResultNodeLinkParserUseCase.tsx b/libs/shared/lib/vis/shared/ResultNodeLinkParserUseCase.tsx
index 965d7a1d3..611d8d120 100644
--- a/libs/shared/lib/vis/shared/ResultNodeLinkParserUseCase.tsx
+++ b/libs/shared/lib/vis/shared/ResultNodeLinkParserUseCase.tsx
@@ -3,7 +3,7 @@
  * Utrecht University within the Software Project course.
  * © Copyright Utrecht University (Department of Information and Computing Sciences)
  */
-import { GraphType, LinkType, NodeType } from '../visualizations/nodelink/types';
+import { GraphType, LinkType, NodeType } from '../visualizations/nodelinkvis/types';
 import { Edge, Node, GraphQueryResult } from '../../data-access/store';
 import { ML } from '../../data-access/store/mlSlice';
 /** ResultNodeLinkParserUseCase implements methods to parse and translate websocket messages from the backend into a GraphType. */
diff --git a/libs/shared/lib/vis/types.ts b/libs/shared/lib/vis/types.ts
index ef3523875..cf79a63d5 100644
--- a/libs/shared/lib/vis/types.ts
+++ b/libs/shared/lib/vis/types.ts
@@ -1,49 +1,39 @@
-import { NodeLinkProps, PaohVisProps, RawJSONVisProps, SemanticSubstratesProps, TableProps } from './visualizations';
 import { GraphQueryResult } from '../data-access/store/graphQueryResultSlice';
 import { ML } from '../data-access/store/mlSlice';
 import { SchemaGraph } from '../schema';
 import type { AppDispatch } from '../data-access';
 import { InputProps } from '../components/inputs';
 import { FC } from 'react';
-import { Visualizations } from '.';
+import { EncodingProps, EncodingTypes } from './configuration/encodings';
+import { SettingProps, SettingTypes } from './configuration/settings';
+import { InteractionProps, InteractionTypes } from './configuration/interactions';
+import { Visualizations } from './visualizationManager';
 
-export type settingConifgTypes = InputProps;
+export type globalConfigTypes = { [id: string]: InputProps };
 
-export type globalConfigSchemaTypes = {
-  [id: string]: settingConifgTypes;
-};
-
-export type globalConfigPropTypes = {
-  [id: string]: any;
-};
+export type globalConfigPropTypes = { [K in keyof globalConfigTypes]: any };
 
-export type localConfigSchemaType = {
-  [id: string]: settingConifgTypes & { condition?: (config: Record<string, any>) => boolean };
+export type VisualizationConfiguration = {
+  settings?: SettingTypes;
+  encodings?: EncodingTypes;
+  interactions?: InteractionTypes;
 };
 
-export type localConfigPropTypes<T extends keyof typeof Visualizations> = T extends 'TableVis'
-  ? TableProps
-  : T extends 'PaohVis'
-    ? PaohVisProps
-    : T extends 'RawJSONVis'
-      ? RawJSONVisProps
-      : T extends 'SemanticSubstrates'
-        ? SemanticSubstratesProps
-        : T extends 'NodeLinkVis'
-          ? NodeLinkProps
-          : never;
-
 export type VISComponentType = {
-  displayName: string;
-  VIS: any;
-  localConfigSchema: localConfigSchemaType;
+  displayName: keyof typeof Visualizations;
+  VIS: FC<any>;
+  settings?: SettingTypes;
+  encodings?: EncodingTypes;
+  interactions?: InteractionTypes;
 };
 
-export type VisualizationPropTypes<T extends keyof typeof Visualizations> = {
+export type VisualizationPropTypes = {
   data: GraphQueryResult;
   schema: SchemaGraph;
   ml: ML;
   dispatch: AppDispatch;
   globalConfig: globalConfigPropTypes;
-  localConfig: localConfigPropTypes<T>;
+  settings: SettingProps;
+  encodings?: EncodingProps;
+  interactions?: InteractionProps;
 };
diff --git a/libs/shared/lib/vis/visualizationManager.tsx b/libs/shared/lib/vis/visualizationManager.tsx
new file mode 100644
index 000000000..670371440
--- /dev/null
+++ b/libs/shared/lib/vis/visualizationManager.tsx
@@ -0,0 +1,108 @@
+import React, { useEffect, useMemo, useState } from 'react';
+import { useAppDispatch } from '@graphpolaris/shared/lib/data-access';
+import { addVisualization } from '../data-access/store/visualizationSlice';
+import {
+  useGraphQueryResult,
+  useML,
+  useSchemaGraph,
+  useVisualizationSettings,
+  useVisualizationState,
+} from '@graphpolaris/shared/lib/data-access/store/hooks';
+import { VISComponentType, globalConfigPropTypes } from './types';
+
+export const Visualizations: Record<string, Function> = {
+  TableVis: () => import('./visualizations/tablevis/tablevis'),
+  PaohVis: () => import('./visualizations/paohvis/paohvis'),
+  RawJSONVis: () => import('./visualizations/rawjsonvis/rawjsonvis'),
+  NodeLinkVis: () => import('./visualizations/nodelinkvis/nodelinkvis'),
+  // MapVis: () => import(''),
+  MatrixVis: () => import('./visualizations/matrixvis/matrixvis'),
+};
+
+export const VisualizationManager = () => {
+  const dispatch = useAppDispatch();
+  const vis = useVisualizationState();
+  const settings = useVisualizationSettings();
+  const graphQueryResult = useGraphQueryResult();
+  const schema = useSchemaGraph();
+  const ml = useML();
+
+  const [visualizationComponent, setVisualizationComponent] = useState<VISComponentType>();
+
+  useEffect(() => {
+    if (vis.activeVisualization && vis.activeVisualization in Visualizations) {
+      Visualizations[vis.activeVisualization]().then((r: any) => {
+        setVisualizationComponent(r.default);
+      });
+    }
+  }, [vis.activeVisualization]);
+
+  useEffect(() => {
+    if (visualizationComponent) {
+      const { displayName, settings = {}, encodings = {}, interactions = {} } = visualizationComponent;
+      dispatch(addVisualization({ id: displayName, settings, encodings, interactions }));
+    }
+  }, [vis.activeVisualization]);
+
+  if (!visualizationComponent) {
+    return <></>;
+  }
+
+  const globalConfig: globalConfigPropTypes = settings.general
+    ? Object.keys(settings.general).reduce((propsObject, val) => {
+        return {
+          ...propsObject,
+          [val]: vis.settings.general[val].value,
+        };
+      }, {})
+    : {};
+
+  let visSettings = {};
+  let visEncodings = {};
+  let visInteractions = {};
+
+  if (settings[vis.activeVisualization]) {
+    visSettings = Object.keys(settings[vis.activeVisualization]?.settings ?? {}).reduce((propsObject, val) => {
+      return {
+        ...propsObject,
+        [val]: vis.settings[vis.activeVisualization]?.settings?.[val]?.value,
+      };
+    }, {});
+
+    visEncodings = Object.keys(settings[vis.activeVisualization]?.encodings ?? {}).reduce((propsObject, val) => {
+      return {
+        ...propsObject,
+        [val]: vis.settings[vis.activeVisualization]?.encodings?.[val]?.marking,
+      };
+    }, {});
+
+    visInteractions = Object.keys(settings[vis.activeVisualization]?.interactions ?? {}).reduce((propsObject, val) => {
+      return {
+        ...propsObject,
+        [val]: vis.settings[vis.activeVisualization]?.interactions?.[val]?.value,
+      };
+    }, {});
+  }
+
+  try {
+    return (
+      visSettings &&
+      visualizationComponent && (
+        <div className="w-full h-full">
+          <visualizationComponent.VIS
+            data={graphQueryResult}
+            schema={schema}
+            ml={ml}
+            dispatch={dispatch}
+            globalConfig={globalConfig}
+            settings={visSettings}
+            encodings={visEncodings}
+            interactions={visInteractions}
+          />
+        </div>
+      )
+    );
+  } catch (error) {
+    return <div className="w-full h-full flex items-center justify-center">Something went wrong in the visualization component.</div>;
+  }
+};
diff --git a/apps/web/src/app/panels/Visualization.tsx b/libs/shared/lib/vis/visualizationPanel.tsx
similarity index 60%
rename from apps/web/src/app/panels/Visualization.tsx
rename to libs/shared/lib/vis/visualizationPanel.tsx
index 3a28806cb..0cb6f554c 100644
--- a/apps/web/src/app/panels/Visualization.tsx
+++ b/libs/shared/lib/vis/visualizationPanel.tsx
@@ -1,21 +1,41 @@
-import React, { useState } from 'react';
-import { useAppDispatch, useGraphQueryResult, useQuerybuilderGraph, useVisualizationState } from '@graphpolaris/shared/lib/data-access';
+import React, { useState, useRef, useEffect } from 'react';
+import {
+  useAppDispatch,
+  useGraphQueryResult,
+  useQuerybuilderGraph,
+  useSessionCache,
+  useVisualizationState,
+} from '@graphpolaris/shared/lib/data-access';
 import { LoadingSpinner } from '@graphpolaris/shared/lib/components/LoadingSpinner';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 import { DropdownItem, DropdownItemContainer } from '@graphpolaris/shared/lib/components/dropdowns';
 import ControlContainer from '@graphpolaris/shared/lib/components/controls';
 import { Button } from '@graphpolaris/shared/lib/components/buttons';
-import { VisualizationComponent } from '@graphpolaris/shared/lib/vis';
-import VisualizationDialog from './VisualizationDialog';
+
+import { VisualizationDialog } from './configuration';
 import { Settings as SettingsIcon, Apps as AppsIcon } from '@mui/icons-material';
+import { VisualizationManager, Visualizations } from './visualizationManager';
 
 export const VisualizationPanel = () => {
-  const vis = useVisualizationState();
   const graphQueryResult = useGraphQueryResult();
   const query = useQuerybuilderGraph();
   const dispatch = useAppDispatch();
+  const vis = useVisualizationState();
   const [visDropdownOpen, setVisDropdownOpen] = useState<boolean>(false);
   const [showVisSettings, setShowVisSettings] = useState<boolean>(false);
+  const visDropdownRef = useRef<HTMLDivElement>(null);
+
+  useEffect(() => {
+    const handleClickOutside = (event: MouseEvent) => {
+      if (visDropdownRef.current && !visDropdownRef.current.contains(event.target as Node)) {
+        setVisDropdownOpen(false);
+      }
+    };
+    if (visDropdownOpen) document.addEventListener('mousedown', handleClickOutside);
+    return () => {
+      document.removeEventListener('mousedown', handleClickOutside);
+    };
+  }, [visDropdownOpen]);
 
   return (
     <div className="vis-panel h-full w-full overflow-y-auto" style={graphQueryResult.nodes.length === 0 ? { overflow: 'hidden' } : {}}>
@@ -29,7 +49,8 @@ export const VisualizationPanel = () => {
             size="xs"
             iconComponent={<SettingsIcon />}
             onClick={() => {
-              setShowVisSettings(!showVisSettings);
+              // TODO
+              // setShowVisSettings(!showVisSettings);
             }}
           />
           <Button
@@ -42,18 +63,20 @@ export const VisualizationPanel = () => {
             }}
           />
           {visDropdownOpen && (
-            <DropdownItemContainer align="top-6 right-6">
-              {Object.keys(Visualizations).map((key) => (
-                <DropdownItem
-                  key={key}
-                  value={key}
-                  onClick={() => {
-                    setVisDropdownOpen(false);
-                    dispatch(setActiveVisualization(Visualizations[key as keyof typeof Visualizations]));
-                  }}
-                />
-              ))}
-            </DropdownItemContainer>
+            <div ref={visDropdownRef}>
+              <DropdownItemContainer align="top-6 right-6">
+                {Object.keys(Visualizations).map((key) => (
+                  <DropdownItem
+                    key={key}
+                    value={key}
+                    onClick={() => {
+                      setVisDropdownOpen(false);
+                      dispatch(setActiveVisualization(key));
+                    }}
+                  />
+                ))}
+              </DropdownItemContainer>
+            </div>
           )}
         </ControlContainer>
       </div>
@@ -69,7 +92,7 @@ export const VisualizationPanel = () => {
         </div>
       ) : (
         <div className="w-full h-full">
-          <VisualizationComponent />
+          <VisualizationManager />
         </div>
       )}
     </div>
diff --git a/libs/shared/lib/vis/visualizations/documentation.mdx b/libs/shared/lib/vis/visualizations/documentation.mdx
new file mode 100644
index 000000000..74c0fd8ac
--- /dev/null
+++ b/libs/shared/lib/vis/visualizations/documentation.mdx
@@ -0,0 +1,89 @@
+import { Meta, Unstyled } from '@storybook/blocks';
+
+<Meta title="Visualizations/Implementation" />
+
+# Visualization documentation
+
+## Contents
+
+1. [Introduction](#Introduction)
+2. [Overview](#Overview)
+3. [Props](#Props)
+
+## Introduction
+
+This is the documentation for implementing visualizations within the GraphPolaris system.
+
+## Overview
+
+The basic code implementation looks like this:
+
+```jsx
+import React from 'react';
+import { VisualizationPropTypes, VISComponentType } from '@graphpolaris/shared/lib/vis/Types';
+
+/**
+ * Visualization Component
+ * @param {object} props - Props for the Visualization component.
+ * @param {GraphQueryResult} props.data - Queried data from the database.
+ * @param {object} props.schema - Database schema.
+ * @param {any} props.ml - Machine-learning results.
+ * @param {function} props.dispatch - Redux dispatch for state management.
+ * @param {object} props.globalConfig - Global configurations for the panel (applicable to all visualizations).
+ * @param {object} props.settings - Local configuration props unique to this visualization.
+ * @param {object} props.encodings - data attributes mapped to visual variables.
+ * @returns {JSX.Element} - Rendered content of the visualization.
+ */
+export const Visualization = ({ data, schema, ml, dispatch, globalConfig, settings, encodings }) => {
+  // Perform additional processing specific to this visualization using the provided props
+
+  return (
+    // Rendered visualization content
+  );
+};
+
+/**
+ * Visualization Component Object
+ * Contains information and configurations for the exported visualization component.
+ */
+export const VisualizationComponent: VISComponentType = {
+  displayName: 'Visualization',
+  VIS: Visualization,
+  settings: {},
+  encodings: {},
+};
+```
+
+### VisualizationComponent
+
+The visualization component is what gets exported and is used in the visualization manager. It must have a displayName and VIS.
+
+## Props
+
+### Data prop
+
+Contains the queried data from the database.
+
+### Schema prop
+
+Contains the database schema.
+
+### ML prop
+
+Contains the Machine-Learning results.
+
+### Dispatch prop
+
+Contains the dispatch for the redux state.
+
+### Global config prop
+
+Contains the global configurations for the panel that is the same for every visualization.
+
+### Settings prop
+
+Contains the local config props that are unique per visualization.
+
+### Encodings prop
+
+Contains the encodings for the visualization.
diff --git a/libs/shared/lib/vis/visualizations/index.tsx b/libs/shared/lib/vis/visualizations/index.tsx
index 2ba6e220c..e69de29bb 100644
--- a/libs/shared/lib/vis/visualizations/index.tsx
+++ b/libs/shared/lib/vis/visualizations/index.tsx
@@ -1,6 +0,0 @@
-export * from './rawjsonvis';
-export * from './nodelink';
-export * from './paohvis';
-export * from './semanticsubstrates';
-export * from './table_vis';
-export * from './matrix';
diff --git a/libs/shared/lib/vis/visualizations/mapvis/components/MapPanel.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/MapPanel.tsx
index 2d27524f0..877464ba4 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/components/MapPanel.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/components/MapPanel.tsx
@@ -112,7 +112,7 @@ export function MapPanel({ graph, layers, showFilter, setShowFilter }: Props) {
         onClick={handleSelect}
         onHover={({ object }) => setHoverObject(object !== undefined ? object : null)}
       />
-      <SelectedMenu selected={selected} />
+      {/* <SelectedMenu selected={selected} />
       <SecondaryMenu
         mapSize={mapSize}
         setMapSize={setMapSize}
@@ -120,7 +120,7 @@ export function MapPanel({ graph, layers, showFilter, setShowFilter }: Props) {
         flyToBoundingBox={flyToBoundingBox}
         setSelectingRectangle={setSelectingRectangle}
         selectingRectangle={selectingRectangle}
-      />
+      /> */}
       {showFilter && <FilterMenu graph={graph} setShowFilter={setShowFilter} />}
     </div>
   );
diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.stories.tsx b/libs/shared/lib/vis/visualizations/mapvis/mapvis.stories.tsx
index 92ebf0d7e..9f9138d21 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.stories.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.stories.tsx
@@ -1,6 +1,6 @@
 import React from 'react';
 import { Meta } from '@storybook/react';
-import { VisualizationPanel } from '../../panel/visualization';
+import { VisualizationPanel } from '../../visualizationPanel';
 import { Provider } from 'react-redux';
 import { configureStore } from '@reduxjs/toolkit';
 import {
@@ -11,7 +11,7 @@ import {
   visualizationSlice,
 } from '../../../data-access/store';
 import { mockMobilityQueryResult, bigMockQueryResults } from '../../../mock-data';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 
 const Component: Meta<typeof VisualizationPanel> = {
   title: 'Visualizations/MapVis',
@@ -45,7 +45,7 @@ export const DutchVehicleTheft = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: mockMobilityQueryResult } }));
-    // dispatch(setActiveVisualization(Visualizations.MapVis));
+    dispatch(setActiveVisualization('MapVis'));
   },
 };
 
@@ -53,7 +53,7 @@ export const AmericanFlights = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: bigMockQueryResults } }));
-    // dispatch(setActiveVisualization(Visualizations.MapVis));
+    dispatch(setActiveVisualization('MapVis'));
   },
 };
 
diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx
index 65dd5a40b..784bb118c 100644
--- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx
+++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.tsx
@@ -2,13 +2,11 @@ import React from 'react';
 import { MapPanel, LayerPanel } from './components';
 import GraphModel from './graphModel';
 import { GraphType, Layer } from './Types';
-import { VISComponentType, VisualizationPropTypes, localConfigSchemaType } from '../../types';
+import { VISComponentType, VisualizationPropTypes } from '../../types';
 
 export type MapProps = {};
 
-const displayName = 'MapVis';
-
-export default function MapVis({ data, schema, localConfig }: VisualizationPropTypes<typeof displayName>) {
+export default function MapVis({ data, schema, settings }: VisualizationPropTypes) {
   const [layers, setLayers] = React.useState<Layer[]>([]);
   const [showFilter, setShowFilter] = React.useState<boolean>(false);
 
@@ -24,17 +22,15 @@ export default function MapVis({ data, schema, localConfig }: VisualizationPropT
   }
 
   return (
-    <div className="flex flex-row justify-between overflow-hidden w-screen h-screen font-sans">
+    <div className="flex flex-row justify-between overflow-hidden h-full w-full font-sans">
       <MapPanel graph={graph} layers={layers} showFilter={showFilter} setShowFilter={setShowFilter} />
-      <LayerPanel layers={layers} setLayers={setLayers} graphInfo={graph.getGraphInfo()} setShowFilter={setShowFilter} />
+      {/* <LayerPanel layers={layers} setLayers={setLayers} graphInfo={graph.getGraphInfo()} setShowFilter={setShowFilter} /> */}
     </div>
   );
 }
 
-const localConfigSchema: localConfigSchemaType = {};
-
-export const TableComponent: VISComponentType = {
-  displayName: displayName,
+export const MapComponent: VISComponentType = {
+  displayName: 'Map',
   VIS: MapVis,
-  localConfigSchema: localConfigSchema,
+  settings: {},
 };
diff --git a/libs/shared/lib/vis/visualizations/matrix/matrixvis.tsx b/libs/shared/lib/vis/visualizations/matrix/matrixvis.tsx
deleted file mode 100644
index 5bfd89dcc..000000000
--- a/libs/shared/lib/vis/visualizations/matrix/matrixvis.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import React, { useEffect, useRef, useState } from 'react';
-import { useImmer } from 'use-immer';
-import { GraphQueryResult, useGraphQueryResult } from '../../../data-access/store';
-import { ML } from '../../../data-access/store/mlSlice';
-import { LinkType, NodeType } from './Types';
-import { MatrixPixi } from './components/MatrixPixi';
-import { VisualizationPropTypes, VISComponentType, localConfigSchemaType } from '../../types';
-
-interface Props {
-  loading?: boolean;
-  // currentColours: any;
-}
-
-// const displayName = 'Matrix Visualization';
-
-export const MatrixVis = React.memo(({ data, ml, dispatch, localConfig }: VisualizationPropTypes<typeof displayName>) => {
-  const ref = useRef<HTMLDivElement>(null);
-  const [graph, setGraph] = useImmer<GraphQueryResult | undefined>(undefined);
-  const [highlightNodes, setHighlightNodes] = useState<NodeType[]>([]);
-  const [highlightedLinks, setHighlightedLinks] = useState<LinkType[]>([]);
-
-  useEffect(() => {
-    if (data) {
-      setGraph(
-        data,
-        // parseQueryResult(graphQueryResult, ml, {
-        //   defaultX: (ref.current?.clientWidth || 1000) / 2,
-        //   defaultY: (ref.current?.clientHeight || 1000) / 2,
-        // })
-      );
-    }
-  }, [data, ml]);
-
-  return (
-    <>
-      <MatrixPixi graph={graph} highlightNodes={highlightNodes} highlightedLinks={highlightedLinks} localConfig={localConfig} />
-
-      {/* <VisConfigPanelComponent> */}
-      {/* <NodeLinkConfigPanelComponent
-               graph={this.state.graph}
-               nlViewModel={this.nodeLinkViewModel}
-            /> */}
-      {/*</VisConfigPanelComponent>*/}
-      {/*<VisConfigPanelComponent isLeft>*/}
-      {/*  <AttributesConfigPanel nodeLinkViewModel={this.nodeLinkViewModel} />*/}
-      {/* </VisConfigPanelComponent> */}
-    </>
-  );
-});
-
-const displayName = 'MatrixVis';
-const localConfigSchema: localConfigSchemaType = {
-  marks: {
-    type: 'dropdown',
-    options: ['rect', 'circle'],
-    value: 'rect',
-    label: 'Configure Marks',
-  },
-};
-
-export const MatrixVisComponent: VISComponentType = {
-  displayName: displayName,
-  VIS: MatrixVis,
-  localConfigSchema: localConfigSchema,
-};
-export default MatrixVisComponent;
diff --git a/libs/shared/lib/vis/visualizations/matrix/Types.tsx b/libs/shared/lib/vis/visualizations/matrixvis/Types.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/Types.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/Types.tsx
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/BitmapTextLabel.ts b/libs/shared/lib/vis/visualizations/matrixvis/components/BitmapTextLabel.ts
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/components/BitmapTextLabel.ts
rename to libs/shared/lib/vis/visualizations/matrixvis/components/BitmapTextLabel.ts
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/ColumnGraphicsComponent.tsx b/libs/shared/lib/vis/visualizations/matrixvis/components/ColumnGraphicsComponent.tsx
similarity index 77%
rename from libs/shared/lib/vis/visualizations/matrix/components/ColumnGraphicsComponent.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/components/ColumnGraphicsComponent.tsx
index facb7ff10..7226749f9 100644
--- a/libs/shared/lib/vis/visualizations/matrix/components/ColumnGraphicsComponent.tsx
+++ b/libs/shared/lib/vis/visualizations/matrixvis/components/ColumnGraphicsComponent.tsx
@@ -50,14 +50,9 @@ export const createColumn = (
       gfx.drawRect(0, i * cellWidth, cellWidth, cellHeight);
       gfx.endFill();
     } else {
-      // console.log('inOutEdge', inOutEdge, Math.min(inOutEdge.length, 1), currentVisMapping.colorScale(Math.min(inOutEdge.length, 1)));
-
       if (outEdges.length > 0) {
         const thisEdge = outEdges[0];
-        // console.log('inOutEdge', thisEdge);
-
         const value = byString(thisEdge, currentVisMapping.attribute);
-        // console.log('value', value);
 
         color = currentVisMapping.colorScale(value);
       }
@@ -67,25 +62,8 @@ export const createColumn = (
       gfx.drawRect(0, i * cellWidth, cellWidth, cellHeight);
       gfx.endFill();
     }
-
-    // if (inOutEdge.length > 0) {
-    //   color = fgCellColor;
-    // } else {
-    //   color = bgCellColor;
-    // }
   }
 
-  //   gfx.on('pointerdown', onButtonDown);
-  // .on('pointerup', onDragEnd).on('pointerupoutside', onDragEnd);
-
-  // for (let i = 0; i < edges.length; i++) {
-  //   const edge = edges[i];
-  //   // draw an rectangle with a fill
-  //   gfx.beginFill(0xff0000, 0.5);
-  //   gfx.drawRect(0, i * cellWidth, cellWidth, cellHeight * nodes.length);
-  //   gfx.endFill();
-  // }
-
   return gfx;
 };
 
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/ColumnLabelTrack.tsx b/libs/shared/lib/vis/visualizations/matrixvis/components/ColumnLabelTrack.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/components/ColumnLabelTrack.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/components/ColumnLabelTrack.tsx
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/ColumnSpriteComponent.tsx b/libs/shared/lib/vis/visualizations/matrixvis/components/ColumnSpriteComponent.tsx
similarity index 70%
rename from libs/shared/lib/vis/visualizations/matrix/components/ColumnSpriteComponent.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/components/ColumnSpriteComponent.tsx
index b04470376..6176f1d5b 100644
--- a/libs/shared/lib/vis/visualizations/matrix/components/ColumnSpriteComponent.tsx
+++ b/libs/shared/lib/vis/visualizations/matrixvis/components/ColumnSpriteComponent.tsx
@@ -11,7 +11,6 @@ export const createColumn = (
   cellWidth: number,
   cellHeight: number
 ) => {
-  // console.log('createColumn', id, nodes.length, edges.length);
   const sprite = new Sprite(Texture.WHITE);
   sprite.name = 'col_' + id;
   sprite.eventMode = 'static';
@@ -20,8 +19,6 @@ export const createColumn = (
   const edgesForThisColumn = edges.filter((edge) => {
     return edge.from === nodesCol[id].id || edge.to === nodesCol[id].id;
   });
-  // console.log('edgesForThisColumn', nodesCol[id].id, edgesForThisColumn);
-  // const color = new Color({ r: 255 * (id / nodesCol.length), g: 255, b: 255, a: 1 });
   const bgCellColor = dataColors.neutral['5'];
   const fgCellColor = tailwindColors.entity.DEFAULT;
   let color = bgCellColor;
@@ -47,14 +44,5 @@ export const createColumn = (
     // sprite.drawRect(0, i * cellWidth, cellWidth, cellHeight);
     // sprite.endFill();
   }
-  //   sprite.on('pointerdown', onButtonDown);
-  // .on('pointerup', onDragEnd).on('pointerupoutside', onDragEnd);
-  // for (let i = 0; i < edges.length; i++) {
-  //   const edge = edges[i];
-  //   // draw an rectangle with a fill
-  //   gfx.beginFill(0xff0000, 0.5);
-  //   gfx.drawRect(0, i * cellWidth, cellWidth, cellHeight * nodes.length);
-  //   gfx.endFill();
-  // }
   return sprite;
 };
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/MatrixExport.tsx b/libs/shared/lib/vis/visualizations/matrixvis/components/MatrixExport.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/components/MatrixExport.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/components/MatrixExport.tsx
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/MatrixPixi.tsx b/libs/shared/lib/vis/visualizations/matrixvis/components/MatrixPixi.tsx
similarity index 64%
rename from libs/shared/lib/vis/visualizations/matrix/components/MatrixPixi.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/components/MatrixPixi.tsx
index 4ed2df886..5c48d1f27 100644
--- a/libs/shared/lib/vis/visualizations/matrix/components/MatrixPixi.tsx
+++ b/libs/shared/lib/vis/visualizations/matrixvis/components/MatrixPixi.tsx
@@ -1,5 +1,4 @@
 import { Edge, GraphQueryResult, Node, useML, useSearchResultData } from '@graphpolaris/shared/lib/data-access';
-import { Cull } from '@pixi-essentials/cull';
 import { dataColors, tailwindColors } from 'config';
 import * as d3 from 'd3';
 import { Viewport } from 'pixi-viewport';
@@ -23,10 +22,9 @@ import { NLPopup } from './MatrixPopup';
 import { Actions, Interpolations } from 'pixi-actions';
 
 import Color from 'color';
-import { localConfigSchemaType } from '../../../types';
+import { SettingTypes } from '../../../configuration/settings';
 import { createColumn } from './ColumnGraphicsComponent';
 import { ReorderingManager } from './ReorderingManager';
-import { TextLabel } from './TextLabel';
 
 type Props = {
   // onClick: (node: NodeType, pos: IPointData) => void;
@@ -36,45 +34,34 @@ type Props = {
   currentShortestPathEdges?: LinkType[];
   highlightedLinks?: LinkType[];
   graph?: GraphQueryResult;
-  localConfig: localConfigSchemaType;
+  localConfig: SettingTypes;
 };
 
 const app = new Application({ background: 0xffffff, antialias: true, autoDensity: true, eventMode: 'auto' });
+const columnsContainer = new Container();
+const columnAxisContainer = new Container();
+const rowsContainer = new Container();
+
 //////////////////
 // MAIN COMPONENT
 //////////////////
-export const MatrixPixi = (props: Props) => {
-  const columnLayer = new Container();
-  const columnLabelLayer = new Container();
-  const rowsLabelLayer = new Container();
-
-  let lastZoom = Infinity;
 
+export const MatrixPixi = (props: Props) => {
   let config = {
     textOffsetX: 100,
     textOffsetY: 100,
     visMapping: [] as any[], // TODO type
 
-    CELL_HEIGHT: 50,
-    CELL_WIDTH: 50,
-
-    LABEL_FONT_FAMILY: 'Roboto',
-    LABEL_FILL: 0x000000,
-    LABEL_ALIGN: 'center',
-    LABEL_FONT_SIZE: 25,
-    LABEL_MARGIN: 4,
-    LABEL_MIN_WIDTH: 6,
-
-    ANIMATION_DURATION: 0.8,
+    cellHeight: 100,
+    cellWidth: 100,
   };
 
   let columnOrder: string[] = [];
   let rowOrder: string[] = [];
-  const [columnOrderState, setColumnOrderState] = useState<string[]>([]);
-  const [rowOrderState, setRowOrderState] = useState<string[]>([]);
 
   const [quickPopup, setQuickPopup] = useState<{ node: NodeType; pos: IPointData } | undefined>();
   const [popups, setPopups] = useState<{ node: NodeType; pos: IPointData }[]>([]);
+  // const [columnOrder, setColumnOrder] = useState<string[]>([]);
 
   const nodeMap = useRef(new Map<string, Graphics>());
   const linkMap = useRef(new Map<string, Graphics>());
@@ -84,8 +71,7 @@ export const MatrixPixi = (props: Props) => {
   const ml = useML();
   const searchResults = useSearchResultData();
 
-  const cull = new Cull();
-  let cullDirty = useRef(true);
+  // const imperative = useRef<any>(null);
 
   function resize() {
     const width = ref?.current?.clientWidth || 1000;
@@ -121,11 +107,8 @@ export const MatrixPixi = (props: Props) => {
     }
   }, [ref]);
 
-  // useEffect(() => {
-  //   console.log('newColumnOrder', columnOrder);
-  // }, [columnOrder]);
-
   useEffect(() => {
+    console.log('graph change');
     // console.log('graph changed', props.graph, ref.current, ref.current.children.length > 0, imperative.current);
     if (props.graph && ref.current && ref.current.children.length > 0) {
       if (!isSetup.current) setup();
@@ -134,6 +117,7 @@ export const MatrixPixi = (props: Props) => {
   }, [props.graph]);
 
   useEffect(() => {
+    console.log('config change');
     // console.log('graph changed', props.graph, ref.current, ref.current.children.length > 0, imperative.current);
     if (props.graph && ref.current && ref.current.children.length > 0) {
       setup();
@@ -161,7 +145,7 @@ export const MatrixPixi = (props: Props) => {
 
   function onButtonDown(event: FederatedPointerEvent) {
     console.log(
-      event.currentTarget,
+      event.currentTarget
       // graph.nodes.find((node) => node.id === event.currentTarget.name)
     );
   }
@@ -174,45 +158,45 @@ export const MatrixPixi = (props: Props) => {
       if (forceClear) {
         nodeMap.current.clear();
         linkMap.current.clear();
-        rowsLabelLayer.removeChildren();
-        columnLayer.removeChildren();
+        rowsContainer.removeChildren();
+        columnsContainer.removeChildren();
       }
     }
   };
 
   const onHoverColumn = (event: any, currentNode: Node | null) => {
-    app.renderer.events.cursorStyles.pointer = 'crosshair';
+    app.renderer.plugins.interaction.cursorStyles.pointer = 'crosshair';
     if (!currentNode) {
-      rowsLabelLayer.children.forEach((row) => {
+      rowsContainer.children.forEach((row) => {
         row.alpha = 1;
       });
-      columnLayer.children.forEach((col) => {
+      columnsContainer.children.forEach((col) => {
         col.alpha = 1;
       });
-      columnLabelLayer.children.forEach((colText) => {
+      columnAxisContainer.children.forEach((colText) => {
         colText.alpha = 1;
       });
 
       return;
     }
 
-    columnLayer.children.forEach((col) => {
+    columnsContainer.children.forEach((col) => {
       col.alpha = 0.25;
     });
     event.currentTarget.alpha = 1;
 
-    columnLabelLayer.children.forEach((colText) => {
+    columnAxisContainer.children.forEach((colText) => {
       colText.alpha = 0.25;
       if (colText.name?.endsWith(currentNode.id)) colText.alpha = 1;
     });
 
     if (!props.graph) return;
-    rowsLabelLayer.children.forEach((col) => {
+    rowsContainer.children.forEach((col) => {
       col.alpha = 0.25;
     });
 
     if (!props.graph) return;
-    rowsLabelLayer.children.forEach((col) => {
+    rowsContainer.children.forEach((col) => {
       col.alpha = 0.25;
     });
 
@@ -222,17 +206,17 @@ export const MatrixPixi = (props: Props) => {
     });
 
     edgesForThisColumn.forEach((edge) => {
-      let row = rowsLabelLayer.getChildByName(edge.to);
+      let row = rowsContainer.getChildByName(edge.to);
       if (row) row.alpha = 1;
 
-      row = rowsLabelLayer.getChildByName(edge.from);
+      row = rowsContainer.getChildByName(edge.from);
       if (row) row.alpha = 1;
     });
   };
 
   const reorderColumns = (columnOrder: string[]) => {
-    const columns = columnLayer.children;
-    const columnLabels = columnLabelLayer.children;
+    const columns = columnsContainer.children;
+    const columnsText = columnAxisContainer.children;
     let columnPositions: Point[] = [];
     for (let i = 0; i < columns.length; i++) {
       const col = columns[i];
@@ -240,14 +224,14 @@ export const MatrixPixi = (props: Props) => {
     }
 
     let columnTextPositions: Point[] = [];
-    for (let i = 0; i < columnLabels.length; i++) {
-      const col = columnLabels[i];
+    for (let i = 0; i < columnsText.length; i++) {
+      const col = columnsText[i];
       columnTextPositions.push(new Point(col.position.x, col.position.y));
     }
 
     if (columnTextPositions.length !== columnPositions.length)
       throw new Error(
-        'columnTextPositions and columnPositions have different length ' + columnTextPositions.length + ' / ' + columnPositions.length,
+        'columnTextPositions and columnPositions have different length ' + columnTextPositions.length + ' / ' + columnPositions.length
       );
 
     for (let i = 0; i < columns.length; i++) {
@@ -262,7 +246,7 @@ export const MatrixPixi = (props: Props) => {
 
       const newPositionText = columnTextPositions[index];
       // move column to new position
-      Actions.moveTo(columnLabels[i], newPositionText.x, newPositionText.y, 1, Interpolations.pow2out).play();
+      Actions.moveTo(columnsText[i], newPositionText.x, newPositionText.y, 1, Interpolations.pow2out).play();
     }
   };
 
@@ -273,9 +257,9 @@ export const MatrixPixi = (props: Props) => {
     rowOrder: string[],
     colorScale: any,
     cellWidth: number,
-    cellHeight: number,
+    cellHeight: number
   ) => {
-    const rows = rowsLabelLayer.children;
+    const rows = rowsContainer.children;
     let rowPositions: Point[] = [];
     for (let i = 0; i < rows.length; i++) {
       const row = rows[i];
@@ -293,44 +277,40 @@ export const MatrixPixi = (props: Props) => {
       Actions.moveTo(row, newPosition.x, newPosition.y, 1, Interpolations.pow2out).play();
     }
 
-    const colOrder = columnLayer.children.map((col) => col.name) as string[];
+    const colOrder = columnsContainer.children.map((col) => col.name) as string[];
     console.log('colOrder', colOrder);
     if (!colOrder) throw new Error('colOrder is undefined');
-    columnLayer.removeChildren();
+    columnsContainer.removeChildren();
     setupColumns(edges, colOrder, rowOrder);
   };
 
   const setup = () => {
     if (!props.graph) throw Error('Graph is undefined; setup not possible');
-    console.log('setup matrixvis with graph:', props.graph);
-
-    columnLayer.removeChildren();
-    rowsLabelLayer.removeChildren();
-    columnLabelLayer.removeChildren();
-    viewport.current?.removeAllListeners();
-    viewport.current?.removeChildren();
+    columnsContainer.removeChildren();
+    rowsContainer.removeChildren();
+    columnAxisContainer.removeChildren();
     app.stage.removeChildren();
 
     const size = ref.current?.getBoundingClientRect();
     viewport.current = new Viewport({
       screenWidth: size?.width || 1000,
       screenHeight: size?.height || 1000,
+      worldWidth: size?.width || 1000,
+      worldHeight: size?.height || 1000,
       stopPropagation: true,
       events: app.renderer.events, // the interaction module is important for wheel to work properly when renderer.view is placed or scaled
     });
 
-    // setupZoomLogic();
+    app.stage.addChild(viewport.current);
 
-    viewport.current.addChild(columnLayer);
+    viewport.current.addChild(columnsContainer);
+    viewport.current.addChild(rowsContainer);
 
-    const groupByType = props.graph.nodes.reduce(
-      (group: any, node: Node) => {
-        if (!group[node.label]) group[node.label] = [];
-        group[node.label].push(node);
-        return group;
-      },
-      {} as { [key: string]: Node[] },
-    );
+    const groupByType = props.graph.nodes.reduce((group: any, node: Node) => {
+      if (!group[node.label]) group[node.label] = [];
+      group[node.label].push(node);
+      return group;
+    }, {} as { [key: string]: Node[] });
 
     // order groupByType by size
     const ordered = Object.entries(groupByType).sort((a: any[], b: any[]) => b[1].length - a[1].length);
@@ -365,127 +345,31 @@ export const MatrixPixi = (props: Props) => {
     columnOrder = newOrdering.columnOrder;
     rowOrder = newOrdering.rowOrder;
 
+    config.cellWidth = Math.max((size?.width || 1000) / props.graph.nodes.length, (size?.height || 1000) / props.graph.nodes.length);
+    config.cellHeight = config.cellWidth;
+
+    // console.log('currentColumnOrder', columnOrder);
+
     setupVisualizationEncodingMapping(props.localConfig);
 
     setupColumns(props.graph.edges, columnOrder, rowOrder);
-    setupColumnLabels(columnOrder);
+    setupColumnLegend(columnOrder);
     setupColumnInteractivity(cols);
 
-    setupRowLabels(rows, rowOrder);
+    setupRowLegend(rows, rowOrder);
     setupRowInteractivity(cols, rows, rowOrder, d3.scaleOrdinal(d3.schemeCategory10));
 
+    console.log('setup matrixvis with graph:', props.graph);
+
     // activate plugins
     viewport.current.drag().pinch().wheel().animate({}).decelerate({ friction: 0.75 });
-    lastZoom = viewport.current.scale.x;
 
-    app.stage.addChild(viewport.current);
     app.ticker.add(tick);
-
-    // requestRender();
     update();
     isSetup.current = true;
-
-    cull.clear();
-    cull.addAll(viewport.current.children);
-    cull.addAll(rowsLabelLayer.children);
-    cull.addAll(columnLabelLayer.children);
-    cull.addAll(columnLayer.children);
-    cullDirty.current = true;
-
-    setColumnOrderState([...columnOrder]);
-    setRowOrderState([...rowOrder]);
-
-    resetViewport();
   };
 
-  // let renderRequestId: number | undefined = undefined;
-  // const requestRender = () => {
-  //   if (renderRequestId) {
-  //     return;
-  //   }
-  //   renderRequestId = window.requestAnimationFrame(() => {
-  //     app.render();
-  //     renderRequestId = undefined;
-  //   });
-  // };
-
-  const updateVisibility = () => {
-    if (!viewport.current) return;
-
-    // culling
-    if (viewport.current.dirty || cullDirty.current) {
-      cull.cull(app.renderer.screen);
-      // console.log(
-      //   'culling',
-      //   [...viewport.current.children].filter((x) => x.visible === true).length,
-      //   [...viewport.current.children].filter((x) => x.visible === false).length,
-      //   [...rowsLabelLayer.children].filter((x) => x.visible === true).length,
-      //   [...rowsLabelLayer.children].filter((x) => x.visible === false).length,
-      //   [...columnLabelLayer.children].filter((x) => x.visible === true).length,
-      //   [...columnLabelLayer.children].filter((x) => x.visible === false).length,
-      //   [...columnLayer.children].filter((x) => x.visible === true).length,
-      //   [...columnLayer.children].filter((x) => x.visible === false).length
-      // );
-
-      viewport.current.dirty = false;
-      cullDirty.current = false;
-    }
-
-    // levels of detail
-    // if (viewport.current?.scale.x !== lastZoom) {
-    const zoom = viewport.current?.scale.x || 0.0;
-
-    const oneColumn = columnLayer.children[0];
-    if (!oneColumn) return;
-
-    const targetLabelWidth = oneColumn.getBounds().width;
-    const targetLabelHeight = targetLabelWidth; //cells have same width and height
-
-    [...columnLabelLayer.children]
-      .filter((x) => x.visible === true)
-      .forEach((columnItem) => {
-        if (targetLabelWidth < config.LABEL_MIN_WIDTH) {
-          columnItem.visible = false;
-        } else {
-          const textLabel = columnItem as TextLabel;
-          textLabel.visible = true;
-        }
-      });
-
-    [...rowsLabelLayer.children]
-      .filter((x) => x.visible === true)
-      .forEach((rowItem) => {
-        if (targetLabelWidth < config.LABEL_MIN_WIDTH) {
-          rowItem.visible = false;
-        } else {
-          rowItem.visible = true;
-        }
-      });
-
-    lastZoom = zoom;
-    // }
-  };
-
-  // TODO
-  // const setupZoomLogic = () => {
-  //   const zoomIn = () => {
-  //     if (!viewport.current) return
-  //     viewport.current.zoom(-WORLD_WIDTH / 10, true);
-  //   };
-  //   const zoomOut = () => {
-  //     if (!viewport.current) return;
-  //     viewport.current.zoom(WORLD_WIDTH / 10, true);
-  //   };
-  const resetViewport = () => {
-    if (!viewport.current) return;
-    viewport.current.center = new Point(viewport.current.worldWidth / 2, viewport.current.worldHeight / 2);
-
-    // console.log('resetViewPort', viewport.current.center);
-    viewport.current.fitWorld(true);
-  };
-  // }
-
-  const setupVisualizationEncodingMapping = (localConfig: localConfigSchemaType) => {
+  const setupVisualizationEncodingMapping = (localConfig: SettingTypes) => {
     if (!props.graph) throw new Error('Graph is undefined; cannot setup matrix');
     const visMapping = []; // TODO type
 
@@ -507,10 +391,10 @@ export const MatrixPixi = (props: Props) => {
 
         gfxContext.lineStyle(2, 0xffffff);
         if (this.encoding === 'rect') {
-          gfxContext.drawRect(0, i * config.CELL_HEIGHT, config.CELL_WIDTH, config.CELL_HEIGHT);
+          gfxContext.drawRect(0, i * config.cellHeight, config.cellWidth, config.cellHeight);
         }
         if (this.encoding === 'circle') {
-          gfxContext.drawCircle(config.CELL_WIDTH / 2, i * config.CELL_HEIGHT + config.CELL_HEIGHT / 2, config.CELL_WIDTH / 2);
+          gfxContext.drawCircle(config.cellWidth / 2, i * config.cellHeight + config.cellHeight / 2, config.cellWidth / 2);
         }
         gfxContext.endFill();
 
@@ -558,28 +442,20 @@ export const MatrixPixi = (props: Props) => {
       });
       // console.log('edgesForThisColumn', oneColumn.name, edgesForThisColumn);
 
-      const col = createColumn(rowOrder, edgesForThisColumn, config.visMapping, config.CELL_WIDTH, config.CELL_HEIGHT);
+      const col = createColumn(rowOrder, edgesForThisColumn, config.visMapping, config.cellWidth, config.cellHeight);
       oneColumn.addChild(col);
-      oneColumn.position.set(j * config.CELL_WIDTH + config.textOffsetX, config.textOffsetY);
-
-      columnLayer.addChild(oneColumn);
-
-      if (columnOrder.length < 100) {
-        oneColumn.alpha = 0;
-        Actions.sequence(
-          Actions.delay(j * 0.005 * Math.random()),
-          Actions.fadeIn(oneColumn, config.ANIMATION_DURATION, Interpolations.pow2out),
-        ).play();
-      } else {
-        oneColumn.alpha = 1;
-      }
+      oneColumn.position.set(j * config.cellWidth + config.textOffsetX, config.textOffsetY);
+
+      columnsContainer.addChild(oneColumn);
+      oneColumn.alpha = 0;
+      Actions.sequence(Actions.delay(j * 0.005 * Math.random()), Actions.fadeIn(oneColumn, 0.8, Interpolations.pow2out)).play();
     }
   }
 
   const setupColumnInteractivity = (cols: Node[]) => {
-    for (let j = 0; j < columnLayer.children.length; j++) {
-      const oneColumn = columnLayer.children[j];
-      oneColumn.eventMode = 'dynamic';
+    for (let j = 0; j < columnsContainer.children.length; j++) {
+      const oneColumn = columnsContainer.children[j];
+      oneColumn.interactive = true;
       oneColumn.on('pointerdown', onButtonDown);
       oneColumn.on('pointerover', (event) => {
         const col = cols.find((col) => col.id === oneColumn.name);
@@ -591,28 +467,34 @@ export const MatrixPixi = (props: Props) => {
     }
   };
 
-  const setupColumnLabels = (columnOrder: string[]) => {
-    columnLabelLayer.removeChildren();
-
-    columnLabelLayer.position.set(config.textOffsetX, 0);
+  const setupColumnLegend = (columnOrder: string[]) => {
+    // console.log('setupColumnLegend');
+    // columnAxisContainer.removeChildren();
+    columnAxisContainer.position.set(config.textOffsetX, 0);
     for (let j = 0; j < columnOrder.length; j++) {
-      const textLabel = new TextLabel(columnOrder[j], config);
-      // const textLabel = new BitmapTextLabel(columnOrder[j]);
-
-      // console.log('font resolved', columnOrder[j]);
-      textLabel.position.set(j * config.CELL_WIDTH + config.CELL_WIDTH / 4, config.textOffsetY - 5);
-      columnLabelLayer.addChild(textLabel);
+      // from the PixiJS documention: Setting a text object's scale to > 1.0 will result in blurry/pixely display,
+      // because the text is not re-rendered at the higher resolution needed to look sharp -
+      // it's still the same resolution it was when generated. To deal with this, you can render at a higher
+      // initial size and down-scale, instead.
+      const finalTextSize = 10;
+      const bigRandomScaleFactor = 10;
+      const basicText = new Text(columnOrder[j], { fontSize: finalTextSize * bigRandomScaleFactor, fill: 0x000000, align: 'center' });
+      basicText.position.set(j * config.cellWidth + config.cellWidth / 4, config.textOffsetY - 5);
+      basicText.scale.set(finalTextSize / (bigRandomScaleFactor * bigRandomScaleFactor));
+      basicText.rotation = (Math.PI * 3) / 2;
+      basicText.eventMode = 'none';
+      basicText.name = 'Text_' + columnOrder[j];
+      columnAxisContainer.addChild(basicText);
     }
-    viewport.current?.addChild(columnLabelLayer);
+    viewport.current?.addChild(columnAxisContainer);
 
     const columnAxis = new Sprite(Texture.WHITE);
-    columnAxis.width = columnOrder.length * config.CELL_WIDTH;
+    columnAxis.width = columnOrder.length * config.cellWidth;
     columnAxis.height = config.textOffsetY;
     columnAxis.tint = 0xaaaaaa;
     columnAxis.position.set(config.textOffsetX, 0);
     columnAxis.alpha = 0.0;
-    // columnAxis.interactive = true;
-    columnAxis.eventMode = 'dynamic';
+    columnAxis.interactive = true;
     columnAxis.on('pointerdown', (event) => {
       // const reordering = new ReorderColumnsAction(columnOrder);
 
@@ -626,10 +508,10 @@ export const MatrixPixi = (props: Props) => {
     });
     // columnAxis.on('pointerdown', reorderColumns);
     columnAxis.on('pointerover', (event) => {
-      Actions.fadeTo(columnAxis, 0.5, config.ANIMATION_DURATION, Interpolations.pow2out).play();
+      Actions.fadeTo(columnAxis, 0.5, 0.5, Interpolations.pow2out).play();
     });
     columnAxis.on('pointerout', (event) => {
-      Actions.fadeTo(columnAxis, 0.0, config.ANIMATION_DURATION, Interpolations.pow2out).play();
+      Actions.fadeTo(columnAxis, 0.0, 0.5, Interpolations.pow2out).play();
     });
     viewport.current?.addChild(columnAxis);
   };
@@ -653,46 +535,35 @@ export const MatrixPixi = (props: Props) => {
     return array;
   }
 
-  function setupRowLabels(rows: Node[], rowOrder: string[]) {
-    rowsLabelLayer.removeChildren();
-
-    // columnLabelLayer.position.set(config.textOffsetX, 0);
+  function setupRowLegend(rows: Node[], rowOrder: string[]) {
     for (let i = 0; i < rowOrder.length; i++) {
       const rowID = rowOrder[i];
-      const textLabel = new TextLabel(rowID, config);
-      textLabel.anchor.x = 1;
-      textLabel.anchor.y = 0;
-      textLabel.rotation = 0;
-      textLabel.name = rowID;
-      textLabel.position.set(config.textOffsetX - 5, i * config.CELL_HEIGHT + config.textOffsetY + config.CELL_HEIGHT / 4);
-      rowsLabelLayer.addChild(textLabel);
-
-      if (rowOrder.length < 100) {
-        textLabel.alpha = 0;
-        Actions.sequence(
-          Actions.delay(i * 0.005 * Math.random()),
-          Actions.fadeIn(textLabel, config.ANIMATION_DURATION, Interpolations.pow2out),
-        ).play();
-      } else {
-        textLabel.alpha = 1;
-      }
-    }
+      const finalTextSize = 10;
+      const bigRandomScaleFactor = 10;
+      const basicText = new Text(rowID, { fontSize: finalTextSize * bigRandomScaleFactor, fill: 0x000000, align: 'right' });
+      basicText.anchor.x = 1;
+      basicText.anchor.y = 0;
+      basicText.scale.set(finalTextSize / (bigRandomScaleFactor * bigRandomScaleFactor));
+      basicText.position.set(config.textOffsetX - 5, i * config.cellHeight + config.textOffsetY + config.cellHeight / 4);
+      basicText.name = rowID;
+      rowsContainer.addChild(basicText);
 
-    viewport.current?.addChild(rowsLabelLayer);
+      Actions.sequence(Actions.delay(i * 0.005 * Math.random()), Actions.fadeIn(basicText, 0.5, Interpolations.pow2out)).play();
+    }
   }
 
   const setupRowInteractivity = (cols: Node[], rows: Node[], rowOrder: string[], colorScale: any) => {
     const rowAxis = new Sprite(Texture.WHITE);
     rowAxis.width = config.textOffsetX;
-    rowAxis.height = rows.length * config.CELL_HEIGHT;
+    rowAxis.height = rows.length * config.cellHeight;
     rowAxis.tint = 0xaaaaaa;
     rowAxis.position.set(0, config.textOffsetY);
     rowAxis.alpha = 0.0;
-    rowAxis.eventMode = 'dynamic';
+    rowAxis.interactive = true;
     rowAxis.on('pointerdown', (event) => {
       if (props.graph) {
         rowOrder = shuffle(rowOrder);
-        reorderRows(cols, rows, props?.graph.edges, rowOrder, colorScale, config.CELL_WIDTH, config.CELL_HEIGHT);
+        reorderRows(cols, rows, props?.graph.edges, rowOrder, colorScale, config.cellWidth, config.cellHeight);
       }
     });
     rowAxis.on('pointerover', (event) => {
@@ -707,8 +578,6 @@ export const MatrixPixi = (props: Props) => {
   const tick = (delta: number) => {
     if (props.graph) {
       Actions.tick(delta / 60);
-
-      updateVisibility();
     }
   };
 
@@ -718,8 +587,6 @@ export const MatrixPixi = (props: Props) => {
         <NLPopup onClose={() => {}} data={popup} key={popup.node.id} />
       ))}
       {quickPopup && <NLPopup onClose={() => {}} data={quickPopup} />}
-
-      {/* {columnOrderState.length > 0 && <ColumnLabelTrack columnLabels={columnOrderState} config={config} />} */}
       <div className="h-full w-full overflow-hidden" ref={ref}></div>
     </>
   );
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/MatrixPopup.tsx b/libs/shared/lib/vis/visualizations/matrixvis/components/MatrixPopup.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/components/MatrixPopup.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/components/MatrixPopup.tsx
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/ReorderingManager.tsx b/libs/shared/lib/vis/visualizations/matrixvis/components/ReorderingManager.tsx
similarity index 76%
rename from libs/shared/lib/vis/visualizations/matrix/components/ReorderingManager.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/components/ReorderingManager.tsx
index a843663d3..ae69d44e2 100644
--- a/libs/shared/lib/vis/visualizations/matrix/components/ReorderingManager.tsx
+++ b/libs/shared/lib/vis/visualizations/matrixvis/components/ReorderingManager.tsx
@@ -10,27 +10,6 @@ export class ReorderingManager {
     this.graph = graph;
   }
 
-  //   // Precompute the orders.
-  //   orders = {
-  //     //
-  //     //   name: d3.range(n).sort(function (a, b) {
-  //     //     return d3.ascending(nodes[a].name, nodes[b].name);
-  //     //   }),
-  //     //   count: d3.range(n).sort(function (a, b) {
-  //     //     return nodes[b].count - nodes[a].count;
-  //     //   }),
-  //     //   group: d3.range(n).sort(function (a, b) {
-  //     //     var x = nodes[b].group - nodes[a].group;
-  //     //     return x != 0 ? x : d3.ascending(nodes[a].name, nodes[b].name);
-  //     //   }),
-  //     leafOrder: computeLeaforder,
-  //     //   leafOrderDist: computeLeaforderDist,
-  //     //   barycenter: computeBarycenter,
-  //     //   rcm: computeRCM,
-  //     //   spectral: computeSpectral,
-  //     //   nn2opt: computeNN2OPT,
-  //   };
-
   private computeNameOrder = (columnOrder: string[], rowOrder: string[]) => {
     const columnOrderSorted = columnOrder.sort((a, b) => a.localeCompare(b));
     const rowOrderSorted = rowOrder.sort((a, b) => a.localeCompare(b));
@@ -101,8 +80,6 @@ export class ReorderingManager {
       adjacency.push(rowOrderIDs);
     }
 
-    // console.log('adjacency', adjacency);
-
     return adjacency;
   }
 
@@ -137,9 +114,6 @@ export class ReorderingManager {
     );
 
     const nodes = columnOrder.map((id) => nodesTemp.find((node) => node.id === id));
-    const nodesWithRows = nodes.push(...rowOrder.map((id) => nodesTemp.find((node) => node.id === id)));
-    console.log('nodesTemp', nodes);
-
     const graphReorderJs = reorderAny.graph().nodes(nodes);
 
     const edges = graph.edges.map((edge) => {
@@ -156,7 +130,6 @@ export class ReorderingManager {
       } as any;
     });
 
-    // const edges = rowOrder.map((id) => edgesTemp.find((edge) => edge.id === id));
     graphReorderJs.links(edges);
     graphReorderJs.init();
 
@@ -181,14 +154,6 @@ export class ReorderingManager {
     const reorderGraph = this.getReorderJSGraph(this.graph, columnOrder, rowOrder);
 
     var order = reorderAny.spectral_order(reorderGraph);
-    // rcm.forEach(function (lo, i) {
-    //   nodes[i].rcm = lo;
-    // });
-
-    // return nodes.map(function (n) {
-    //   return n.rcm;
-    // });
-
     const columnOrderSorted = order.map((i: number) => columnOrder[i]).filter((a: string) => columnOrder.includes(a));
     const rowOrderSorted = order.map((i: number) => rowOrder[i]).filter((a: string) => rowOrder.includes(a));
 
@@ -202,13 +167,6 @@ export class ReorderingManager {
     const reorderGraph = this.getReorderJSGraph(this.graph, columnOrder, rowOrder);
 
     var order = reorderAny.bfs_order(reorderGraph);
-    // rcm.forEach(function (lo, i) {
-    //   nodes[i].rcm = lo;
-    // });
-
-    // return nodes.map(function (n) {
-    //   return n.rcm;
-    // });
 
     const columnOrderSorted = order.map((i: number) => columnOrder[i]).filter((a: string) => columnOrder.includes(a));
     const rowOrderSorted = order.map((i: number) => rowOrder[i]).filter((a: string) => rowOrder.includes(a));
@@ -225,7 +183,6 @@ export class ReorderingManager {
 
     const order = reorderAny.pca_order(columnOrderUnSorted);
     const columnOrderSorted = order.map((i: number) => columnOrder[i]);
-    // const rowOrderSorted = order.map((i: number) => rowOrder[i]).filter((a: string) => rowOrder.includes(a));
 
     console.debug('improved pca', order, columnOrderSorted, rowOrder);
 
@@ -236,22 +193,22 @@ export class ReorderingManager {
     console.log('reorderMatrix', orderingname);
     switch (orderingname.toLowerCase()) {
       case 'leafordering': {
-        return this.computeLeaforder(columnOrder, rowOrder) as { columnOrder: string[]; rowOrder: string[] };
+        return this.computeLeaforder(columnOrder, rowOrder);
       }
       case 'name': {
-        return this.computeNameOrder(columnOrder, rowOrder) as { columnOrder: string[]; rowOrder: string[] };
+        return this.computeNameOrder(columnOrder, rowOrder);
       }
       case 'count': {
-        return this.computeCountOrder(columnOrder, rowOrder) as { columnOrder: string[]; rowOrder: string[] };
+        return this.computeCountOrder(columnOrder, rowOrder);
       }
       case 'barycenter': {
-        return this.computeBarycenter(columnOrder, rowOrder) as { columnOrder: string[]; rowOrder: string[] };
+        return this.computeBarycenter(columnOrder, rowOrder);
       }
       case 'rcm': {
-        return this.computeRCM(columnOrder, rowOrder) as { columnOrder: string[]; rowOrder: string[] };
+        return this.computeRCM(columnOrder, rowOrder);
       }
       case 'spectral': {
-        return this.computeSpectal(columnOrder, rowOrder) as { columnOrder: string[]; rowOrder: string[] };
+        return this.computeSpectal(columnOrder, rowOrder);
       }
       // case 'pca': {
       //   return this.computePCA(columnOrder, rowOrder);
@@ -260,10 +217,10 @@ export class ReorderingManager {
       //   return this.computeBFS(columnOrder, rowOrder);
       // }
       case 'none' || 'identity': {
-        return { columnOrder, rowOrder } as { columnOrder: string[]; rowOrder: string[] };
+        return { columnOrder, rowOrder };
       }
       default: {
-        return this.computeLeaforder(columnOrder, rowOrder) as { columnOrder: string[]; rowOrder: string[] };
+        return this.computeLeaforder(columnOrder, rowOrder);
       }
     }
   };
diff --git a/libs/shared/lib/vis/visualizations/matrix/components/TextLabel.ts b/libs/shared/lib/vis/visualizations/matrixvis/components/TextLabel.ts
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/components/TextLabel.ts
rename to libs/shared/lib/vis/visualizations/matrixvis/components/TextLabel.ts
diff --git a/libs/shared/lib/vis/visualizations/matrix/index.ts b/libs/shared/lib/vis/visualizations/matrixvis/index.ts
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/index.ts
rename to libs/shared/lib/vis/visualizations/matrixvis/index.ts
diff --git a/libs/shared/lib/vis/visualizations/matrix/matrix.stories.tsx b/libs/shared/lib/vis/visualizations/matrixvis/matrix.stories.tsx
similarity index 61%
rename from libs/shared/lib/vis/visualizations/matrix/matrix.stories.tsx
rename to libs/shared/lib/vis/visualizations/matrixvis/matrix.stories.tsx
index 766845b3c..69dca0855 100644
--- a/libs/shared/lib/vis/visualizations/matrix/matrix.stories.tsx
+++ b/libs/shared/lib/vis/visualizations/matrixvis/matrix.stories.tsx
@@ -2,26 +2,9 @@ import { Meta } from '@storybook/react';
 
 import { configureStore } from '@reduxjs/toolkit';
 import { Provider } from 'react-redux';
-import {
-  big2ndChamberQueryResult,
-  bigMockQueryResults,
-  big2ndChamberSchemaRaw,
-  smallFlightsQueryResults,
-  mockLargeQueryResults,
-  recommendationPersonActedInMovieQueryResult,
-  recommendationPersonActedInMovieQueryResultPayload,
-  slackReactionToThreadedMessageQueryResultPayload,
-} from '../../../mock-data';
-
-// export * from './mockLargeQueryResults';
-// export * from './big2ndChamberQueryResult';
-// export * from './bigMockQueryResults';
-// export * from './mockQueryResults';
-// export * from './smallFlightsQueryResults';
-// export * from './mockMobilityQueryResult';
-// export * from './typesMockQueryResults';
+import { big2ndChamberQueryResult, smallFlightsQueryResults, mockLargeQueryResults } from '../../../mock-data';
+import { VisualizationPanel } from '../../visualizationPanel';
 
-import { VisualizationPanel } from '../../panel/visualization';
 import {
   assignNewGraphQueryResult,
   graphQueryResultSlice,
@@ -33,7 +16,7 @@ import {
 
 import { SchemaUtils } from '../../../schema/schema-utils';
 import { simpleSchemaAirportRaw } from '../../../mock-data/schema/simpleAirportRaw';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 
 const Component: Meta<typeof VisualizationPanel> = {
   title: 'Visualizations/MatrixVis',
@@ -54,7 +37,7 @@ const Component: Meta<typeof VisualizationPanel> = {
   ],
 };
 
-const Mockstore = configureStore({
+const Mockstore: any = configureStore({
   reducer: {
     schema: schemaSlice.reducer,
     graphQueryResult: graphQueryResultSlice.reducer,
@@ -85,9 +68,9 @@ export const TestWithData = {
             edges: [{ id: 'escape/escape', from: 'agent/007', to: 'villain', attributes: { name: 'Escape' } }],
           },
         },
-      })
+      }),
     );
-    dispatch(setActiveVisualization(Visualizations.Matrix));
+    dispatch(setActiveVisualization('MatrixVis'));
   },
 };
 
@@ -105,9 +88,9 @@ export const TestWithNoData = {
             edges: [],
           },
         },
-      })
+      }),
     );
-    dispatch(setActiveVisualization(Visualizations.Matrix));
+    dispatch(setActiveVisualization('MatrixVis'));
   },
 };
 
@@ -116,7 +99,7 @@ export const TestWithBig2ndChamber = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: big2ndChamberQueryResult } }));
-    dispatch(setActiveVisualization(Visualizations.Matrix));
+    dispatch(setActiveVisualization('MatrixVis'));
   },
 };
 
@@ -125,7 +108,7 @@ export const TestWithSmallFlights = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: smallFlightsQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.Matrix));
+    dispatch(setActiveVisualization('MatrixVis'));
   },
 };
 
@@ -134,25 +117,7 @@ export const TestWithLargeQueryResult = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: mockLargeQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.Matrix));
-  },
-};
-
-export const TestWithRecommendationPersonActedInMovieQueryResult = {
-  args: { loading: false },
-  play: async () => {
-    const dispatch = Mockstore.dispatch;
-    dispatch(assignNewGraphQueryResult(recommendationPersonActedInMovieQueryResultPayload));
-    dispatch(setActiveVisualization(Visualizations.Matrix));
-  },
-};
-
-export const TestWithSlackReactionToThreadedMessageQueryResult = {
-  args: { loading: false },
-  play: async () => {
-    const dispatch = Mockstore.dispatch;
-    dispatch(assignNewGraphQueryResult(slackReactionToThreadedMessageQueryResultPayload));
-    dispatch(setActiveVisualization(Visualizations.Matrix));
+    dispatch(setActiveVisualization('MatrixVis'));
   },
 };
 
diff --git a/libs/shared/lib/vis/visualizations/matrix/matrixvis.module.scss b/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.module.scss
similarity index 100%
rename from libs/shared/lib/vis/visualizations/matrix/matrixvis.module.scss
rename to libs/shared/lib/vis/visualizations/matrixvis/matrixvis.module.scss
diff --git a/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.tsx b/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.tsx
new file mode 100644
index 000000000..d307dcd99
--- /dev/null
+++ b/libs/shared/lib/vis/visualizations/matrixvis/matrixvis.tsx
@@ -0,0 +1,44 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { useImmer } from 'use-immer';
+import { GraphQueryResult } from '../../../data-access/store';
+import { LinkType, NodeType } from './Types';
+import { MatrixPixi } from './components/MatrixPixi';
+import { VisualizationPropTypes, VISComponentType } from '../../types';
+
+export const MatrixVis = React.memo(({ data, ml, settings }: VisualizationPropTypes) => {
+  const ref = useRef<HTMLDivElement>(null);
+  const [graph, setGraph] = useImmer<GraphQueryResult | undefined>(undefined);
+  const [highlightNodes, setHighlightNodes] = useState<NodeType[]>([]);
+  const [highlightedLinks, setHighlightedLinks] = useState<LinkType[]>([]);
+
+  useEffect(() => {
+    if (data) {
+      setGraph(data);
+    }
+  }, [data, ml]);
+
+  return (
+    <>
+      <div className="h-full w-full overflow-hidden" ref={ref}>
+        <MatrixPixi graph={graph} highlightNodes={highlightNodes} highlightedLinks={highlightedLinks} localConfig={settings} />
+      </div>
+    </>
+  );
+});
+
+const displayName = 'Matrix';
+
+export const MatrixVisComponent: VISComponentType = {
+  displayName: displayName,
+  VIS: MatrixVis,
+  settings: {
+    marks: {
+      type: 'dropdown',
+      options: ['rect', 'circle'],
+      value: 'rect',
+      label: 'Configure Marks',
+    },
+  },
+};
+
+export default MatrixVisComponent;
diff --git a/libs/shared/lib/vis/visualizations/nodelink/index.ts b/libs/shared/lib/vis/visualizations/nodelink/index.ts
deleted file mode 100644
index e019d6524..000000000
--- a/libs/shared/lib/vis/visualizations/nodelink/index.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from 'react';
-import { VISComponentType } from '../../types';
-import { displayName, NodeLinkVis, localConfigSchema } from './nodelinkvis';
-export type { NodeLinkProps } from './nodelinkvis';
-
-export const NodeLinkComponent: VISComponentType = {
-  displayName,
-  VIS: NodeLinkVis,
-  localConfigSchema: localConfigSchema,
-};
-
-export default NodeLinkComponent;
diff --git a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.module.scss.d.ts b/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.module.scss.d.ts
deleted file mode 100644
index 3ad131d27..000000000
--- a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.module.scss.d.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-declare const classNames: {
-  readonly container: 'container';
-  readonly title: 'title';
-  readonly subtitle: 'subtitle';
-  readonly subsubtitle: 'subsubtitle';
-  readonly subContainer: 'subContainer';
-  readonly rulesContainer: 'rulesContainer';
-  readonly selectContainer: 'selectContainer';
-};
-export = classNames;
diff --git a/libs/shared/lib/vis/visualizations/nodelink/components/NLExport.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLExport.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/components/NLExport.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/components/NLExport.tsx
diff --git a/libs/shared/lib/vis/visualizations/nodelink/components/NLForce.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLForce.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/components/NLForce.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/components/NLForce.tsx
diff --git a/libs/shared/lib/vis/visualizations/nodelink/components/NLMachineLearning.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLMachineLearning.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/components/NLMachineLearning.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/components/NLMachineLearning.tsx
diff --git a/libs/shared/lib/vis/visualizations/nodelink/components/NLPixi.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/components/NLPixi.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPixi.tsx
diff --git a/libs/shared/lib/vis/visualizations/nodelink/components/NLPopup.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPopup.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/components/NLPopup.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/components/NLPopup.tsx
diff --git a/libs/shared/lib/vis/visualizations/nodelink/components/query2NL.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/query2NL.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/components/query2NL.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/components/query2NL.tsx
diff --git a/libs/shared/lib/vis/visualizations/nodelink/components/utils.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/components/utils.tsx
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/components/utils.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/components/utils.tsx
diff --git a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.module.scss b/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.module.scss
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.module.scss
rename to libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.module.scss
diff --git a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.stories.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.stories.tsx
similarity index 84%
rename from libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.stories.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.stories.tsx
index ea402d7a4..22500ead3 100644
--- a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.stories.tsx
+++ b/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.stories.tsx
@@ -1,6 +1,6 @@
 import React from 'react';
 import { Meta } from '@storybook/react';
-import { VisualizationPanel } from '../../panel/visualization';
+import { VisualizationPanel } from '../../visualizationPanel';
 import {
   assignNewGraphQueryResult,
   graphQueryResultSlice,
@@ -11,7 +11,7 @@ import {
 import { configureStore } from '@reduxjs/toolkit';
 import { Provider } from 'react-redux';
 import { big2ndChamberQueryResult, smallFlightsQueryResults, mockLargeQueryResults } from '../../../mock-data';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 
 const Component: Meta<typeof VisualizationPanel> = {
   title: 'Visualizations/NodeLinkVis',
@@ -59,9 +59,9 @@ export const TestWithData = {
             edges: [{ id: 'escape/escape', from: 'agent/007', to: 'villain', attributes: { name: 'Escape' } }],
           },
         },
-      })
+      }),
     );
-    dispatch(setActiveVisualization(Visualizations.NodeLink));
+    dispatch(setActiveVisualization('NodeLinkVis'));
   },
 };
 
@@ -79,9 +79,9 @@ export const TestWithNoData = {
             edges: [],
           },
         },
-      })
+      }),
     );
-    dispatch(setActiveVisualization(Visualizations.NodeLink));
+    dispatch(setActiveVisualization('NodeLinkVis'));
   },
 };
 
@@ -89,7 +89,7 @@ export const TestWithBig2ndChamber = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: big2ndChamberQueryResult } }));
-    dispatch(setActiveVisualization(Visualizations.NodeLink));
+    dispatch(setActiveVisualization('NodeLinkVis'));
   },
 };
 
@@ -98,7 +98,7 @@ export const TestWithSmallFlights = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: smallFlightsQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.NodeLink));
+    dispatch(setActiveVisualization('NodeLinkVis'));
   },
 };
 
@@ -107,7 +107,7 @@ export const TestWithLargeQueryResult = {
   play: async () => {
     const dispatch = Mockstore.dispatch;
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: mockLargeQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.NodeLink));
+    dispatch(setActiveVisualization('NodeLinkVis'));
   },
 };
 
diff --git a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.tsx b/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.tsx
similarity index 65%
rename from libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.tsx
rename to libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.tsx
index 1ddefa86e..9a6009e48 100644
--- a/libs/shared/lib/vis/visualizations/nodelink/nodelinkvis.tsx
+++ b/libs/shared/lib/vis/visualizations/nodelinkvis/nodelinkvis.tsx
@@ -1,5 +1,4 @@
 import React, { useEffect, useRef, useState } from 'react';
-import * as PIXI from 'pixi.js';
 import { GraphType, LinkType, NodeType } from './types';
 import { NLPixi } from './components/NLPixi';
 import { parseQueryResult } from './components/query2NL';
@@ -7,31 +6,7 @@ import { useImmer } from 'use-immer';
 import { ML, setShortestPathSource, setShortestPathTarget } from '../../../data-access/store/mlSlice';
 import { VisualizationPropTypes, VISComponentType } from '../../types';
 
-export type NodeLinkProps = {};
-
-/** Return default radius */
-function Radius() {
-  return 1;
-}
-
-/** This is the interface of the NodeLinkComponentState. */
-export interface NodeLinkComponentState {
-  graph: GraphType;
-  width: number;
-  height: number;
-  stage: PIXI.Container;
-  links: PIXI.Graphics;
-  simulation: d3.Simulation<NodeType, LinkType>;
-  myRef: React.RefObject<HTMLDivElement>;
-  renderer: PIXI.IRenderer;
-  dragOffset: any;
-  panOffset: any;
-  scalexy: number;
-}
-
-export const displayName = 'NodeLinkVis';
-
-export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationPropTypes<typeof displayName>) => {
+export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationPropTypes) => {
   const ref = useRef<HTMLDivElement>(null);
   const [graph, setGraph] = useImmer<GraphType | undefined>(undefined);
   const [highlightNodes, setHighlightNodes] = useState<NodeType[]>([]);
@@ -39,8 +14,6 @@ export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationProp
 
   useEffect(() => {
     if (data) {
-      console.debug('%cResult from graphQuery', 'color: aquamarine', data);
-
       setGraph(
         parseQueryResult(data, ml, {
           defaultX: (ref.current?.clientWidth || 1000) / 2,
@@ -51,12 +24,8 @@ export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationProp
   }, [data, ml]);
 
   const onClickedNode = (node: NodeType, ml: ML) => {
-    console.log('shortestPath', graph, ml.shortestPath.enabled);
-
     if (graph) {
       if (ml.shortestPath.enabled) {
-        console.log('shortestPath');
-
         setGraph((draft) => {
           let _node = draft?.nodes.find((n) => n.id === node.id);
           if (!_node) return draft;
@@ -79,7 +48,6 @@ export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationProp
             dispatch(setShortestPathSource(node.id));
             dispatch(setShortestPathTarget(undefined));
           }
-          console.log('shortestPath', _node);
           return draft;
         });
       }
@@ -97,19 +65,53 @@ export const NodeLinkVis = React.memo(({ data, ml, dispatch }: VisualizationProp
             onClickedNode(node, ml);
           }}
         />
-        {/* <VisConfigPanelComponent> */}
-        {/* <NodeLinkConfigPanelComponent
-               graph={this.state.graph}
-               nlViewModel={this.nodeLinkViewModel}
-            /> */}
-        {/*</VisConfigPanelComponent>*/}
-        {/*<VisConfigPanelComponent isLeft>*/}
-        {/*  <AttributesConfigPanel nodeLinkViewModel={this.nodeLinkViewModel} />*/}
-        {/* </VisConfigPanelComponent> */}
       </div>
     </>
   );
 });
 
-export const localConfigSchema = {};
-export default NodeLinkVis;
+export const NodeLinkComponent: VISComponentType = {
+  displayName: 'NodeLink',
+  VIS: NodeLinkVis,
+  settings: {
+    layout: {
+      value: 'Force directed',
+      type: 'dropdown',
+      label: 'Layout',
+      options: ['Force directed'],
+      description: 'Select a layout that is used for the visualization',
+    },
+  },
+  encodings: {
+    color: {
+      label: 'Node color',
+      element: 'node',
+      dimension: ['categorical', 'numerical'],
+      selector: 'Color',
+      description: 'Select a color for the nodes',
+    },
+    shape: {
+      label: 'Node shape',
+      element: 'node',
+      dimension: ['categorical'],
+      selector: 'Shape',
+      description: 'Select a shape for the nodes',
+    },
+    size: {
+      label: 'Node size',
+      element: 'node',
+      dimension: ['categorical'],
+      selector: 'Size',
+      description: 'Select a size for the nodes',
+    },
+  },
+  interactions: {
+    showPopUpOnHover: {
+      value: true,
+      type: 'boolean',
+      label: 'Show pop-up',
+    },
+  },
+};
+
+export default NodeLinkComponent;
diff --git a/libs/shared/lib/vis/visualizations/nodelink/types.ts b/libs/shared/lib/vis/visualizations/nodelinkvis/types.ts
similarity index 100%
rename from libs/shared/lib/vis/visualizations/nodelink/types.ts
rename to libs/shared/lib/vis/visualizations/nodelinkvis/types.ts
diff --git a/libs/shared/lib/vis/visualizations/paohvis/paohvis.stories.tsx b/libs/shared/lib/vis/visualizations/paohvis/paohvis.stories.tsx
index 96dc4eaa8..b6252719d 100644
--- a/libs/shared/lib/vis/visualizations/paohvis/paohvis.stories.tsx
+++ b/libs/shared/lib/vis/visualizations/paohvis/paohvis.stories.tsx
@@ -8,12 +8,12 @@ import {
 } from '../../../data-access/store';
 import { configureStore } from '@reduxjs/toolkit';
 import { Meta } from '@storybook/react';
-import { VisualizationPanel } from '../../panel/visualization';
+import { VisualizationPanel } from '../../visualizationPanel';
 import { Provider } from 'react-redux';
 import { SchemaUtils } from '../../../schema/schema-utils';
 import { bigMockQueryResults, simpleSchemaRaw, smallFlightsQueryResults } from '../../../mock-data';
 import { simpleSchemaAirportRaw } from '../../../mock-data/schema/simpleAirportRaw';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 
 const Component: Meta<typeof VisualizationPanel> = {
   title: 'Visualizations/Paohvis',
@@ -86,9 +86,9 @@ export const TestWithData = {
             ],
           },
         },
-      })
+      }),
     );
-    dispatch(setActiveVisualization(Visualizations.Paohvis));
+    dispatch(setActiveVisualization('PaohVis'));
   },
 };
 
@@ -99,7 +99,7 @@ export const TestWithAirportSimple = {
 
     dispatch(setSchema(schema.export()));
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: smallFlightsQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.Paohvis));
+    dispatch(setActiveVisualization('PaohVis'));
   },
 };
 
@@ -110,7 +110,7 @@ export const TestWithAirport = {
 
     dispatch(setSchema(schema.export()));
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: bigMockQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.Paohvis));
+    dispatch(setActiveVisualization('PaohVis'));
   },
 };
 
diff --git a/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx b/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx
index 0e81c7bfb..52f4d24a8 100644
--- a/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx
+++ b/libs/shared/lib/vis/visualizations/paohvis/paohvis.tsx
@@ -638,12 +638,10 @@ export const PaohVis = (props: PaohVisProps) => {
   );
 };
 
-const localConfigSchema = {};
-
 export const PaohVisComponent: VISComponentType = {
-  displayName: 'PaohVis',
+  displayName: 'Paoh',
   VIS: PaohVis,
-  localConfigSchema: localConfigSchema,
+  settings: {},
 };
 
 export default PaohVisComponent;
diff --git a/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.stories.tsx b/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.stories.tsx
index cdcef1260..ff4929db6 100644
--- a/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.stories.tsx
+++ b/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.stories.tsx
@@ -1,6 +1,6 @@
 import React from 'react';
 import { Meta } from '@storybook/react';
-import { VisualizationPanel } from '../../panel/visualization';
+import { VisualizationPanel } from '../../visualizationPanel';
 import {
   assignNewGraphQueryResult,
   graphQueryResultSlice,
@@ -13,7 +13,7 @@ import {
 import { configureStore } from '@reduxjs/toolkit';
 import { Provider } from 'react-redux';
 import { mockLargeQueryResults } from '../../../mock-data/query-result';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 
 const Component: Meta<typeof VisualizationPanel> = {
   title: 'Visualizations/RawJSONVIS',
@@ -59,9 +59,9 @@ export const SimpleData = {
             edges: [],
           },
         },
-      })
+      }),
     );
-    dispatch(setActiveVisualization(Visualizations.RawJSON));
+    dispatch(setActiveVisualization('RawJSONVis'));
   },
 };
 
@@ -78,9 +78,9 @@ export const LargeData = {
           type: 'nodelink',
           payload: mockLargeQueryResults,
         },
-      })
+      }),
     );
-    dispatch(setActiveVisualization(Visualizations.RawJSON));
+    dispatch(setActiveVisualization('RawJSONVis'));
   },
 };
 
@@ -88,7 +88,7 @@ export const Empty = {
   play: async () => {
     const dispatch = store.dispatch;
     dispatch(resetGraphQueryResults());
-    dispatch(setActiveVisualization(Visualizations.RawJSON));
+    dispatch(setActiveVisualization('RawJSONVis'));
   },
 };
 
diff --git a/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx b/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx
index a4deeeec0..f5cf47861 100644
--- a/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx
+++ b/libs/shared/lib/vis/visualizations/rawjsonvis/rawjsonvis.tsx
@@ -6,7 +6,7 @@ export interface RawJSONVisProps {}
 
 const displayName = 'RawJSONVis';
 
-export const RawJSONVis = React.memo(({ data }: VisualizationPropTypes<typeof displayName>) => {
+export const RawJSONVis = React.memo(({ data }: VisualizationPropTypes) => {
   useEffect(() => {
     console.log('update rawjson useEffect');
   }, [data]);
@@ -28,12 +28,9 @@ export const RawJSONVis = React.memo(({ data }: VisualizationPropTypes<typeof di
   );
 });
 
-const localConfigSchema = {};
-
 export const RawJSONComponent: VISComponentType = {
-  displayName: displayName,
+  displayName: 'RawJSON',
   VIS: RawJSONVis,
-  localConfigSchema: localConfigSchema,
+  settings: {},
 };
-
 export default RawJSONComponent;
diff --git a/libs/shared/lib/vis/visualizations/semanticsubstrates/semanticsubstrates.stories.tsx b/libs/shared/lib/vis/visualizations/semanticsubstrates/semanticsubstrates.stories.tsx
index 5d4f31bda..8984fc8b9 100644
--- a/libs/shared/lib/vis/visualizations/semanticsubstrates/semanticsubstrates.stories.tsx
+++ b/libs/shared/lib/vis/visualizations/semanticsubstrates/semanticsubstrates.stories.tsx
@@ -9,11 +9,11 @@ import {
 import { configureStore } from '@reduxjs/toolkit';
 import { Meta } from '@storybook/react';
 import { Provider } from 'react-redux';
-import { VisualizationPanel } from '../../panel/visualization';
+import { VisualizationPanel } from '../../visualizationPanel';
 import { SchemaUtils } from '../../../schema/schema-utils';
 import { simpleSchemaAirportRaw } from '../../../mock-data/schema/simpleAirportRaw';
 import { bigMockQueryResults } from '../../../mock-data';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 
 const Component: Meta<typeof VisualizationPanel> = {
   title: 'Visualizations/SemanticSubstrates',
@@ -73,7 +73,7 @@ export const TestWithData = {
             ],
           },
         },
-      })
+      }),
     );
     // dispatch(setActiveVisualization(Visualizations.SemanticSubstrates));
   },
@@ -89,7 +89,7 @@ export const TestWithAirport = {
       assignNewGraphQueryResult({
         queryID: '1',
         result: { type: 'nodelink', payload: bigMockQueryResults },
-      })
+      }),
     );
     // dispatch(setActiveVisualization(Visualizations.SemanticSubstrates));
   },
diff --git a/libs/shared/lib/vis/visualizations/table_vis/components/table.module.scss.d.ts b/libs/shared/lib/vis/visualizations/table_vis/components/table.module.scss.d.ts
deleted file mode 100644
index 8798bcd00..000000000
--- a/libs/shared/lib/vis/visualizations/table_vis/components/table.module.scss.d.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-declare const classNames: {
-  readonly 'table-container': 'table-container';
-  readonly table: 'table';
-};
-export = classNames;
diff --git a/libs/shared/lib/vis/visualizations/table_vis/index.ts b/libs/shared/lib/vis/visualizations/table_vis/index.ts
deleted file mode 100644
index 87bfb9628..000000000
--- a/libs/shared/lib/vis/visualizations/table_vis/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './tableVis';
diff --git a/libs/shared/lib/vis/visualizations/table_vis/components/Table.tsx b/libs/shared/lib/vis/visualizations/tablevis/components/Table.tsx
similarity index 99%
rename from libs/shared/lib/vis/visualizations/table_vis/components/Table.tsx
rename to libs/shared/lib/vis/visualizations/tablevis/components/Table.tsx
index dddec9dc4..20164229e 100644
--- a/libs/shared/lib/vis/visualizations/table_vis/components/Table.tsx
+++ b/libs/shared/lib/vis/visualizations/tablevis/components/Table.tsx
@@ -106,7 +106,7 @@ export const Table = ({ data, itemsPerPage, showBarPlot }: TableProps) => {
     let categoryCounts = [];
     const firstRowData = data[0];
 
-    console.log('First row: ', firstRowData);
+    // console.log('First row: ', firstRowData);
 
     let _data2Render = Object.keys(firstRowData.attribute).map((dataColumn: string, i) => {
       const newData2Render: Data2RenderI = {
@@ -187,7 +187,7 @@ export const Table = ({ data, itemsPerPage, showBarPlot }: TableProps) => {
       return newData2Render;
     });
 
-    console.log(_data2Render);
+    // console.log(_data2Render);
 
     setData2Render(_data2Render);
   }, [currentPage, data, sortedData]);
diff --git a/libs/shared/lib/vis/visualizations/table_vis/components/table.module.scss b/libs/shared/lib/vis/visualizations/tablevis/components/table.module.scss
similarity index 100%
rename from libs/shared/lib/vis/visualizations/table_vis/components/table.module.scss
rename to libs/shared/lib/vis/visualizations/tablevis/components/table.module.scss
diff --git a/libs/shared/lib/vis/visualizations/tablevis/index.ts b/libs/shared/lib/vis/visualizations/tablevis/index.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/libs/shared/lib/vis/visualizations/table_vis/tablevis.stories.tsx b/libs/shared/lib/vis/visualizations/tablevis/tablevis.stories.tsx
similarity index 86%
rename from libs/shared/lib/vis/visualizations/table_vis/tablevis.stories.tsx
rename to libs/shared/lib/vis/visualizations/tablevis/tablevis.stories.tsx
index c3d1e9c3c..f75a72e75 100644
--- a/libs/shared/lib/vis/visualizations/table_vis/tablevis.stories.tsx
+++ b/libs/shared/lib/vis/visualizations/tablevis/tablevis.stories.tsx
@@ -1,6 +1,6 @@
 import React from 'react';
 import { Meta } from '@storybook/react';
-import { VisualizationPanel } from '../../panel/visualization';
+import { VisualizationPanel } from '../../visualizationPanel';
 import {
   assignNewGraphQueryResult,
   graphQueryResultSlice,
@@ -21,7 +21,7 @@ import {
 
 import { SchemaUtils } from '../../../schema/schema-utils';
 import { simpleSchemaAirportRaw } from '../../../mock-data/schema/simpleAirportRaw';
-import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
+import { setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice';
 
 const Component: Meta<typeof VisualizationPanel> = {
   title: 'Visualizations/TableVis',
@@ -58,7 +58,7 @@ export const TestWithAirport = {
 
     dispatch(setSchema(schema.export()));
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: bigMockQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.Table));
+    dispatch(setActiveVisualization('TableVis'));
   },
 };
 
@@ -69,7 +69,7 @@ export const TestWithBig2ndChamber = {
 
     dispatch(setSchema(schema.export()));
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: big2ndChamberQueryResult } }));
-    dispatch(setActiveVisualization(Visualizations.Table));
+    dispatch(setActiveVisualization('TableVis'));
   },
 };
 
@@ -80,7 +80,7 @@ export const TestWithTypesMock = {
 
     dispatch(setSchema(schema.export()));
     dispatch(assignNewGraphQueryResult({ queryID: '1', result: { type: 'nodelink', payload: typesMockQueryResults } }));
-    dispatch(setActiveVisualization(Visualizations.Table));
+    dispatch(setActiveVisualization('TableVis'));
   },
 };
 
diff --git a/libs/shared/lib/vis/visualizations/table_vis/tableVis.tsx b/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx
similarity index 53%
rename from libs/shared/lib/vis/visualizations/table_vis/tableVis.tsx
rename to libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx
index f8ca400e5..d4ab3688d 100644
--- a/libs/shared/lib/vis/visualizations/table_vis/tableVis.tsx
+++ b/libs/shared/lib/vis/visualizations/tablevis/tablevis.tsx
@@ -1,16 +1,14 @@
 import React, { useMemo, useRef } from 'react';
 import { Table, AugmentedNodeAttributes } from './components/Table';
 import { SchemaAttribute } from '../../../schema';
-import { VisualizationPropTypes, VISComponentType, localConfigSchemaType } from '../../types';
+import { VisualizationPropTypes, VISComponentType } from '../../types';
 
 export type TableProps = {
   showBarplot: boolean;
   itemsPerPage: number;
 };
 
-const displayName = 'TableVis';
-
-export const TableVis = ({ data, schema, localConfig }: VisualizationPropTypes<typeof displayName>) => {
+export const TableVis = ({ data, schema, settings }: VisualizationPropTypes) => {
   const ref = useRef<HTMLDivElement>(null);
 
   const attributesArray = useMemo<AugmentedNodeAttributes[]>(
@@ -30,33 +28,29 @@ export const TableVis = ({ data, schema, localConfig }: VisualizationPropTypes<t
   );
 
   return (
-    <>
-      <div className="h-full w-full" ref={ref}>
-        {attributesArray.length > 0 && (
-          <Table data={attributesArray} itemsPerPage={localConfig.itemsPerPage} showBarPlot={localConfig.showBarplot} />
-        )}
-      </div>
-    </>
+    <div className="h-full w-full" ref={ref}>
+      {attributesArray.length > 0 && (
+        <Table data={attributesArray} itemsPerPage={settings.itemsPerPage} showBarPlot={settings.showBarplot} />
+      )}
+    </div>
   );
 };
 
-const localConfigSchema: localConfigSchemaType = {
-  showBarplot: {
-    value: true,
-    type: 'boolean',
-    label: 'Show barplot',
-  },
-  itemsPerPage: {
-    value: 10,
-    type: 'dropdown',
-    label: 'Items per page',
-    options: [10, 20, 30],
-  },
-};
-
 export const TableComponent: VISComponentType = {
-  displayName: displayName,
+  displayName: 'Table',
   VIS: TableVis,
-  localConfigSchema: localConfigSchema,
+  settings: {
+    showBarplot: {
+      value: true,
+      type: 'boolean',
+      label: 'Show barplot',
+    },
+    itemsPerPage: {
+      value: 10,
+      type: 'dropdown',
+      label: 'Items per page',
+      options: [10, 20, 30],
+    },
+  },
 };
 export default TableComponent;
diff --git a/package.json b/package.json
index 7b29d489f..b03b7479a 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,7 @@
     "sb": "turbo run sb --no-daemon",
     "lint": "turbo run lint --no-daemon",
     "test": "turbo run test --no-daemon",
-    "push": "turbo run lint test build-dev --no-daemon",
+    "push": "turbo run lint test build --no-daemon",
     "format": "prettier --write \"**/*.{ts,tsx,md,js}\"",
     "prepare": "husky install"
   },
-- 
GitLab