diff --git a/web/src/components/CaseBuilder/BusesTable.tsx b/web/src/components/CaseBuilder/BusesTable.tsx index 25cdc0e..f14f081 100644 --- a/web/src/components/CaseBuilder/BusesTable.tsx +++ b/web/src/components/CaseBuilder/BusesTable.tsx @@ -6,7 +6,7 @@ import Papa from "papaparse"; import { Buses, UnitCommitmentScenario } from "../../core/fixtures"; -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { CellComponent, ColumnDefinition, @@ -154,21 +154,21 @@ interface BusesTableProps { function computeBusesTableHeight(scenario: UnitCommitmentScenario): string { const numBuses = Object.keys(scenario.Buses).length; - const height = 65 + Math.min(numBuses, 15) * 28; + const height = 70 + Math.min(numBuses, 15) * 28; return `${height}px`; } function BusesTable(props: BusesTableProps) { const tableContainerRef = useRef(null); + const tableRef = useRef(null); + const [isTableBuilt, setTableBuilt] = useState(false); useEffect(() => { - const scenario = props.scenario; const onCellEdited = (cell: CellComponent) => { let newValue = cell.getValue(); let oldValue = cell.getOldValue(); // eslint-disable-next-line eqeqeq if (newValue == oldValue) return; - if (cell.getField() === "Name") { if (newValue === "") { props.onBusDeleted(oldValue); @@ -188,18 +188,54 @@ function BusesTable(props: BusesTableProps) { } } }; - if (tableContainerRef.current === null) return; - const table = new Tabulator(tableContainerRef.current, { - layout: "fitColumns", - data: generateBusesTableData(scenario), - columns: generateBusesTableColumns(scenario), - height: computeBusesTableHeight(scenario), - }); - table.on("cellEdited", (cell) => { - onCellEdited(cell); - }); - }, [props]); + if (tableRef.current === null) { + tableRef.current = new Tabulator(tableContainerRef.current, { + layout: "fitColumns", + data: generateBusesTableData(props.scenario), + columns: generateBusesTableColumns(props.scenario), + height: computeBusesTableHeight(props.scenario), + }); + tableRef.current.on("tableBuilt", () => { + setTableBuilt(true); + }); + } + if (isTableBuilt) { + const newHeight = computeBusesTableHeight(props.scenario); + const newColumns = generateBusesTableColumns(props.scenario); + const newData = generateBusesTableData(props.scenario); + const oldRows = tableRef.current.getRows(); + + // Update data + tableRef.current.replaceData(newData).then(() => {}); + + // Update columns + if (newColumns.length !== tableRef.current.getColumns().length) { + tableRef.current.setColumns(newColumns); + } + + // Update height + if (tableRef.current.options.height !== newHeight) { + tableRef.current.setHeight(newHeight); + } + + // Scroll to bottom + if (tableRef.current.getRows().length === oldRows.length + 1) { + setTimeout(() => { + const rows = tableRef.current!.getRows()!; + const lastRow = rows[rows.length - 1]!; + lastRow.scrollTo().then((r) => {}); + lastRow.getCell("Name").edit(); + }, 10); + } + + // Update callbacks + tableRef.current.off("cellEdited"); + tableRef.current.on("cellEdited", (cell) => { + onCellEdited(cell); + }); + } + }, [props, isTableBuilt]); return
; } diff --git a/web/src/components/CaseBuilder/CaseBuilder.tsx b/web/src/components/CaseBuilder/CaseBuilder.tsx index dfdaa05..9539355 100644 --- a/web/src/components/CaseBuilder/CaseBuilder.tsx +++ b/web/src/components/CaseBuilder/CaseBuilder.tsx @@ -26,16 +26,27 @@ import { renameBus, } from "../../core/Operations/busOperations"; import { + changeParameter, changeTimeHorizon, changeTimeStep, } from "../../core/Operations/parameterOperations"; import { preprocess } from "../../core/Operations/preprocessing"; +import Toast from "../Common/Forms/Toast"; const CaseBuilder = () => { - const [scenario, setScenario] = useState(TEST_SCENARIO); + const [scenario, setScenario] = useState(() => { + const savedScenario = localStorage.getItem("scenario"); + return savedScenario ? JSON.parse(savedScenario) : TEST_SCENARIO; + }); + const [toastMessage, setToastMessage] = useState(""); + + const setAndSaveScenario = (scenario: UnitCommitmentScenario) => { + setScenario(scenario); + localStorage.setItem("scenario", JSON.stringify(scenario)); + }; const onClear = () => { - setScenario(BLANK_SCENARIO); + setAndSaveScenario(BLANK_SCENARIO); }; const onSave = () => { @@ -48,7 +59,7 @@ const CaseBuilder = () => { const onBusCreated = () => { const newScenario = createBus(scenario); - setScenario(newScenario); + setAndSaveScenario(newScenario); }; const onBusDataChanged = ( @@ -58,16 +69,16 @@ const CaseBuilder = () => { ): ValidationError | null => { const [newScenario, err] = changeBusData(bus, field, newValue, scenario); if (err) { - console.log(err); + setToastMessage(err.message); return err; } - setScenario(newScenario); + setAndSaveScenario(newScenario); return null; }; const onBusDeleted = (bus: string) => { const newScenario = deleteBus(bus, scenario); - setScenario(newScenario); + setAndSaveScenario(newScenario); }; const onBusRenamed = ( @@ -76,15 +87,15 @@ const CaseBuilder = () => { ): ValidationError | null => { const [newScenario, err] = renameBus(oldName, newName, scenario); if (err) { - console.log(err); + setToastMessage(err.message); return err; } - setScenario(newScenario); + setAndSaveScenario(newScenario); return null; }; const onDataChanged = (newScenario: UnitCommitmentScenario) => { - setScenario(newScenario); + setAndSaveScenario(newScenario); }; const onLoad = (scenario: UnitCommitmentScenario) => { @@ -94,32 +105,29 @@ const CaseBuilder = () => { // Validate and assign default values if (!validate(preprocessed)) { + setToastMessage("Error loading JSON file"); console.error(validate.errors); return; } - setScenario(preprocessed); + + setAndSaveScenario(preprocessed); + setToastMessage("Data loaded successfully"); }; const onParameterChanged = (key: string, value: string) => { + let newScenario, err; if (key === "Time horizon (h)") { - const [newScenario, err] = changeTimeHorizon(scenario, value); - if (err) { - return err; - } - setScenario(newScenario); - return null; + [newScenario, err] = changeTimeHorizon(scenario, value); + } else if (key === "Time step (min)") { + [newScenario, err] = changeTimeStep(scenario, value); + } else { + [newScenario, err] = changeParameter(scenario, key, value); } - - if (key === "Time step (min)") { - const [newScenario, err] = changeTimeStep(scenario, value); - if (err) { - return err; - } - setScenario(newScenario); - return null; + if (err) { + setToastMessage(err.message); + return err; } - - console.log("onParameterChanged", key, value); + setAndSaveScenario(newScenario); return null; }; @@ -139,6 +147,7 @@ const CaseBuilder = () => { onBusDeleted={onBusDeleted} onDataChanged={onDataChanged} /> +