From cc73821dd845fa831a3b613e4b7d0a04b3f5e8b4 Mon Sep 17 00:00:00 2001
From: Marcos Pieras <pieras.marcos@gmail.com>
Date: Mon, 30 Sep 2024 09:41:20 +0000
Subject: [PATCH] feat: new vis 0Dviz

---
 .../lib/vis/components/VisualizationPanel.tsx |  1 +
 .../lib/vis/visualizations/Vis0D/Vis0D.tsx    | 97 +++++++++++++++++++
 .../lib/vis/visualizations/Vis0D/index.ts     |  1 +
 .../visualizations/Vis0D/vis0D.stories.tsx    | 61 ++++++++++++
 libs/shared/lib/vis/visualizations/index.tsx  |  1 +
 5 files changed, 161 insertions(+)
 create mode 100644 libs/shared/lib/vis/visualizations/Vis0D/Vis0D.tsx
 create mode 100644 libs/shared/lib/vis/visualizations/Vis0D/index.ts
 create mode 100644 libs/shared/lib/vis/visualizations/Vis0D/vis0D.stories.tsx

diff --git a/libs/shared/lib/vis/components/VisualizationPanel.tsx b/libs/shared/lib/vis/components/VisualizationPanel.tsx
index 8f178196b..8d5e4e2eb 100644
--- a/libs/shared/lib/vis/components/VisualizationPanel.tsx
+++ b/libs/shared/lib/vis/components/VisualizationPanel.tsx
@@ -30,6 +30,7 @@ export const Visualizations: Record<string, PromiseFunc> = {
     SemanticSubstratesVis: () => import('../visualizations/semanticsubstratesvis/semanticsubstratesvis'),
   }),
   ...(isVisualizationReleased('MapVis') && { MapVis: () => import('../visualizations/mapvis/mapvis') }),
+  ...(isVisualizationReleased('Vis0D') && { Vis0D: () => import('../visualizations/Vis0D/Vis0D') }),
 };
 
 export const VISUALIZATION_TYPES: string[] = Object.keys(Visualizations);
