From 60ce4ec24dcbeca6d854efcda12e20e0000559d6 Mon Sep 17 00:00:00 2001
From: Leonardo <leomilho@gmail.com>
Date: Wed, 5 Jun 2024 12:01:18 +0200
Subject: [PATCH] feat(inspector): inspector panel refactor

(cherry picked from commit f307ae4a77ef00d9ca1f41cf1f685e321170458d)
---
 apps/web/src/app/App.tsx                      |  46 +++---
 .../components/charts/Axis/axis.stories.tsx   |   2 +-
 libs/shared/lib/components/forms/index.tsx    |  51 ++++--
 libs/shared/lib/components/inputs/index.tsx   |  52 +++++--
 .../lib/components/inputs/inputs.module.scss  |  23 +++
 .../components/inputs/inputs.module.scss.d.ts |   1 +
 libs/shared/lib/components/layout/Panel.tsx   |  12 +-
 .../lib/components/layout/Resizable.tsx       |   4 +
 libs/shared/lib/components/tabs/Tab.tsx       |  29 ++++
 libs/shared/lib/components/tabs/index.ts      |   1 +
 libs/shared/lib/data-access/store/hooks.ts    |   3 +-
 .../lib/data-access/store/interactionSlice.ts |  12 +-
 libs/shared/lib/index.ts                      |   1 +
 .../lib/inspector/ConnectionInspector.tsx     |  26 ++++
 libs/shared/lib/inspector/InspectorPanel.tsx  |  76 +++++++++
 libs/shared/lib/inspector/InspectorTab.tsx    |   6 +
 libs/shared/lib/inspector/index.ts            |   2 +
 .../lib/querybuilder/panel/QueryBuilder.tsx   |  10 +-
 .../panel/querysidepanel/QuerySettings.tsx    | 102 ++++++++++++
 .../querysidepanel/querySettingsDialog.tsx    | 145 ------------------
 .../pills/pilldropdown/PillDropdown.tsx       |   2 +-
 libs/shared/lib/schema/panel/Schema.tsx       |  10 +-
 libs/shared/lib/schema/panel/SchemaDialog.tsx |  71 ---------
 .../lib/schema/panel/SchemaSettings.tsx       |  49 ++++++
 .../{panel.tsx => VisualizationPanel.tsx}     |  13 +-
 libs/shared/lib/vis/components/bar.tsx        |  13 +-
 .../config/ActiveVisualizationConfig.tsx      |  44 +++---
 .../vis/components/config/SelectionConfig.tsx |   9 +-
 .../lib/vis/components/config/index.tsx       |   2 +-
 .../lib/vis/components/config/panel.tsx       |  53 +------
 libs/shared/lib/vis/index.ts                  |   2 +-
 .../matrixvis/matrix.stories.tsx              |   2 +-
 32 files changed, 506 insertions(+), 368 deletions(-)
 create mode 100644 libs/shared/lib/components/tabs/Tab.tsx
 create mode 100644 libs/shared/lib/components/tabs/index.ts
 create mode 100644 libs/shared/lib/inspector/ConnectionInspector.tsx
 create mode 100644 libs/shared/lib/inspector/InspectorPanel.tsx
 create mode 100644 libs/shared/lib/inspector/InspectorTab.tsx
 create mode 100644 libs/shared/lib/inspector/index.ts
 create mode 100644 libs/shared/lib/querybuilder/panel/querysidepanel/QuerySettings.tsx
 delete mode 100644 libs/shared/lib/querybuilder/panel/querysidepanel/querySettingsDialog.tsx
 delete mode 100644 libs/shared/lib/schema/panel/SchemaDialog.tsx
 create mode 100644 libs/shared/lib/schema/panel/SchemaSettings.tsx
 rename libs/shared/lib/vis/components/{panel.tsx => VisualizationPanel.tsx} (66%)

diff --git a/apps/web/src/app/App.tsx b/apps/web/src/app/App.tsx
index bde42e867..cb7388cd4 100644
--- a/apps/web/src/app/App.tsx
+++ b/apps/web/src/app/App.tsx
@@ -20,8 +20,7 @@ import { URLParams, setParam } from '@graphpolaris/shared/lib/data-access/api/ur
 import { VisualizationPanel } from '@graphpolaris/shared/lib/vis';
 import { QueryBuilder } from '@graphpolaris/shared/lib/querybuilder';
 import { SideNavTab, Sidebar } from '@graphpolaris/shared/lib/sidebar';
-import { VisualizationManager } from '@graphpolaris/shared/lib/vis/manager';
-import { ConfigPanel } from '@graphpolaris/shared/lib/vis/components/config';
+import { InspectorPanel } from '@graphpolaris/shared/lib/inspector';
 import { SearchBar } from '@graphpolaris/shared/lib/sidebar/search/SearchBar';
 import { Schema } from '@graphpolaris/shared/lib/schema/panel';
 
