in frontend/src/containers/AddChart.tsx [54:543]
function AddChart() {
const history = useHistory<LocationState>();
const { state } = history.location;
const { t } = useTranslation();
const { dashboardId } = useParams<PathParams>();
const { dashboard, loading } = useDashboard(dashboardId);
const { dynamicDatasets } = useDatasets();
const { register, errors, handleSubmit, reset, watch } =
useForm<FormValues>();
const [currentJson, setCurrentJson] = useState<Array<any>>(
state && state.json ? state.json : []
);
const [dynamicJson, setDynamicJson] = useState<Array<any>>([]);
const [staticJson] = useState<Array<any>>(
state && state.json ? state.json : []
);
const [csvJson, setCsvJson] = useState<Array<any>>([]);
const [filteredJson, setFilteredJson] = useState<Array<any>>(currentJson);
const [dynamicDataset, setDynamicDataset] = useState<Dataset | undefined>(
undefined
);
const [staticDataset] = useState<Dataset | undefined>(
state && state.staticDataset ? state.staticDataset : undefined
);
const [csvErrors, setCsvErrors] = useState<Array<object> | undefined>(
undefined
);
const [csvFile, setCsvFile] = useState<File | undefined>(undefined);
const [fileLoading, setFileLoading] = useState(false);
const [datasetLoading, setDatasetLoading] = useState(false);
const [creatingWidget, setCreatingWidget] = useState(false);
const [showColumnHeaderAlert, setShowColumnHeaderAlert] = useState(false);
const [showNoDatasetTypeAlert, setShowNoDatasetTypeAlert] = useState(false);
const [enableContinueButton, setEnableContinueButton] = useState(true);
const [datasetType, setDatasetType] = useState<DatasetType | undefined>(
state && state.json ? DatasetType.StaticDataset : undefined
);
const [step, setStep] = useState<number>(state && state.json ? 1 : 0);
const [selectedHeaders, setSelectedHeaders] = useState<Set<string>>(
new Set<string>()
);
const [hiddenColumns, setHiddenColumns] = useState<Set<string>>(
new Set<string>()
);
const [sortByColumn, setSortByColumn] = useState<string | undefined>(
undefined
);
const [sortByDesc, setSortByDesc] = useState<boolean | undefined>(undefined);
const { fullPreviewButton, fullPreview } = useFullPreview();
const [dataTypes, setDataTypes] = useState<Map<string, ColumnDataType>>(
new Map<string, ColumnDataType>()
);
const [numberTypes, setNumberTypes] = useState<Map<string, NumberDataType>>(
new Map<string, NumberDataType>()
);
const [currencyTypes, setCurrencyTypes] = useState<
Map<string, CurrencyDataType>
>(new Map<string, CurrencyDataType>());
const [oldStep, setOldStep] = useState<number>(-1);
const title = watch("title");
const summary = watch("summary");
const summaryBelow = watch("summaryBelow");
const chartType = watch("chartType");
const showTitle = watch("showTitle");
const horizontalScroll = watch("horizontalScroll");
const stackedChart = watch("stackedChart");
const dataLabels = watch("dataLabels");
const computePercentages = watch("computePercentages");
const showTotal = watch("showTotal");
const significantDigitLabels = watch("significantDigitLabels");
const initializeColumnsMetadata = () => {
setSelectedHeaders(new Set<string>());
setHiddenColumns(new Set<string>());
setDataTypes(new Map<string, ColumnDataType>());
setNumberTypes(new Map<string, NumberDataType>());
setCurrencyTypes(new Map<string, CurrencyDataType>());
setSortByColumn(undefined);
setSortByDesc(false);
};
useMemo(() => {
const newFilteredJson = DatasetParsingService.getFilteredJson(
currentJson,
hiddenColumns
);
DatasetParsingService.sortFilteredJson(
newFilteredJson,
sortByColumn,
sortByDesc
);
setFilteredJson(newFilteredJson);
}, [currentJson, hiddenColumns, sortByColumn, sortByDesc]);
const uploadDataset = async (): Promise<Dataset> => {
if (!csvFile) {
throw new Error(t("CSVFileNotSpecified"));
}
setFileLoading(true);
const uploadResponse = await StorageService.uploadDataset(
csvFile,
JSON.stringify(currentJson),
t
);
const newDataset = await BackendService.createDataset(csvFile.name, {
raw: uploadResponse.s3Keys.raw,
json: uploadResponse.s3Keys.json,
});
setFileLoading(false);
return newDataset;
};
const onSubmit = async (values: FormValues) => {
try {
let newDataset;
if (csvFile) {
newDataset = await uploadDataset();
}
setCreatingWidget(true);
await BackendService.createWidget(
dashboardId,
values.title,
WidgetType.Chart,
values.showTitle,
{
title: values.title,
summary: values.summary,
summaryBelow: values.summaryBelow,
chartType: values.chartType,
...((values.chartType === ChartType.LineChart ||
values.chartType === ChartType.ColumnChart) && {
horizontalScroll: values.horizontalScroll,
}),
...((values.chartType === ChartType.BarChart ||
values.chartType === ChartType.ColumnChart) && {
stackedChart: values.stackedChart,
}),
...((values.chartType === ChartType.BarChart ||
values.chartType === ChartType.ColumnChart ||
values.chartType === ChartType.PieChart ||
values.chartType === ChartType.DonutChart) && {
dataLabels: values.dataLabels,
}),
...((values.chartType === ChartType.PieChart ||
values.chartType === ChartType.DonutChart) && {
computePercentages: values.computePercentages,
}),
...(values.chartType === ChartType.DonutChart && {
showTotal: values.showTotal,
}),
datasetType: datasetType,
datasetId: newDataset
? newDataset.id
: datasetType === DatasetType.DynamicDataset
? dynamicDataset?.id
: staticDataset?.id,
s3Key: newDataset
? newDataset.s3Key
: datasetType === DatasetType.DynamicDataset
? dynamicDataset?.s3Key
: staticDataset?.s3Key,
fileName: csvFile
? csvFile.name
: datasetType === DatasetType.DynamicDataset
? dynamicDataset?.fileName
: staticDataset?.fileName,
sortByColumn,
sortByDesc,
significantDigitLabels: values.significantDigitLabels,
columnsMetadata: ColumnsMetadataService.getColumnsMetadata(
hiddenColumns,
dataTypes,
numberTypes,
currencyTypes
),
}
);
setCreatingWidget(false);
history.push(`/admin/dashboard/edit/${dashboardId}`, {
alert: {
type: "success",
message: t("AddChartScreen.AddChartSuccess", { title: values.title }),
},
});
} catch (err) {
console.log(t("AddContentFailure"), err);
setCreatingWidget(false);
}
};
const onCancel = () => {
history.push(`/admin/dashboard/edit/${dashboardId}`);
};
const advanceStep = () => {
setStep(step + 1);
};
const backStep = () => {
setStep(step - 1);
};
const goBack = () => {
history.push(`/admin/dashboard/${dashboardId}/add-content`);
};
const browseDatasets = () => {
history.push({
pathname: `/admin/dashboard/${dashboardId}/choose-static-dataset`,
state: {
redirectUrl: `/admin/dashboard/${dashboardId}/add-chart/`,
crumbLabel: t("AddChartScreen.AddChart"),
},
});
};
const onFileProcessed = useCallback(
async (data: File) => {
if (!data) {
return;
}
setDatasetLoading(true);
ParsingFileService.parseFile(data, true, (errors: any, results: any) => {
initializeColumnsMetadata();
let wrongCSV = false;
const firstRow = results[0];
for (let columnName in firstRow) {
if (columnName === "") {
wrongCSV = true;
break;
}
}
if (wrongCSV) {
setEnableContinueButton(false);
setShowColumnHeaderAlert(true);
setCsvFile(undefined);
return;
} else {
setEnableContinueButton(true);
setShowColumnHeaderAlert(false);
}
if (errors !== null && errors.length) {
setCsvErrors(errors);
setCsvJson([]);
setCurrentJson([]);
} else {
setShowNoDatasetTypeAlert(false);
setCsvErrors(undefined);
const csvJson = ParsingFileService.isExcelFile(data.type)
? DatasetParsingService.createHeaderRowJson(results)
: results;
setCsvJson(csvJson);
setCurrentJson(csvJson);
}
setDatasetLoading(false);
});
setCsvFile(data);
},
[setCurrentJson, setCsvJson]
);
const handleChange = async (event: React.FormEvent<HTMLFieldSetElement>) => {
const target = event.target as HTMLInputElement;
if (target.name === "datasetType") {
setDatasetLoading(true);
const datasetType = target.value as DatasetType;
setDatasetType(datasetType);
initializeColumnsMetadata();
await UtilsService.timeout(0);
if (datasetType === DatasetType.DynamicDataset) {
setCurrentJson(dynamicJson);
}
if (datasetType === DatasetType.StaticDataset) {
if (csvJson && csvJson.length) {
setCurrentJson(csvJson);
} else {
setCurrentJson(staticJson);
}
}
setDatasetLoading(false);
}
};
const selectDynamicDataset = async (selectedDataset: Dataset) => {
setDatasetLoading(true);
if (
selectedDataset &&
selectedDataset.s3Key &&
selectedDataset.s3Key.json
) {
const jsonFile = selectedDataset.s3Key.json;
initializeColumnsMetadata();
const dataset = await StorageService.downloadJson(jsonFile);
setDynamicJson(dataset);
setCurrentJson(dataset);
setDynamicDataset(dynamicDatasets.find((d) => d.s3Key.json === jsonFile));
}
setDatasetLoading(false);
};
useEffect(() => {
if (datasetType) {
reset({
datasetType,
});
}
}, []);
const crumbs = [
{
label: t("Dashboards"),
url: "/admin/dashboards",
},
{
label: dashboard?.name,
url: `/admin/dashboard/edit/${dashboardId}`,
},
];
if (!loading) {
crumbs.push({
label: t("AddChartScreen.AddChart"),
url: "",
});
}
const configHeader = (
<div>
<h1 className="margin-top-0">{t("AddChartScreen.AddChart")}</h1>
<StepIndicator
current={step}
segments={[
{
label: t("AddChartScreen.ChooseData"),
},
{
label: t("AddChartScreen.CheckData"),
},
{
label: t("AddChartScreen.Visualize"),
},
]}
showStepChart={true}
showStepText={false}
/>
</div>
);
useChangeBackgroundColor();
useScrollUp(oldStep, step, setOldStep);
return (
<>
<Breadcrumbs crumbs={crumbs} />
<div className="grid-row">
<div className="grid-col-12">
<form onSubmit={handleSubmit(onSubmit)}>
<div hidden={step !== 0}>
<PrimaryActionBar>
{configHeader}
<div className="margin-y-3" hidden={!showColumnHeaderAlert}>
<Alert
type="error"
message={t("AddChartScreen.ResolveError")}
slim
/>
</div>
<div className="margin-y-3" hidden={!showNoDatasetTypeAlert}>
<Alert
type="error"
message={t("AddChartScreen.ChooseDataset")}
slim
/>
</div>
<ChooseData
selectDynamicDataset={selectDynamicDataset}
dynamicDatasets={dynamicDatasets}
datasetType={datasetType}
onFileProcessed={onFileProcessed}
handleChange={handleChange}
backStep={goBack}
advanceStep={advanceStep}
fileLoading={fileLoading}
browseDatasets={browseDatasets}
hasErrors={!enableContinueButton || !currentJson.length}
csvErrors={csvErrors}
csvFile={csvFile}
onCancel={onCancel}
register={register}
widgetType={t("ChooseDataDescriptionChart")}
staticFileName={undefined}
dynamicFileName={undefined}
setShowNoDatasetTypeAlert={setShowNoDatasetTypeAlert}
/>
</PrimaryActionBar>
</div>
<div hidden={step !== 1}>
<PrimaryActionBar>
{configHeader}
<CheckData
data={currentJson}
advanceStep={advanceStep}
backStep={backStep}
selectedHeaders={selectedHeaders}
setSelectedHeaders={setSelectedHeaders}
hiddenColumns={hiddenColumns}
setHiddenColumns={setHiddenColumns}
onCancel={onCancel}
dataTypes={dataTypes}
setDataTypes={setDataTypes}
numberTypes={numberTypes}
setNumberTypes={setNumberTypes}
currencyTypes={currencyTypes}
setCurrencyTypes={setCurrencyTypes}
sortByColumn={sortByColumn}
sortByDesc={sortByDesc}
setSortByColumn={setSortByColumn}
setSortByDesc={setSortByDesc}
reset={reset}
widgetType={t("CheckDataDescriptionChart")}
/>
</PrimaryActionBar>
</div>
<div hidden={step !== 2}>
<VisualizeChart
errors={errors}
register={register}
json={filteredJson}
headers={
currentJson.length
? (Object.keys(currentJson[0]) as Array<string>)
: []
}
originalJson={currentJson}
csvJson={csvJson}
datasetLoading={datasetLoading}
datasetType={datasetType}
onCancel={onCancel}
backStep={backStep}
advanceStep={advanceStep}
fileLoading={fileLoading}
processingWidget={creatingWidget}
fullPreviewButton={fullPreviewButton}
fullPreview={fullPreview}
submitButtonLabel={t("AddChartScreen.AddChart")}
sortByColumn={sortByColumn}
sortByDesc={sortByDesc}
setSortByColumn={setSortByColumn}
setSortByDesc={setSortByDesc}
title={title}
summary={summary}
summaryBelow={summaryBelow}
showTitle={showTitle}
chartType={chartType as ChartType}
significantDigitLabels={significantDigitLabels}
horizontalScroll={horizontalScroll}
stackedChart={stackedChart}
dataLabels={dataLabels}
computePercentages={computePercentages}
showTotal={showTotal}
columnsMetadata={ColumnsMetadataService.getColumnsMetadata(
hiddenColumns,
dataTypes,
numberTypes,
currencyTypes
)}
configHeader={configHeader}
/>
</div>
</form>
</div>
</div>
</>
);
}