import { isEqual, extend, map, sortBy, findIndex, filter, pick } from "lodash"; import React, { useState, useMemo, useRef, useEffect } from "react"; import PropTypes from "prop-types"; import Modal from "antd/lib/modal"; import Select from "antd/lib/select"; import Input from "antd/lib/input"; import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper"; import ErrorBoundary, { ErrorMessage } from "@/components/ErrorBoundary"; import Filters, { filterData } from "@/components/Filters"; import notification from "@/services/notification"; import Visualization from "@/services/visualization"; import recordEvent from "@/services/recordEvent"; import getQueryResultData from "@/lib/getQueryResultData"; import { VisualizationType } from "@/visualizations/prop-types"; import registeredVisualizations, { getDefaultVisualization, newVisualization } from "@/visualizations"; import "./EditVisualizationDialog.less"; function updateQueryVisualizations(query, visualization) { const index = findIndex(query.visualizations, v => v.id === visualization.id); if (index > -1) { query.visualizations[index] = visualization; } else { // new visualization query.visualizations.push(visualization); } query.visualizations = [...query.visualizations]; // clone array } function saveVisualization(visualization) { if (visualization.id) { recordEvent("update", "visualization", visualization.id, { type: visualization.type }); } else { recordEvent("create", "visualization", null, { type: visualization.type }); } return Visualization.save(visualization) .then(result => { notification.success("Visualization saved"); return result; }) .catch(error => { notification.error("Visualization could not be saved"); return Promise.reject(error); }); } function confirmDialogClose(isDirty) { return new Promise((resolve, reject) => { if (isDirty) { Modal.confirm({ title: "Visualization Editor", content: "Are you sure you want to close the editor without saving?", okText: "Yes", cancelText: "No", onOk: () => resolve(), onCancel: () => reject(), }); } else { resolve(); } }); } function EditVisualizationDialog({ dialog, visualization, query, queryResult }) { const errorHandlerRef = useRef(); const isNew = !visualization; const data = getQueryResultData(queryResult); const [filters, setFilters] = useState(data.filters); const filteredData = useMemo( () => ({ columns: data.columns, rows: filterData(data.rows, filters), }), [data, filters] ); const defaultState = useMemo(() => { const config = visualization ? registeredVisualizations[visualization.type] : getDefaultVisualization(); const options = config.getOptions(isNew ? {} : visualization.options, data); return { type: config.type, name: isNew ? config.name : visualization.name, options, originalOptions: options, }; }, [data, isNew, visualization]); const [type, setType] = useState(defaultState.type); const [name, setName] = useState(defaultState.name); const [nameChanged, setNameChanged] = useState(false); const [options, setOptions] = useState(defaultState.options); const [saveInProgress, setSaveInProgress] = useState(false); useEffect(() => { if (errorHandlerRef.current) { errorHandlerRef.current.reset(); } }, [data, options]); function onTypeChanged(newType) { setType(newType); const config = registeredVisualizations[newType]; if (!nameChanged) { setName(config.name); } setOptions(config.getOptions(isNew ? {} : visualization.options, data)); } function onNameChanged(newName) { setName(newName); setNameChanged(newName !== name); } function onOptionsChanged(newOptions) { const config = registeredVisualizations[type]; setOptions(config.getOptions(newOptions, data)); } function save() { setSaveInProgress(true); const visualizationData = extend(newVisualization(type), visualization, { name, options, query_id: query.id }); saveVisualization(visualizationData).then(savedVisualization => { updateQueryVisualizations(query, savedVisualization); dialog.close(savedVisualization); }); } function dismiss() { const optionsChanged = !isEqual(options, defaultState.originalOptions); confirmDialogClose(nameChanged || optionsChanged).then(dialog.dismiss); } const { Renderer, Editor } = registeredVisualizations[type]; // When editing existing visualization chart type selector is disabled, so add only existing visualization's // descriptor there (to properly render the component). For new visualizations show all types except of deprecated const availableVisualizations = isNew ? filter(sortBy(registeredVisualizations, ["name"]), vis => !vis.isDeprecated) : pick(registeredVisualizations, [type]); return (
onNameChanged(event.target.value)} />
Error while rendering visualization.}>
); } EditVisualizationDialog.propTypes = { dialog: DialogPropType.isRequired, query: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types visualization: VisualizationType, queryResult: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types }; EditVisualizationDialog.defaultProps = { visualization: null, }; export default wrapDialog(EditVisualizationDialog);