@@ -36,7 +35,6 @@ export function App(props: App) {
   const session = useSessionCache();
   const dispatch = useAppDispatch();
   const queryBuilderSettings = useQuerybuilderSettings();
-  const manager = VisualizationManager();
   const schema = useSchemaGraph();
 
   const runQuery = () => {
@@ -58,7 +56,7 @@ export function App(props: App) {
 
   const [authCheck, setAuthCheck] = useState(false);
   const [tab, setTab] = useState<SideNavTab>('Schema');
-  const [visFullSize, setVisFullSize] = useState<boolean>(false);
+  // const [visFullSize, setVisFullSize] = useState<boolean>(false);
 
   return (
     <div className="h-screen w-screen overflow-clip">
@@ -79,30 +77,30 @@ export function App(props: App) {
               </aside>
               <main className="grow flex flex-row h-screen pt-12">
                 <Sidebar onTab={(tab) => setTab(tab)} tab={tab} />
-                <Resizable divisorSize={3} horizontal={true} defaultProportion={0.33}>
-                  {tab !== undefined ? (
-                    <div className="flex flex-col w-full h-full">
-                      {tab === 'Search' && <SearchBar onRemove={() => setTab(undefined)} />}
-                      {tab === 'Schema' && <Schema auth={authCheck} onRemove={() => setTab(undefined)} />}
-                    </div>
-                  ) : null}
+                <Resizable divisorSize={3} horizontal={true} defaultProportion={0.85} maxProportion={0.85}>
+                  <Resizable divisorSize={3} horizontal={true} defaultProportion={0.33}>
+                    {tab !== undefined ? (
+                      <div className="flex flex-col w-full h-full">
+                        {tab === 'Search' && <SearchBar onRemove={() => setTab(undefined)} />}
+                        {tab === 'Schema' && <Schema auth={authCheck} onRemove={() => setTab(undefined)} />}
+                      </div>
+                    ) : null}
 
-                  <Resizable divisorSize={3} horizontal={false}>
-                    <VisualizationPanel
-                      manager={manager}
-                      fullSize={() => {
-                        setVisFullSize(!visFullSize);
-                        tab === undefined && setTab('Schema');
-                        tab !== undefined && setTab(undefined);
-                      }}
-                    />
+                    <Resizable divisorSize={3} horizontal={false}>
+                      <VisualizationPanel
+                        fullSize={() => {
+                          // setVisFullSize(!visFullSize);
+                          // tab === undefined && setTab('Schema');
+                          // tab !== undefined && setTab(undefined);
+                        }}
+                      />
 
-                    <QueryBuilder onRunQuery={runQuery} />
+                      <QueryBuilder onRunQuery={runQuery} />
+                    </Resizable>
                   </Resizable>
+                  {/* <ConfigPanel /> */}
+                  <InspectorPanel />
                 </Resizable>
-                <div className="info-panel flex h-full w-60 ml-[3px] shrink-0 overflow-auto bg-light border">
-                  <ConfigPanel manager={manager} />
-                </div>
               </main>
             </div>
           </div>
diff --git a/libs/shared/lib/components/charts/Axis/axis.stories.tsx b/libs/shared/lib/components/charts/Axis/axis.stories.tsx
index 00c13d2fb..918d14c1d 100644
--- a/libs/shared/lib/components/charts/Axis/axis.stories.tsx
+++ b/libs/shared/lib/components/charts/Axis/axis.stories.tsx
@@ -9,7 +9,7 @@ const Component: Meta<AxisComponentProps> = {
   component: AxisComponent,
   decorators: [
     (Story) => (
-      <div className="w-full h-full flex flex-row justify-center">
+      <div className="w-full h-full flex flex-row justify-center flex-grow">
         <svg className="border border-secondary-300" width="300" height="200">
           <Story />
         </svg>
diff --git a/libs/shared/lib/components/forms/index.tsx b/libs/shared/lib/components/forms/index.tsx
index 643126cac..a4bb74327 100644
--- a/libs/shared/lib/components/forms/index.tsx
+++ b/libs/shared/lib/components/forms/index.tsx
@@ -79,27 +79,48 @@ export const FormBody = ({
     {children}
   </form>
 );
-export const FormTitle = ({ children, title, onClose }: PropsWithChildren<{ title: string; onClose: () => void }>) => {
+export const FormTitle = ({ children, title, onClose }: PropsWithChildren<{ title: string; onClose?: () => void }>) => {
   return (
     <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()} />
+      {onClose && <Button rounded variant="ghost" iconComponent={<Close />} onClick={() => onClose()} />}
     </div>
   );
 };
 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 mb-2">
-    <Button
-      type="secondary"
-      variant="outline"
-      label="Cancel"
-      onClick={(e) => {
-        e.preventDefault();
-        props.onClose();
-      }}
-    />
-    <Button type="primary" label="Apply" onClick={() => {}} />
-  </div>
+export const FormActions = (props: { onClose?: () => void; onApply?: () => void }) => (
+  <>
+    {props.onClose && (
+      <div className="grid grid-cols-2 px-5 gap-2 mb-2">
+        <Button
+          type="secondary"
+          variant="outline"
+          label="Cancel"
+          onClick={(e) => {
+            e.preventDefault();
+            if (props.onClose) props.onClose();
+          }}
+        />
+        <Button
+          type="primary"
+          label="Apply"
+          onClick={() => {
+            if (props.onApply) props.onApply();
+          }}
+          className="flex-grow"
+        />
+      </div>
+    )}
+    {!props.onClose && (
+      <Button
+        type="primary"
+        label="Apply"
+        onClick={() => {
+          if (props.onApply) props.onApply();
+        }}
+        className="w-full"
+      />
+    )}
+  </>
 );
diff --git a/libs/shared/lib/components/inputs/index.tsx b/libs/shared/lib/components/inputs/index.tsx
index 02cf813b7..cb8c073e7 100644
--- a/libs/shared/lib/components/inputs/index.tsx
+++ b/libs/shared/lib/components/inputs/index.tsx
@@ -26,6 +26,7 @@ type TextProps = {
   visible?: boolean;
   disabled?: boolean;
   tooltip?: string;
+  inline?: boolean;
   info?: string;
   className?: string;
   validate?: (value: any) => boolean;
@@ -35,6 +36,7 @@ type TextProps = {
 type NumberProps = {
   label: string;
   type: 'number';
+  size?: 'xs' | 'sm' | 'md' | 'xl';
   placeholder?: string;
   value: number;
   required?: boolean;
@@ -43,8 +45,12 @@ type NumberProps = {
   disabled?: boolean;
   tooltip?: string;
   info?: string;
+  inline?: boolean;
   validate?: (value: any) => boolean;
   onChange?: (value: number) => void;
+  onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
+  max?: number;
+  min?: number;
 };
 
 type CheckboxProps = {
@@ -84,6 +90,7 @@ type DropdownProps = {
   tooltip?: string;
   onChange?: (value: string | number) => void;
   required?: boolean;
+  inline?: boolean;
   info?: string;
   disabled?: boolean;
 };
@@ -151,16 +158,21 @@ export const TextInput = ({
   validate,
   disabled = false,
   onChange,
+  inline = false,
   tooltip,
   info,
   className,
 }: TextProps) => {
   const [isValid, setIsValid] = React.useState<boolean>(true);
 
+  if (!tooltip && inline) tooltip = label;
   return (
-    <div data-tip={tooltip || null} className={'form-control w-full' + (tooltip ? ' tooltip' : '')}>
+    <div
+      data-tip={tooltip || null}
+      className={'form-control w-full' + (inline ? ' grid grid-cols-2 items-center' : '') + (tooltip ? ' tooltip' : '')}
+    >
       {label && (
-        <label className="label">
+        <label className="label p-0">
           <span
             className={`text-sm font-medium text-secondary-700 ${required && "after:content-['*'] after:ml-0.5 after:text-danger-500"}`}
           >
@@ -197,7 +209,9 @@ export const TextInput = ({
 export const NumberInput = ({
   label,
   placeholder,
+  size = 'md',
   value = 0,
+  inline = false,
   required = false,
   visible = true,
   errorText,
@@ -206,13 +220,22 @@ export const NumberInput = ({
   onChange,
   tooltip,
   info,
+  onKeyDown,
+  max,
+  min,
 }: NumberProps) => {
   const [isValid, setIsValid] = React.useState<boolean>(true);
 
+  if (!tooltip && inline) tooltip = label;
   return (
-    <div data-tip={tooltip || null} className={'form-control w-full' + (tooltip ? ' tooltip' : '')}>
-      <label className="label">
-        <span className={`text-sm font-medium text-secondary-700 ${required && "after:content-['*'] after:ml-0.5 after:text-danger-500"}`}>
+    <div
+      data-tip={tooltip || null}
+      className={styles['input'] + ' form-control w-full' + (inline ? ' grid grid-cols-2 items-center' : '') + (tooltip ? ' tooltip' : '')}
+    >
+      <label className="label p-0">
+        <span
+          className={`text-sm text-left truncate font-medium text-secondary-700 ${required && "after:content-['*'] after:ml-0.5 after:text-danger-500"}`}
+        >
           {label}
         </span>
         {required && isValid ? null : <span className="label-text-alt text-error">{errorText}</span>}
@@ -221,7 +244,7 @@ export const NumberInput = ({
       <input
         type="number"
         placeholder={placeholder}
-        className={`px-3 py-2 bg-light border border-secondary-300 placeholder-secondary-400 focus:outline-none block w-full sm:text-sm focus:ring-1 ${
+        className={`${size} bg-light border border-secondary-300 placeholder-secondary-400 focus:outline-none block w-full sm:text-sm focus:ring-1 ${
           isValid ? '' : 'input-error'
         }`}
         value={value.toString()}
@@ -235,6 +258,9 @@ export const NumberInput = ({
         }}
         required={required}
         disabled={disabled}
+        onKeyDown={onKeyDown}
+        max={max}
+        min={min}
       />
     </div>
   );
@@ -243,7 +269,7 @@ export const NumberInput = ({
 export const RadioInput = ({ label, value, options, onChange, tooltip }: RadioProps) => {
   return (
     <div data-tip={tooltip || null} className={tooltip ? 'tooltip' : ''}>
-      <label className="label">
+      <label className="label p-0">
         <span className="label-text">{label}</span>
       </label>
       {options.map((option, index) => (
@@ -270,7 +296,7 @@ export const CheckboxInput = ({ label, value, options, onChange, tooltip }: Chec
   return (
     <div data-tip={tooltip || null} className={tooltip ? 'tooltip' : ''}>
       {label && (
-        <label className="label">
+        <label className="label p-0">
           <span className="label-text">{label}</span>
         </label>
       )}
@@ -334,12 +360,14 @@ export const DropDownInput = ({
   required = false,
   tooltip,
   size = 'sm',
+  inline = false,
   disabled = false,
   info,
 }: DropdownProps) => {
   const dropdownRef = React.useRef<HTMLDivElement>(null);
   const [isDropdownOpen, setIsDropdownOpen] = React.useState<boolean>(false);
 
+  if (!tooltip && inline) tooltip = label;
   React.useEffect(() => {
     const handleClickOutside = (event: MouseEvent) => {
       if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
@@ -353,18 +381,18 @@ export const DropDownInput = ({
   }, [isDropdownOpen]);
 
   return (
-    <div data-tip={tooltip || null} className={'w-full' + (tooltip ? ' tooltip' : '')}>
+    <div data-tip={tooltip || null} className={'w-full' + (inline ? ' grid grid-cols-2 items-center' : '') + (tooltip ? ' tooltip' : '')}>
       {label && (
-        <label className="label">
+        <label className="label p-0">
           <span
-            className={`text-sm font-medium text-secondary-700 ${required && "after:content-['*'] after:ml-0.5 after:text-danger-500"}`}
+            className={`text-sm text-left truncate font-medium text-secondary-700 ${required && "after:content-['*'] after:ml-0.5 after:text-danger-500"}`}
           >
             {label}
           </span>
           {info && <Info tooltip={info} />}
         </label>
       )}
-      <DropdownContainer className="w-full" ref={dropdownRef}>
+      <DropdownContainer className="w-full right-0 left-auto" ref={dropdownRef}>
         <DropdownButton
           title={overrideRender || value}
           size={size}
diff --git a/libs/shared/lib/components/inputs/inputs.module.scss b/libs/shared/lib/components/inputs/inputs.module.scss
index 32f8e811d..f1733a091 100644
--- a/libs/shared/lib/components/inputs/inputs.module.scss
+++ b/libs/shared/lib/components/inputs/inputs.module.scss
@@ -21,3 +21,26 @@
     }
   }
 }
+
+.input {
+  input[class~='xs'] {
+    @apply py-0;
+    @apply px-0;
+  }
+  input[class~='sm'] {
+    @apply py-1;
+    @apply px-1;
+  }
+  input[class~='md'] {
+    @apply py-2;
+    @apply px-3;
+  }
+  input[class~='md'] {
+    @apply py-2;
+    @apply px-3;
+  }
+  input[class~='xl'] {
+    @apply py-3;
+    @apply px-5;
+  }
+}
diff --git a/libs/shared/lib/components/inputs/inputs.module.scss.d.ts b/libs/shared/lib/components/inputs/inputs.module.scss.d.ts
index c7907e61e..8c532fb8c 100644
--- a/libs/shared/lib/components/inputs/inputs.module.scss.d.ts
+++ b/libs/shared/lib/components/inputs/inputs.module.scss.d.ts
@@ -1,4 +1,5 @@
 declare const classNames: {
   readonly slider: 'slider';
+  readonly input: 'input';
 };
 export = classNames;
diff --git a/libs/shared/lib/components/layout/Panel.tsx b/libs/shared/lib/components/layout/Panel.tsx
index 191afbf50..34556eac9 100644
--- a/libs/shared/lib/components/layout/Panel.tsx
+++ b/libs/shared/lib/components/layout/Panel.tsx
@@ -2,8 +2,8 @@ import React, { useEffect, useState } from 'react';
 import { ControlContainer } from '..';
 
 export type Panel = {
-  title: string;
-  tooltips: React.ReactNode;
+  title: string | React.ReactNode;
+  tooltips?: React.ReactNode;
   children: React.ReactNode;
 };
 
@@ -14,9 +14,11 @@ export function Panel(props: Panel) {
         <div className="flex items-center">
           <h1 className="text-xs font-semibold text-secondary-600 px-2 truncate">{props.title}</h1>
         </div>
-        <div className="shrink-0 sticky right-0 px-0.5 ml-auto items-center flex">
-          <ControlContainer>{props.tooltips}</ControlContainer>
-        </div>
+        {props.tooltips && (
+          <div className="shrink-0 sticky right-0 px-0.5 ml-auto items-center flex">
+            <ControlContainer>{props.tooltips}</ControlContainer>
+          </div>
+        )}
       </div>
       {props.children}
     </div>
diff --git a/libs/shared/lib/components/layout/Resizable.tsx b/libs/shared/lib/components/layout/Resizable.tsx
index 9b88169e6..061620796 100644
--- a/libs/shared/lib/components/layout/Resizable.tsx
+++ b/libs/shared/lib/components/layout/Resizable.tsx
@@ -9,6 +9,7 @@ type Props = {
   defaultProportion?: number;
   classNameLeft?: string;
   classNameRight?: string;
+  maxProportion?: number;
 };
 
 function convertRemToPixels(rem: number) {
@@ -24,6 +25,7 @@ export const Resizable = ({
   defaultProportion,
   classNameLeft,
   classNameRight,
+  maxProportion,
   ...props
 }: Props) => {
   const ref = useRef<HTMLDivElement>(null);
@@ -81,11 +83,13 @@ export const Resizable = ({
 
         if (horizontal) {
           const newFirstSize = Math.max(minSizeX, relativeX);
+          if (maxProportion && newFirstSize / rect.width > maxProportion) return;
           setFirstSize(newFirstSize);
           setSecondSize(Math.max(minSizeX, rect.width - relativeX));
           setCurrentProportion(newFirstSize / rect.width);
         } else {
           const newFirstSize = Math.max(minSizeY, relativeY);
+          if (maxProportion && newFirstSize / rect.height > maxProportion) return;
           setFirstSize(newFirstSize);
           setSecondSize(Math.max(minSizeY, rect.height - relativeY));
           setCurrentProportion(newFirstSize / rect.height);
diff --git a/libs/shared/lib/components/tabs/Tab.tsx b/libs/shared/lib/components/tabs/Tab.tsx
new file mode 100644
index 000000000..855da1029
--- /dev/null
+++ b/libs/shared/lib/components/tabs/Tab.tsx
@@ -0,0 +1,29 @@
+import React, { MouseEventHandler } from 'react';
+
+export const Tabs = (props: { children: React.ReactNode }) => {
+  return (
+    <div className="flex items-stretch divide-x divide-secondary-200 border-x border-secondary-200 overflow-x-auto -my-px">
+      {props.children}
+    </div>
+  );
+};
+
+export const Tab = (
+  props: React.ButtonHTMLAttributes<HTMLDivElement> & {
+    active: boolean;
+    children: React.ReactNode;
+    text: string;
+    key?: string;
+  },
+) => {
+  return (
+    <div
+      key={props.key}
+      className={`flex items-center pl-2 pr-1 gap-1 cursor-pointer relative border-secondary-200 before:content-[''] before:absolute before:left-0 before:bottom-0 before:h-[2px] before:w-full ${props.active ? 'before:bg-primary-500' : 'before:bg-transparent hover:before:bg-secondary-300 hover:bg-secondary-200'}`}
+      {...props}
+    >
+      <p className={`text-xs text-secondary-500 font-semibold ${props.active && 'text-secondary-950'}`}>{props.text}</p>
+      {props.children}
+    </div>
+  );
+};
diff --git a/libs/shared/lib/components/tabs/index.ts b/libs/shared/lib/components/tabs/index.ts
new file mode 100644
index 000000000..2e2986cb5
--- /dev/null
+++ b/libs/shared/lib/components/tabs/index.ts
@@ -0,0 +1 @@
+export * from './Tab';
diff --git a/libs/shared/lib/data-access/store/hooks.ts b/libs/shared/lib/data-access/store/hooks.ts
index 247785872..3cf9e53fa 100644
--- a/libs/shared/lib/data-access/store/hooks.ts
+++ b/libs/shared/lib/data-access/store/hooks.ts
@@ -29,7 +29,7 @@ import { AllLayoutAlgorithms } from '../../graph-layout';
 import { QueryGraphEdgeHandle, QueryMultiGraph } from '../../querybuilder';
 import { SchemaGraph } from '../../schema';
 import { GraphMetadata } from '../statistics';
-import { SelectionStateI, selectionState } from './interactionSlice';
+import { SelectionStateI, FocusStateI, focusState, selectionState } from './interactionSlice';
 
 // Use throughout your app instead of plain `useDispatch` and `useSelector`
 export const useAppDispatch: () => AppDispatch = useDispatch;
@@ -73,3 +73,4 @@ export const useVisualization: () => VisState = () => useAppSelector(visualizati
 
 // Interaction Slices
 export const useSelection: () => SelectionStateI | undefined = () => useAppSelector(selectionState);
+export const useFocus: () => FocusStateI | undefined = () => useAppSelector(focusState);
diff --git a/libs/shared/lib/data-access/store/interactionSlice.ts b/libs/shared/lib/data-access/store/interactionSlice.ts
index 4a7434768..145cd7a32 100644
--- a/libs/shared/lib/data-access/store/interactionSlice.ts
+++ b/libs/shared/lib/data-access/store/interactionSlice.ts
@@ -17,15 +17,21 @@ export type SelectionStateI =
       content: Edge[];
     };
 
+export type FocusStateI = {
+  focusType: 'schema' | 'query' | 'visualization';
+};
+
 // Define the initial state using that type
 export type InteractionsType = {
   hover?: HoverStateI;
   selection?: SelectionStateI;
+  focus?: FocusStateI;
 };
 
 export const initialState: InteractionsType = {
   hover: undefined,
   selection: undefined,
+  focus: undefined,
 };
 
 export const interactionSlice = createSlice({
@@ -50,12 +56,16 @@ export const interactionSlice = createSlice({
         };
       }
     },
+    resultSetFocus: (state, action: PayloadAction<FocusStateI | undefined>) => {
+      state.focus = action.payload;
+    },
   },
 });
 
-export const { addHover, unSelect, resultSetSelection } = interactionSlice.actions;
+export const { addHover, unSelect, resultSetSelection, resultSetFocus } = interactionSlice.actions;
 
 export const interactionState = (state: RootState) => state.interaction;
 export const selectionState = (state: RootState) => state.interaction.selection;
+export const focusState = (state: RootState) => state.interaction.focus;
 
 export default interactionSlice.reducer;
diff --git a/libs/shared/lib/index.ts b/libs/shared/lib/index.ts
index 82d44d1ea..eae28d420 100644
--- a/libs/shared/lib/index.ts
+++ b/libs/shared/lib/index.ts
@@ -4,3 +4,4 @@ export * from './graph-layout';
 export * from './querybuilder';
 export * from './schema';
 export * from './vis';
+export * from './inspector';
diff --git a/libs/shared/lib/inspector/ConnectionInspector.tsx b/libs/shared/lib/inspector/ConnectionInspector.tsx
new file mode 100644
index 000000000..16988b41f
--- /dev/null
+++ b/libs/shared/lib/inspector/ConnectionInspector.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import { useSessionCache } from '../data-access';
+
+export function ConnectionInspector() {
+  const session = useSessionCache();
+
+  return (
+    <div>
+      {session && session.currentSaveState && (
+        <div className="flex flex-col p-4 border-b">
+          <span className="text-sm font-bold">Connection details</span>
+          <span className="text-xs font-semibold">Name</span>
+          <span className="text-xs">{session.saveStates[session.currentSaveState].name}</span>
+          <span className="text-xs font-semibold">Database</span>
+          <span className="text-xs">{session.saveStates[session.currentSaveState].db.internalDatabaseName}</span>
+          <span className="text-xs font-semibold">Protocol</span>
+          <span className="text-xs">{session.saveStates[session.currentSaveState].db.protocol}</span>
+          <span className="text-xs font-semibold">Hostname</span>
+          <span className="text-xs">{session.saveStates[session.currentSaveState].db.url}</span>
+          <span className="text-xs font-semibold">Port</span>
+          <span className="text-xs">{session.saveStates[session.currentSaveState].db.port}</span>
+        </div>
+      )}
+    </div>
+  );
+}
diff --git a/libs/shared/lib/inspector/InspectorPanel.tsx b/libs/shared/lib/inspector/InspectorPanel.tsx
new file mode 100644
index 000000000..703af6cee
--- /dev/null
+++ b/libs/shared/lib/inspector/InspectorPanel.tsx
@@ -0,0 +1,76 @@
+import React, { useMemo } from 'react';
+import { Button, Panel } from '../components';
+import { useFocus, useSelection } from '../data-access';
+import { resultSetFocus } from '../data-access/store/interactionSlice';
+import { useDispatch } from 'react-redux';
+import { ConnectionInspector } from './ConnectionInspector';
+import { VisualizationConfigPanel } from '../vis/components/config/panel';
+import { SelectionConfig } from '../vis/components/config/SelectionConfig';
+import { SchemaDialog } from '../schema/panel/SchemaSettings';
+import { QuerySettings } from '../querybuilder/panel/querysidepanel/QuerySettings';
+
+export function InspectorPanel(props: { children?: React.ReactNode }) {
+  const buildInfo = import.meta.env.GRAPHPOLARIS_VERSION;
+  const selection = useSelection();
+  const focus = useFocus();
+  const dispatch = useDispatch();
+
+  const inspector = useMemo(() => {
+    if (selection) return <SelectionConfig />;
+    if (!focus) return <ConnectionInspector />;
+    if (focus.focusType === 'visualization') return <VisualizationConfigPanel />;
+    else if (focus.focusType === 'schema') return <SchemaDialog />;
+    else if (focus.focusType === 'query') return <QuerySettings />;
+    return null;
+  }, [focus, selection]);
+
+  return (
+    <Panel
+      title={
+        <div className="flex flex-row gap-0.5 items-center align-middle">
+          <Button variant="ghost" size="2xs" className="hover:underline p-0" onClick={() => dispatch(resultSetFocus(undefined))}>
+            GP
+          </Button>
+          {focus && (
+            <>
+              <span className="pb-0.5">{'>'}</span>
+              <Button variant="ghost" size="2xs" className="hover:underline p-0">
+                {focus.focusType}
+              </Button>
+            </>
+          )}
+          {selection && (
+            <>
+              <span className="pb-0.5">{'>'}</span>
+              <Button variant="ghost" size="2xs" className="hover:underline p-0">
+                Selection
+              </Button>
+            </>
+          )}
+        </div>
+      }
+    >
+      {inspector}
+
+      <div className="flex flex-col w-full">
+        {buildInfo === 'dev' && (
+          <div className="mt-auto p-2 bg-light">
+            <Button
+              type="primary"
+              variant="outline"
+              size="xs"
+              label="Report an issue"
+              onClick={() =>
+                window.open(
+                  'https://app.asana.com/-/login?u=https%3A%2F%2Fform.asana.com%2F%3Fk%3D2QEC88Dl7ETs2wYYWjkMXg%26d%3D1206648675960041&error=01',
+                  '_blank',
+                )
+              }
+              className="block w-full"
+            />
+          </div>
+        )}
+      </div>
+    </Panel>
+  );
+}
diff --git a/libs/shared/lib/inspector/InspectorTab.tsx b/libs/shared/lib/inspector/InspectorTab.tsx
new file mode 100644
index 000000000..b2f11a384
--- /dev/null
+++ b/libs/shared/lib/inspector/InspectorTab.tsx
@@ -0,0 +1,6 @@
+import React from 'react';
+import { Button } from '../components';
+
+export function InspectorTab(props: { children: React.ReactNode }) {
+  return <div className="flex flex-col w-full"></div>;
+}
diff --git a/libs/shared/lib/inspector/index.ts b/libs/shared/lib/inspector/index.ts
new file mode 100644
index 000000000..83f3c61a6
--- /dev/null
+++ b/libs/shared/lib/inspector/index.ts
@@ -0,0 +1,2 @@
+export * from './InspectorPanel';
+export * from './InspectorTab';
diff --git a/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx b/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx
index 5c3b9a459..f5b3872b7 100644
--- a/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx
+++ b/libs/shared/lib/querybuilder/panel/QueryBuilder.tsx
@@ -38,10 +38,10 @@ import styles from './querybuilder.module.scss';
 import { QueryBuilderLogicPillsPanel } from './querysidepanel/queryBuilderLogicPillsPanel';
 import { QueryBuilderRelatedNodesPanel } from './querysidepanel/queryBuilderRelatedNodesPanel';
 import { QueryMLDialog } from './querysidepanel/queryMLDialog';
-import { QuerySettingsDialog } from './querysidepanel/querySettingsDialog';
 import { ConnectingNodeDataI } from './utils/connectorDrop';
 import { CameraAlt, Cached, Difference, ImportExport, Lightbulb, Settings, Fullscreen, Delete } from '@mui/icons-material';
 import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../components/tooltip';
+import { resultSetFocus } from '../../data-access/store/interactionSlice';
 
 export type QueryBuilderProps = {
   onRunQuery?: () => void;
@@ -428,12 +428,15 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
   };
 
   useEffect(() => {
-    applyLayout();
+    try {
+      applyLayout();
+    } catch (e) {
+      console.error(e);
+    }
   }, [queryBuilderSettings]);
 
   return (
     <div ref={reactFlowWrapper} className="h-full w-full flex flex-col">
-      <QuerySettingsDialog open={toggleSettings === 'settings'} onClose={() => setToggleSettings(undefined)} />
       <QueryMLDialog open={toggleSettings === 'ml'} onClose={() => setToggleSettings(undefined)} />
 
       <div className="sticky shrink-0 top-0 flex items-stretch justify-between h-7 bg-secondary-100 border-b border-secondary-200 max-w-full">
@@ -623,6 +626,7 @@ export const QueryBuilderInner = (props: QueryBuilderProps) => {
         nodeTypes={nodeTypes}
         edgeTypes={edgeTypes}
         connectionLineComponent={ConnectionDragLine}
+        onMouseDownCapture={() => dispatch(resultSetFocus({ focusType: 'query' }))}
         // connectionMode={ConnectionMode.Loose}
         onInit={(reactFlowInstance) => {
           reactFlowInstanceRef.current = reactFlowInstance;
diff --git a/libs/shared/lib/querybuilder/panel/querysidepanel/QuerySettings.tsx b/libs/shared/lib/querybuilder/panel/querysidepanel/QuerySettings.tsx
new file mode 100644
index 000000000..d3b36d1d9
--- /dev/null
+++ b/libs/shared/lib/querybuilder/panel/querysidepanel/QuerySettings.tsx
@@ -0,0 +1,102 @@
+import { useEffect } from 'react';
+import React from 'react';
+import { useAppDispatch, useQuerybuilderSettings } from '../../../data-access';
+import { QueryBuilderSettings, setQuerybuilderSettings } from '../../../data-access/store/querybuilderSlice';
+import { addWarning } from '../../../data-access/store/configSlice';
+import { FormActions, FormBody, FormCard, FormControl, FormDiv, FormHBar, FormTitle } from '../../../components/forms';
+import { Layouts } from '@graphpolaris/shared/lib/graph-layout';
+import { Input } from '@graphpolaris/shared/lib/components/inputs';
+
+export const QuerySettings = React.forwardRef<HTMLDivElement, {}>((props, ref) => {
+  const qb = useQuerybuilderSettings();
+  const dispatch = useAppDispatch();
+  const [state, setState] = React.useState<QueryBuilderSettings>(qb);
+
+  useEffect(() => {
+    setState(qb);
+  }, [qb]);
+
+  function submit() {
+    if (state.depth.min < 0) {
+      dispatch(addWarning('The minimum depth cannot be smaller than 0'));
+    } else if (state.depth.max > 99) {
+      dispatch(addWarning('The maximum depth cannot be larger than 99'));
+    } else if (state.depth.min > state.depth.max) {
+      dispatch(addWarning('The minimum depth cannot be larger than the maximum depth'));
+    } else {
+      dispatch(setQuerybuilderSettings(state));
+    }
+  }
+
+  return (
+    <div className="flex flex-col w-full gap-2 px-4 py-2">
+      <span className="text-xs font-bold">Query Settings</span>
+      <Input
+        type="boolean"
+        value={state.autocompleteRelation}
+        label="Autocomplete Edges"
+        tooltip="When enabled, if you drag a relationship to the query, the query builder will automatically add the entity nodes it is connected to."
+        onChange={(value: boolean) => {
+          setState({ ...state, autocompleteRelation: value as any });
+        }}
+      />
+
+      <Input
+        type="number"
+        tooltip="The maximum number of results to return"
+        label="Limit"
+        inline
+        size="sm"
+        value={state.limit}
+        onChange={(e) => setState({ ...state, limit: e })}
+      />
+      <Input
+        type="number"
+        label="Min Depth Default"
+        size="sm"
+        inline
+        value={state.depth.min}
+        onChange={(e) => setState({ ...state, depth: { min: e, max: state.depth.max } })}
+        placeholder="0"
+        min={0}
+        max={state.depth.max}
+        onKeyDown={(e) => {
+          if (e.key === 'Enter') {
+            submit();
+          }
+        }}
+      />
+      <Input
+        type="number"
+        label="Max Depth Default"
+        size="sm"
+        inline
+        value={state.depth.max}
+        onChange={(e) => setState({ ...state, depth: { max: e, min: state.depth.min } })}
+        placeholder="0"
+        min={state.depth.min}
+        max={99}
+        onKeyDown={(e) => {
+          if (e.key === 'Enter') {
+            submit();
+          }
+        }}
+      />
+
+      <Input
+        type="dropdown"
+        inline
+        label="Default Layout"
+        value={state.layout}
+        onChange={(e) => setState({ ...state, layout: e as any })}
+        options={['manual', ...Object.entries(Layouts).map(([k, v]) => v)]}
+      />
+
+      <FormActions
+        onApply={() => {
+          submit();
+        }}
+      />
+    </div>
+  );
+});
diff --git a/libs/shared/lib/querybuilder/panel/querysidepanel/querySettingsDialog.tsx b/libs/shared/lib/querybuilder/panel/querysidepanel/querySettingsDialog.tsx
deleted file mode 100644
index 81e7567c1..000000000
--- a/libs/shared/lib/querybuilder/panel/querysidepanel/querySettingsDialog.tsx
+++ /dev/null
@@ -1,145 +0,0 @@
-import { useEffect } from 'react';
-import { DialogProps } from '../../../components/layout/Dialog';
-import React from 'react';
-import { useAppDispatch, useQuerybuilderSettings } from '../../../data-access';
-import { QueryBuilderSettings, setQuerybuilderSettings } from '../../../data-access/store/querybuilderSlice';
-import { addWarning } from '../../../data-access/store/configSlice';
-import { FormActions, FormBody, FormCard, FormControl, FormDiv, FormHBar, FormTitle } from '../../../components/forms';
-import { Layouts } from '@graphpolaris/shared/lib/graph-layout';
-import { Input } from '@graphpolaris/shared/lib/components/inputs';
-
-type QuerySettingsDialogProps = DialogProps;
-
-export const QuerySettingsDialog = React.forwardRef<HTMLDivElement, QuerySettingsDialogProps>((props, ref) => {
-  const qb = useQuerybuilderSettings();
-  const dispatch = useAppDispatch();
-  const [state, setState] = React.useState<QueryBuilderSettings>(qb);
-
-  useEffect(() => {
-    setState(qb);
-  }, [qb, props.open]);
-
-  function submit() {
-    if (state.depth.min < 0) {
-      dispatch(addWarning('The minimum depth cannot be smaller than 0'));
-    } else if (state.depth.max > 99) {
-      dispatch(addWarning('The maximum depth cannot be larger than 99'));
-    } else if (state.depth.min > state.depth.max) {
-      dispatch(addWarning('The minimum depth cannot be larger than the maximum depth'));
-    } else {
-      dispatch(setQuerybuilderSettings(state));
-      props.onClose();
-    }
-  }
-
-  return (
-    <>
-      {props.open && (
-        <FormDiv hAnchor="right" ref={ref}>
-          <FormCard>
-            <FormBody
-              onSubmit={(e) => {
-                e.preventDefault();
-                submit();
-              }}
-            >
-              <FormTitle title="Query Settings" onClose={props.onClose} />
-              <FormHBar />
-              <FormControl>
-                <Input
-                  type="boolean"
-                  value={state.autocompleteRelation}
-                  label="Autocomplete Edges"
-                  tooltip="When enabled, if you drag a relationship to the query, the query builder will automatically add the entity nodes it is connected to."
-                  onChange={(value: boolean) => {
-                    setState({ ...state, autocompleteRelation: value as any });
-                  }}
-                />
-              </FormControl>
-
-              <FormHBar />
-              <div className="form-control px-5">
-                <label className="label">
-                  <span className="label-text">Limit - Max number of results</span>
-                </label>
-                <input
-                  type="number"
-                  className="input input-sm input-bordered"
-                  placeholder="500"
-                  value={state.limit}
-                  onChange={(e) => setState({ ...state, limit: parseInt(e.target.value) })}
-                />
-              </div>
-              <FormHBar />
-              <div className="form-control px-5 flex flex-row gap-3">
-                <div className="">
-                  <label className="label">
-                    <span className="label-text">Min Depth Default</span>
-                  </label>
-                  <input
-                    type="number"
-                    className="input input-sm input-bordered w-full"
-                    placeholder="0"
-                    min={0}
-                    max={state.depth.max}
-                    value={state.depth.min}
-                    onChange={(e) => setState({ ...state, depth: { min: parseInt(e.target.value), max: state.depth.max } })}
-                    onKeyDown={(e) => {
-                      if (e.key === 'Enter') {
-                        submit();
-                      }
-                    }}
-                  />
-                </div>
-                <div className="">
-                  <label className="label">
-                    <span className="label-text">Max Depth Default</span>
-                  </label>
-                  <input
-                    type="number"
-                    className="input input-sm input-bordered w-full"
-                    placeholder="0"
-                    min={state.depth.min}
-                    max={99}
-                    value={state.depth.max}
-                    onChange={(e) => setState({ ...state, depth: { max: parseInt(e.target.value), min: state.depth.min } })}
-                    onKeyDown={(e) => {
-                      if (e.key === 'Enter') {
-                        submit();
-                      }
-                    }}
-                  />
-                </div>
-              </div>
-              <FormHBar />
-              <div className="form-control px-5 ">
-                <label className="label">
-                  <span className="label-text">Layout Type</span>
-                </label>
-                <select
-                  className="select select-primary select-sm "
-                  value={state.layout}
-                  onChange={(e) => {
-                    setState({ ...state, layout: e.target.value as any });
-                  }}
-                >
-                  <option className="option" value={'manual'}>
-                    Manual
-                  </option>
-                  {Object.entries(Layouts).map(([k, v]) => (
-                    <option className="option" value={v} key={v}>
-                      {k}
-                    </option>
-                  ))}
-                </select>
-              </div>
-              <FormHBar />
-
-              <FormActions onClose={props.onClose} />
-            </FormBody>
-          </FormCard>
-        </FormDiv>
-      )}
-    </>
-  );
-});
diff --git a/libs/shared/lib/querybuilder/pills/pilldropdown/PillDropdown.tsx b/libs/shared/lib/querybuilder/pills/pilldropdown/PillDropdown.tsx
index d96f52f5e..bb72abda7 100644
--- a/libs/shared/lib/querybuilder/pills/pilldropdown/PillDropdown.tsx
+++ b/libs/shared/lib/querybuilder/pills/pilldropdown/PillDropdown.tsx
@@ -46,7 +46,7 @@ export const PillDropdown = (props: PillDropdownProps) => {
 
   return (
     <div
-      className={'border-[1px] border-secondary-200 divide-y divide-secondary-200'}
+      className={'border-[1px] border-secondary-200 divide-y divide-secondary-200 !z-50'}
       onMouseEnter={(e) => {
         if (props.onMouseEnterDropdown) props.onMouseEnterDropdown(e);
       }}
diff --git a/libs/shared/lib/schema/panel/Schema.tsx b/libs/shared/lib/schema/panel/Schema.tsx
index 2e29acd26..bb9a9c409 100644
--- a/libs/shared/lib/schema/panel/Schema.tsx
+++ b/libs/shared/lib/schema/panel/Schema.tsx
@@ -9,12 +9,14 @@ import { NodeEdge } from '../pills/edges/node-edge';
 import { SelfEdge } from '../pills/edges/self-edge';
 import { SchemaEntityPill } from '../pills/nodes/entity/SchemaEntityPill';
 import { SchemaRelationPill } from '../pills/nodes/relation/SchemaRelationPill';
-import { SchemaDialog } from './SchemaDialog';
+import { SchemaDialog } from './SchemaSettings';
 import { ContentCopy, FitScreen, Fullscreen, KeyboardArrowDown, KeyboardArrowRight, Remove } from '@mui/icons-material';
 import { AlgorithmToLayoutProvider, AllLayoutAlgorithms, LayoutFactory } from '../../graph-layout';
 import { ConnectionLine, ConnectionDragLine } from '../../querybuilder';
 import { schemaExpandRelation, schemaGraphology2Reactflow } from '../schema-utils';
 import { Panel, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../components';
+import { resultSetFocus } from '../../data-access/store/interactionSlice';
+import { useDispatch } from 'react-redux';
 
 interface Props {
   content?: string;
@@ -42,6 +44,7 @@ const edgeTypes = {
 export const Schema = (props: Props) => {
   const settings = useSchemaSettings();
   const searchResults = useSearchResultSchema();
+  const dispatch = useDispatch();
   const [toggleSchemaSettings, setToggleSchemaSettings] = useState(false);
   const [nodes, setNodes, onNodesChange] = useNodesState([] as Node[]);
   const [edges, setEdges, onEdgesChange] = useEdgesState([] as Edge[]);
@@ -173,6 +176,7 @@ export const Schema = (props: Props) => {
               connectionLineComponent={ConnectionDragLine}
               onNodesChange={onNodesChange}
               onEdgesChange={onEdgesChange}
+              onMouseDownCapture={() => dispatch(resultSetFocus({ focusType: 'schema' }))}
               nodes={nodes}
               edges={edges}
               onInit={(reactFlowInstance) => {
@@ -183,7 +187,7 @@ export const Schema = (props: Props) => {
             ></ReactFlow>
           </ReactFlowProvider>
         )}
-        <div>
+        {/* <div>
           <div
             className="w-full py-0 px-2 bg-secondary-50 cursor-pointer border-y flex items-center gap-1"
             onClick={() => setExpanded(!expanded)}
@@ -201,7 +205,7 @@ export const Schema = (props: Props) => {
               <SchemaDialog open={toggleSchemaSettings} onClose={() => setToggleSchemaSettings(false)} />
             </div>
           )}
-        </div>
+        </div> */}
       </div>
     </Panel>
   );
diff --git a/libs/shared/lib/schema/panel/SchemaDialog.tsx b/libs/shared/lib/schema/panel/SchemaDialog.tsx
deleted file mode 100644
index 492e7be6e..000000000
--- a/libs/shared/lib/schema/panel/SchemaDialog.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import { useEffect, useState } from 'react';
-import { Dialog, DialogProps } from '../../components/layout/Dialog';
-import React from 'react';
-import { useAppDispatch, useSchemaSettings } from '../../data-access';
-import { SchemaConnectionTypes, SchemaSettings, schemaConnectionTypeArray, setSchemaSettings } from '../../data-access/store/schemaSlice';
-import { FormActions, FormBody, FormCard, FormControl, FormHBar, FormTitle, FormDiv } from '../../components/forms';
-import { Layouts } from '../../graph-layout';
-import { Input } from '../../components/inputs';
-
-export const SchemaDialog = (props: DialogProps) => {
-  const settings = useSchemaSettings();
-  const dispatch = useAppDispatch();
-  const [state, setState] = React.useState<SchemaSettings>(settings);
-
-  useEffect(() => {
-    setState(settings);
-  }, [settings, props.open]);
-
-  function submit() {
-    dispatch(setSchemaSettings(state));
-    props.onClose();
-  }
-
-  return (
-    <form
-      className="w-full"
-      onSubmit={(e) => {
-        e.preventDefault();
-        submit();
-      }}
-    >
-      <FormControl>
-        <Input
-          type="dropdown"
-          label="Type of Connection"
-          value={state.connectionType}
-          options={schemaConnectionTypeArray}
-          onChange={(value: string | number) => {
-            setState({ ...state, connectionType: value as SchemaConnectionTypes });
-          }}
-        />
-      </FormControl>
-      <FormHBar />
-      <FormControl>
-        <Input
-          type="boolean"
-          value={state.animatedEdges}
-          label="Animated Edges"
-          onChange={(value: boolean) => {
-            setState({ ...state, animatedEdges: value as any });
-          }}
-        />
-      </FormControl>
-      <FormHBar />
-      <FormControl>
-        <Input
-          type="dropdown"
-          label="Layout Type"
-          value={state.layoutName}
-          options={Object.values(Layouts)}
-          onChange={(value: string | number) => {
-            setState({ ...state, layoutName: value as any });
-          }}
-        />
-      </FormControl>
-      <FormHBar />
-
-      <FormActions onClose={props.onClose} />
-    </form>
-  );
-};
diff --git a/libs/shared/lib/schema/panel/SchemaSettings.tsx b/libs/shared/lib/schema/panel/SchemaSettings.tsx
new file mode 100644
index 000000000..270cb50d4
--- /dev/null
+++ b/libs/shared/lib/schema/panel/SchemaSettings.tsx
@@ -0,0 +1,49 @@
+import { useEffect, useState } from 'react';
+import { Dialog, DialogProps } from '../../components/layout/Dialog';
+import React from 'react';
+import { useAppDispatch, useSchemaSettings } from '../../data-access';
+import { SchemaConnectionTypes, SchemaSettings, schemaConnectionTypeArray, setSchemaSettings } from '../../data-access/store/schemaSlice';
+import { FormActions, FormBody, FormCard, FormControl, FormHBar, FormTitle, FormDiv } from '../../components/forms';
+import { Layouts } from '../../graph-layout';
+import { Input } from '../../components/inputs';
+
+export const SchemaDialog = () => {
+  const settings = useSchemaSettings();
+  const dispatch = useAppDispatch();
+
+  return (
+    <div className="flex flex-col w-full gap-2 px-4 py-2">
+      <span className="text-xs font-bold">Schema Settings</span>
+      <Input
+        type="boolean"
+        value={settings.animatedEdges}
+        label="Animated Edges"
+        onChange={(value: boolean) => {
+          dispatch(setSchemaSettings({ ...settings, animatedEdges: value as any }));
+        }}
+      />
+      <Input
+        type="dropdown"
+        label="Type of Connection"
+        inline
+        size="sm"
+        value={settings.connectionType}
+        options={schemaConnectionTypeArray}
+        onChange={(value: string | number) => {
+          dispatch(setSchemaSettings({ ...settings, connectionType: value as SchemaConnectionTypes }));
+        }}
+      />
+      <Input
+        type="dropdown"
+        label="Layout Type"
+        inline
+        size="sm"
+        value={settings.layoutName}
+        options={Object.values(Layouts)}
+        onChange={(value: string | number) => {
+          dispatch(setSchemaSettings({ ...settings, layoutName: value as any }));
+        }}
+      />
+    </div>
+  );
+};
diff --git a/libs/shared/lib/vis/components/panel.tsx b/libs/shared/lib/vis/components/VisualizationPanel.tsx
similarity index 66%
rename from libs/shared/lib/vis/components/panel.tsx
rename to libs/shared/lib/vis/components/VisualizationPanel.tsx
index 38a37bab4..61bcfb78e 100644
--- a/libs/shared/lib/vis/components/panel.tsx
+++ b/libs/shared/lib/vis/components/VisualizationPanel.tsx
@@ -1,12 +1,16 @@
 import React, { useMemo } from 'react';
 import { useGraphQueryResult, useQuerybuilderGraph } from '@graphpolaris/shared/lib/data-access';
 import VisualizationBar from './bar';
-import { VisualizationManagerType } from '../manager';
+import { VisualizationManager, VisualizationManagerType } from '../manager';
 import { Recommender, NoData, Querying } from '../views';
+import { resultSetFocus } from '../../data-access/store/interactionSlice';
+import { useDispatch } from 'react-redux';
 
-export const VisualizationPanel = ({ manager, fullSize }: { manager: VisualizationManagerType; fullSize: () => void }) => {
+export const VisualizationPanel = ({ fullSize }: { fullSize: () => void }) => {
+  const manager = VisualizationManager();
   const query = useQuerybuilderGraph();
   const graphQueryResult = useGraphQueryResult();
+  const dispatch = useDispatch();
 
   const renderContent = useMemo(() => {
     if (graphQueryResult.queryingBackend) {
@@ -20,7 +24,10 @@ export const VisualizationPanel = ({ manager, fullSize }: { manager: Visualizati
   }, [graphQueryResult, manager]);
 
   return (
-    <div className="vis-panel h-full w-full flex flex-col border bg-light">
+    <div
+      className="vis-panel h-full w-full flex flex-col border bg-light"
+      onMouseDownCapture={() => dispatch(resultSetFocus({ focusType: 'visualization' }))}
+    >
       <VisualizationBar manager={manager} fullSize={fullSize} />
       <div className="grow overflow-y-auto" style={graphQueryResult.nodes.length === 0 ? { overflow: 'hidden' } : {}}>
         {renderContent}
diff --git a/libs/shared/lib/vis/components/bar.tsx b/libs/shared/lib/vis/components/bar.tsx
index 269d25f57..76cca2479 100644
--- a/libs/shared/lib/vis/components/bar.tsx
+++ b/libs/shared/lib/vis/components/bar.tsx
@@ -6,6 +6,7 @@ import { Add, Close, Fullscreen } from '@mui/icons-material';
 import { ControlContainer } from '../../components/controls';
 import { Visualizations } from '../manager';
 import { VisualizationManagerType } from '../manager';
+import { Tabs, Tab } from '../../components/tabs';
 
 type Props = {
   manager: VisualizationManagerType;
@@ -64,20 +65,20 @@ export default function VisualizationBar({ manager, fullSize }: Props) {
           </DropdownMenu.Portal>
         </DropdownMenu.Root>
       </div>
-      <div className="flex items-stretch divide-x divide-secondary-200 border-x border-secondary-200 overflow-x-auto -my-px">
+      <Tabs>
         {manager.tabs.map((visId: string) => {
           const isActive = manager.activeVisualization === visId;
           return (
-            <div
+            <Tab
               key={visId}
-              className={`flex items-center pl-2 pr-1 gap-1 cursor-pointer relative border-secondary-200 before:content-[''] before:absolute before:left-0 before:bottom-0 before:h-[2px] before:w-full ${isActive && 'before:bg-primary-500'} ${!isActive && 'before:bg-transparent hover:before:bg-secondary-300 hover:bg-secondary-200'}`}
+              active={isActive}
+              text={visId}
               onClick={() => manager.changeActive(visId)}
               onDragStart={(e) => handleDragStart(e, visId)}
               onDragOver={(e) => handleDragOver(e)}
               onDrop={(e) => handleDrop(e, visId)}
               draggable
             >
-              <p className={`text-xs text-secondary-500 font-semibold ${isActive && 'text-secondary-950'}`}>{visId}</p>
               <Button
                 type="secondary"
                 variant="ghost"
@@ -89,10 +90,10 @@ export default function VisualizationBar({ manager, fullSize }: Props) {
                   manager.deleteVisualization(visId);
                 }}
               />
-            </div>
+            </Tab>
           );
         })}
-      </div>
+      </Tabs>
       <div className="shrink-0 sticky right-0 px-0.5 ml-auto items-center flex">
         <ControlContainer>
           <TooltipProvider delayDuration={0}>
diff --git a/libs/shared/lib/vis/components/config/ActiveVisualizationConfig.tsx b/libs/shared/lib/vis/components/config/ActiveVisualizationConfig.tsx
index fa36ceed1..88ed5de60 100644
--- a/libs/shared/lib/vis/components/config/ActiveVisualizationConfig.tsx
+++ b/libs/shared/lib/vis/components/config/ActiveVisualizationConfig.tsx
@@ -1,5 +1,5 @@
 import { Delete } from '@mui/icons-material';
-import { Button, Input } from '../../..';
+import { Button, Input, Panel } from '../../..';
 import { VisualizationManagerType, VISUALIZATION_TYPES } from '../../manager';
 import { SettingsHeader } from './components';
 
@@ -10,30 +10,28 @@ type Props = {
 export const ActiveVisualizationConfig = ({ manager }: Props) => {
   return (
     <>
-      <div className="border-b py-2">
-        <div className="flex justify-between items-center px-4 py-2">
-          <span className="text-xs font-bold">Visualization</span>
-          <Button
-            type="secondary"
-            variant="ghost"
-            size="xs"
-            iconComponent={<Delete />}
-            onClick={() => {
-              if (manager.activeVisualization) manager.deleteVisualization(manager.activeVisualization);
-            }}
-          />
-        </div>
-        <div className="flex justify-between items-center px-4 py-1">
-          <span className="text-xs font-normal">Type</span>
-          <div className="w-36">
-            <Input type="dropdown" size="xs" options={VISUALIZATION_TYPES} value={manager.activeVisualization} onChange={() => {}} />
-          </div>
-        </div>
-        <div className="flex justify-between items-center px-4 py-1">
-          <span className="text-xs font-normal">Name</span>
-          <input type="text" className="border rouded text-xs w-36" value={manager.activeVisualization} onChange={() => {}} />
+      <div className="flex justify-between items-center px-4 py-2">
+        <span className="text-xs font-bold">Visualization</span>
+        <Button
+          type="secondary"
+          variant="ghost"
+          size="xs"
+          iconComponent={<Delete />}
+          onClick={() => {
+            if (manager.activeVisualization) manager.deleteVisualization(manager.activeVisualization);
+          }}
+        />
+      </div>
+      <div className="flex justify-between items-center px-4 py-1">
+        <span className="text-xs font-normal">Type</span>
+        <div className="w-36">
+          <Input type="dropdown" size="xs" options={VISUALIZATION_TYPES} value={manager.activeVisualization} onChange={() => {}} />
         </div>
       </div>
+      <div className="flex justify-between items-center px-4 py-1">
+        <span className="text-xs font-normal">Name</span>
+        <input type="text" className="border rounded text-xs w-36" value={manager.activeVisualization} onChange={() => {}} />
+      </div>
       {manager.activeVisualization && (
         <div className="border-b p-4 w-full">
           <SettingsHeader name="Configuration" />
diff --git a/libs/shared/lib/vis/components/config/SelectionConfig.tsx b/libs/shared/lib/vis/components/config/SelectionConfig.tsx
index d790d651f..08dcadd77 100644
--- a/libs/shared/lib/vis/components/config/SelectionConfig.tsx
+++ b/libs/shared/lib/vis/components/config/SelectionConfig.tsx
@@ -1,13 +1,16 @@
 import { SelectionStateI, unSelect } from '@graphpolaris/shared/lib/data-access/store/interactionSlice';
 import { Delete } from '@mui/icons-material';
 import { useDispatch } from 'react-redux';
-import { Button, EntityPill } from '../../..';
+import { Button, EntityPill, useSelection } from '../../..';
 import { VISUALIZATION_TYPES } from '../../manager';
 import { SettingsHeader } from './components';
 
-export const SelectionConfig = (props: { selection: SelectionStateI }) => {
+export const SelectionConfig = () => {
+  const selection = useSelection();
   const dispatch = useDispatch();
 
+  if (!selection) return null;
+
   return (
     <div className="border-b py-2">
       <div className="flex justify-between items-center px-4 py-2">
@@ -22,7 +25,7 @@ export const SelectionConfig = (props: { selection: SelectionStateI }) => {
           }}
         />
       </div>
-      {props.selection.content.map((item, index) => (
+      {selection.content.map((item, index) => (
         <>
           <div key={index + 'id'} className="flex justify-between items-center px-4 py-1 gap-1">
             <span className="text-xs font-normal">ID</span>
diff --git a/libs/shared/lib/vis/components/config/index.tsx b/libs/shared/lib/vis/components/config/index.tsx
index 2f98579c7..4bf7db1e4 100644
--- a/libs/shared/lib/vis/components/config/index.tsx
+++ b/libs/shared/lib/vis/components/config/index.tsx
@@ -1,2 +1,2 @@
-export { ConfigPanel } from './panel';
+export { VisualizationConfigPanel as ConfigPanel } from './panel';
 export { SettingsContainer, SettingsHeader } from './components';
diff --git a/libs/shared/lib/vis/components/config/panel.tsx b/libs/shared/lib/vis/components/config/panel.tsx
index 5aae132cd..4f0bd9e03 100644
--- a/libs/shared/lib/vis/components/config/panel.tsx
+++ b/libs/shared/lib/vis/components/config/panel.tsx
@@ -1,61 +1,18 @@
 import React from 'react';
 import { Button } from '../../../components';
-import { VisualizationManagerType } from '../../manager';
+import { VisualizationManager, VisualizationManagerType } from '../../manager';
 import { useSelection, useSessionCache } from '../../../data-access';
 import { SelectionConfig } from './SelectionConfig';
 import { ActiveVisualizationConfig } from './ActiveVisualizationConfig';
 
-type Props = {
-  manager: VisualizationManagerType;
-};
+type Props = {};
 
-export function ConfigPanel({ manager }: Props) {
-  const session = useSessionCache();
-  const selection = useSelection();
-
-  const buildInfo = import.meta.env.GRAPHPOLARIS_VERSION;
+export function VisualizationConfigPanel({}: Props) {
+  const manager = VisualizationManager();
 
   return (
     <div className="flex flex-col w-full">
-      {!!selection && <SelectionConfig selection={selection} />}
-      {!selection && manager.activeVisualization && <ActiveVisualizationConfig manager={manager} />}
-      {!selection && !manager.activeVisualization && (
-        <div>
-          {session && session.currentSaveState && (
-            <div className="flex flex-col p-4 border-b">
-              <span className="text-sm font-bold">Connection details</span>
-              <span className="text-xs font-semibold">Name</span>
-              <span className="text-xs">{session.saveStates[session.currentSaveState].name}</span>
-              <span className="text-xs font-semibold">Database</span>
-              <span className="text-xs">{session.saveStates[session.currentSaveState].db.internalDatabaseName}</span>
-              <span className="text-xs font-semibold">Protocol</span>
-              <span className="text-xs">{session.saveStates[session.currentSaveState].db.protocol}</span>
-              <span className="text-xs font-semibold">Hostname</span>
-              <span className="text-xs">{session.saveStates[session.currentSaveState].db.url}</span>
-              <span className="text-xs font-semibold">Port</span>
-              <span className="text-xs">{session.saveStates[session.currentSaveState].db.port}</span>
-            </div>
-          )}
-        </div>
-      )}
-
-      {buildInfo === 'dev' && (
-        <div className="mt-auto p-2 bg-light">
-          <Button
-            type="primary"
-            variant="outline"
-            size="xs"
-            label="Report an issue"
-            onClick={() =>
-              window.open(
-                'https://app.asana.com/-/login?u=https%3A%2F%2Fform.asana.com%2F%3Fk%3D2QEC88Dl7ETs2wYYWjkMXg%26d%3D1206648675960041&error=01',
-                '_blank',
-              )
-            }
-            className="block w-full"
-          />
-        </div>
-      )}
+      <ActiveVisualizationConfig manager={manager}></ActiveVisualizationConfig>
     </div>
   );
 }
diff --git a/libs/shared/lib/vis/index.ts b/libs/shared/lib/vis/index.ts
index ac473812c..b08eef1b8 100644
--- a/libs/shared/lib/vis/index.ts
+++ b/libs/shared/lib/vis/index.ts
@@ -1 +1 @@
-export * from './components/panel';
+export * from './components/VisualizationPanel';
diff --git a/libs/shared/lib/vis/visualizations/matrixvis/matrix.stories.tsx b/libs/shared/lib/vis/visualizations/matrixvis/matrix.stories.tsx
index 5fdecb241..1a961e293 100644
--- a/libs/shared/lib/vis/visualizations/matrixvis/matrix.stories.tsx
+++ b/libs/shared/lib/vis/visualizations/matrixvis/matrix.stories.tsx
@@ -3,7 +3,7 @@ import { Meta } from '@storybook/react';
 import { configureStore } from '@reduxjs/toolkit';
 import { Provider } from 'react-redux';
 import { big2ndChamberQueryResult, smallFlightsQueryResults, mockLargeQueryResults } from '../../../mock-data';
-import { VisualizationPanel } from '../../components/panel';
+import { VisualizationPanel } from '../../components/VisualizationPanel';
 
 import {
   setNewGraphQueryResult,
-- 
GitLab