diff --git a/viz-lib/package-lock.json b/viz-lib/package-lock.json
index bfdf453a93..8df839bf47 100644
--- a/viz-lib/package-lock.json
+++ b/viz-lib/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "@redash/viz",
- "version": "0.1.1",
+ "version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -8037,6 +8037,16 @@
"minimalistic-assert": "^1.0.1"
}
},
+ "highcharts": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/highcharts/-/highcharts-10.0.0.tgz",
+ "integrity": "sha512-a14PrTraVaoSNyaRPhLF/jJ3/09rJwfUZedLgZOkzozGz4K/H8WtWNJtUZt/tc5QiJkaZHw4YX7vvuO98s+EoA=="
+ },
+ "highcharts-react-official": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/highcharts-react-official/-/highcharts-react-official-3.1.0.tgz",
+ "integrity": "sha512-CkWJHrVMOc6CT8KFu1dR+a0w5OxCVKKgZUNWtEi5TmR0xqBDIDe+RyM652MAN/jBYppxMo6TCUVlRObCyWAn0Q=="
+ },
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
diff --git a/viz-lib/package.json b/viz-lib/package.json
index 4031dd455b..0416a22d86 100644
--- a/viz-lib/package.json
+++ b/viz-lib/package.json
@@ -86,6 +86,8 @@
"debug": "^3.1.0",
"dompurify": "^2.0.7",
"font-awesome": "^4.7.0",
+ "highcharts": "^10.0.0",
+ "highcharts-react-official": "^3.1.0",
"hoist-non-react-statics": "^3.3.0",
"leaflet": "^1.2.0",
"leaflet-fullscreen": "^1.0.2",
diff --git a/viz-lib/src/visualizations/gantt-chart/Editor.tsx b/viz-lib/src/visualizations/gantt-chart/Editor.tsx
new file mode 100644
index 0000000000..ecc596ca31
--- /dev/null
+++ b/viz-lib/src/visualizations/gantt-chart/Editor.tsx
@@ -0,0 +1,33 @@
+import { map, merge } from "lodash";
+import React from "react";
+import * as Grid from "antd/lib/grid";
+import { Section, Select, InputNumber, ControlLabel } from "@/components/visualizations/editor";
+import { EditorPropTypes } from "@/visualizations/prop-types";
+
+export default function Editor({ options, data, onOptionsChange }: any) {
+ const optionsChanged = (newOptions: any) => {
+ onOptionsChange(merge({}, options, newOptions));
+ };
+
+ return (
+
+ {/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
+
+
+
+
+ );
+}
+
+Editor.propTypes = EditorPropTypes;
diff --git a/viz-lib/src/visualizations/gantt-chart/Renderer.tsx b/viz-lib/src/visualizations/gantt-chart/Renderer.tsx
new file mode 100644
index 0000000000..05132dc3cd
--- /dev/null
+++ b/viz-lib/src/visualizations/gantt-chart/Renderer.tsx
@@ -0,0 +1,103 @@
+import React, { useMemo, useState, useEffect } from "react";
+import { RendererPropTypes } from "@/visualizations/prop-types";
+
+import { groupBy } from "lodash";
+
+import HighchartsReact from "highcharts-react-official";
+// Import Highcharts
+import Highcharts from "highcharts";
+import highchartsGantt from "highcharts/modules/gantt";
+
+highchartsGantt(Highcharts);
+
+import "./renderer.less";
+
+function prepareData(data: any) {
+ const colors: any = {
+ "Early Engagement": "#e1ebf3",
+ "Proponent Time: Project Description": "#ccffff",
+ "Readiness Decision": "#c3d7e8",
+ "Process Planning": "#a6c3dd",
+ "Proponent Time: Application Development": "#ccffff",
+ "Application Development & Review": "#faeadc",
+ "Proponent Time: Revised Application": "#ccffff",
+ "Effects Assessment": "#f6d5b9",
+ Recommendation: "#f2c096",
+ "Referral/Decision": "#f2c096",
+ };
+ const projects = groupBy(data, (item: any) => item.project);
+ const series: Array = [];
+ let projectIndex = 0;
+
+ for (const project in projects) {
+ const projectData: any = projects[project];
+ const projectId: string = `${projectData[0].project_id}`;
+ const projectSeries: any = {
+ name: projectData[0].project,
+ data: [],
+ };
+
+ projectData.forEach((phase: any) => {
+ projectSeries.data.push({
+ id: phase.phase_id,
+ name: phase.phase,
+ start: new Date(phase.phase_start).getTime(),
+ end: new Date(phase.phase_end).getTime(),
+ y: projectIndex,
+ color: colors[phase.phase],
+ });
+ });
+ series.push(projectSeries);
+ projectIndex += 1;
+ }
+
+ return [series, Object.keys(projects)];
+}
+
+export default function Renderer({ data, options }: any) {
+ const [tasks, projects] = prepareData(data.rows);
+ const first = tasks[0];
+ const last = tasks[tasks.length - 1];
+ const day = 1000 * 60 * 60 * 24;
+ const chartData: Highcharts.Options = {
+ title: {
+ text: "Highcharts Gantt With Subtasks",
+ },
+ xAxis: [
+ {
+ tickInterval: 1000 * 60 * 60 * 24 * 30, // Month
+ labels: {
+ format: "{value:%b}",
+ style: {
+ fontSize: "8px",
+ },
+ autoRotation: [-90],
+ },
+ min: first.data[0].start - day * 15,
+ max: last.data[last.data.length - 1].end + day * 15,
+ currentDateIndicator: false,
+ },
+ {
+ tickInterval: 1000 * 60 * 60 * 24 * 365, // Year
+ labels: {
+ format: "{value:%Y}",
+ },
+ linkedTo: 0,
+ },
+ ],
+ yAxis: {
+ categories: projects,
+ },
+ series: tasks,
+ };
+
+ console.log(chartData);
+
+ return (
+
+
+
+ );
+}
+
+Renderer.propTypes = RendererPropTypes;
diff --git a/viz-lib/src/visualizations/gantt-chart/index.ts b/viz-lib/src/visualizations/gantt-chart/index.ts
new file mode 100644
index 0000000000..d2ed788294
--- /dev/null
+++ b/viz-lib/src/visualizations/gantt-chart/index.ts
@@ -0,0 +1,18 @@
+import { merge } from "lodash";
+
+import Renderer from "./Renderer";
+import Editor from "./Editor";
+
+const DEFAULT_OPTIONS = {
+ projectName: "",
+};
+
+export default {
+ type: "GANTT_CHART",
+ name: "Gantt Chart",
+ getOptions: (options: any) => merge({}, DEFAULT_OPTIONS, options),
+ Renderer,
+ Editor,
+
+ defaultRows: 8,
+};
diff --git a/viz-lib/src/visualizations/gantt-chart/renderer.less b/viz-lib/src/visualizations/gantt-chart/renderer.less
new file mode 100644
index 0000000000..aee6afcced
--- /dev/null
+++ b/viz-lib/src/visualizations/gantt-chart/renderer.less
@@ -0,0 +1,12 @@
+.word-cloud-visualization-container {
+ overflow: hidden;
+ height: 400px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ svg {
+ transform-origin: center center;
+ flex: 0 0 auto;
+ }
+}
diff --git a/viz-lib/src/visualizations/registeredVisualizations.ts b/viz-lib/src/visualizations/registeredVisualizations.ts
index 2e6fb7df7d..7e9e4f258d 100644
--- a/viz-lib/src/visualizations/registeredVisualizations.ts
+++ b/viz-lib/src/visualizations/registeredVisualizations.ts
@@ -14,6 +14,7 @@ import sankeyVisualization from "./sankey";
import sunburstVisualization from "./sunburst";
import tableVisualization from "./table";
import wordCloudVisualization from "./word-cloud";
+import ganttChartVisualization from "./gantt-chart";
type VisualizationConfig = {
type: string;
@@ -91,6 +92,7 @@ each(
sunburstVisualization,
tableVisualization,
wordCloudVisualization,
+ ganttChartVisualization,
]),
registerVisualization
);