diff --git a/libs/shared/lib/vis/visualizations/Vis0D/Vis0D.tsx b/libs/shared/lib/vis/visualizations/Vis0D/Vis0D.tsx
new file mode 100644
index 000000000..cc079a384
--- /dev/null
+++ b/libs/shared/lib/vis/visualizations/Vis0D/Vis0D.tsx
@@ -0,0 +1,97 @@
+import React, { useRef, useImperativeHandle, forwardRef } from 'react';
+import { VisualizationPropTypes, VISComponentType, VisualizationSettingsPropTypes } from '../../common';
+import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/config';
+import html2canvas from 'html2canvas';
+import { Input } from '@graphpolaris/shared/lib/components/inputs';
+
+export interface Vis0DProps {
+  title: string;
+}
+
+const settings: Vis0DProps = {
+  title: '',
+};
+
+export interface Vis0DVisHandle {
+  exportImageInternal: () => void;
+}
+
+const formatNumber = (number: number) => {
+  return number.toLocaleString('de-DE');
+};
+const Vis0D = forwardRef<Vis0DVisHandle, VisualizationPropTypes<Vis0DProps>>(({ data, settings }, refExternal) => {
+  const internalRef = useRef<HTMLDivElement>(null);
+  useImperativeHandle(refExternal, () => ({
+    exportImageInternal() {
+      const captureImage = () => {
+        const element = internalRef.current;
+        if (element) {
+          html2canvas(element, {
+            backgroundColor: '#FFFFFF',
+          })
+            .then((canvas) => {
+              const finalImage = canvas.toDataURL('image/png');
+              const link = document.createElement('a');
+              link.href = finalImage;
+              link.download = 'Vis0D.png';
+              document.body.appendChild(link);
+              link.click();
+              document.body.removeChild(link);
+            })
+            .catch((error) => {
+              console.error('Error capturing image:', error);
+            });
+        } else {
+          console.error('Container element not found');
+        }
+      };
+
+      const renderCanvas = () => {
+        requestAnimationFrame(() => {
+          captureImage();
+        });
+      };
+
+      renderCanvas();
+    },
+  }));
+
+  // !FIXME: When stats pills are ready, substitue results accordingly
+  return (
+    <div className="h-full w-full flex flex-col items-center justify-center overflow-hidden" ref={internalRef}>
+      {settings.title && <span className="text-3xl text-center mb-4">{settings.title}</span>}
+      {data?.nodes?.length > 0 ? (
+        <span className="text-4xl text-center">Select 0D data</span>
+      ) : (
+        <span className="text-8xl text-center">{formatNumber(1231312)}</span>
+      )}{' '}
+    </div>
+  );
+});
+
+const Vis0DSettings = ({ settings, updateSettings }: VisualizationSettingsPropTypes<Vis0DProps>) => {
+  return (
+    <SettingsContainer>
+      <Input type="text" label="Title" value={settings.title} onChange={(value) => updateSettings({ title: value as string })} />
+    </SettingsContainer>
+  );
+};
+
+const Vis0DRef = React.createRef<Vis0DVisHandle>();
+
+export const Vis0DComponent: VISComponentType<Vis0DProps> = {
+  displayName: '0Dvis',
+  description: 'KPI visualization',
+  component: React.forwardRef((props: VisualizationPropTypes<Vis0DProps>, ref) => <Vis0D {...props} ref={Vis0DRef} />),
+  settingsComponent: Vis0DSettings,
+  settings: settings,
+  exportImage: () => {
+    if (Vis0DRef.current) {
+      Vis0DRef.current.exportImageInternal();
+    } else {
+      console.error('0Dvis reference is not set.');
+    }
+  },
+};
+
+export default Vis0DComponent;
diff --git a/libs/shared/lib/vis/visualizations/Vis0D/index.ts b/libs/shared/lib/vis/visualizations/Vis0D/index.ts
new file mode 100644
index 000000000..8b8204e59
--- /dev/null
+++ b/libs/shared/lib/vis/visualizations/Vis0D/index.ts
@@ -0,0 +1 @@
+export * from './Vis0D';
diff --git a/libs/shared/lib/vis/visualizations/Vis0D/vis0D.stories.tsx b/libs/shared/lib/vis/visualizations/Vis0D/vis0D.stories.tsx
new file mode 100644
index 000000000..92fc6c951
--- /dev/null
+++ b/libs/shared/lib/vis/visualizations/Vis0D/vis0D.stories.tsx
@@ -0,0 +1,61 @@
+import { Meta } from '@storybook/react';
+import { configureStore } from '@reduxjs/toolkit';
+import { Provider } from 'react-redux';
+import { mockData } from '../../../mock-data';
+import { graphQueryResultSlice, querybuilderSlice, schemaSlice, visualizationSlice } from '../../../data-access/store';
+import Vis0DComponent from './Vis0D';
+
+const Component: Meta<typeof Vis0DComponent.component> = {
+  title: 'Visualizations/0DVis',
+  component: Vis0DComponent.component,
+  decorators: [
+    (story) => (
+      <Provider store={Mockstore}>
+        <div
+          style={{
+            width: '100%',
+            height: '100vh',
+          }}
+        >
+          {story()}
+        </div>
+      </Provider>
+    ),
+  ],
+};
+
+const Mockstore: any = configureStore({
+  reducer: {
+    schema: schemaSlice.reducer,
+    graphQueryResult: graphQueryResultSlice.reducer,
+    visualize: visualizationSlice.reducer,
+    querybuilder: querybuilderSlice.reducer,
+  },
+});
+
+export const TestWithData = {
+  args: {
+    data: {
+      nodes: 12313,
+      edges: [],
+    },
+    ml: {},
+    settings: {
+      ...Vis0DComponent.settings,
+      title: 'KPI Count Movie',
+    },
+  },
+};
+
+export const TestWithNoData = {
+  args: {
+    data: {
+      nodes: [123123, 1312, 21313],
+      edges: [],
+    },
+    ml: {},
+    settings: Vis0DComponent.settings,
+  },
+};
+
+export default Component;
diff --git a/libs/shared/lib/vis/visualizations/index.tsx b/libs/shared/lib/vis/visualizations/index.tsx
index 5fa9f4221..1a830c42e 100644
--- a/libs/shared/lib/vis/visualizations/index.tsx
+++ b/libs/shared/lib/vis/visualizations/index.tsx
@@ -4,3 +4,4 @@ export * from './paohvis/paohvis';
 export * from './tablevis/tablevis';
 export * from './matrixvis/matrixvis';
 export * from './semanticsubstratesvis/semanticsubstratesvis';
+export * from './Vis0D/Vis0D';
-- 
GitLab