diff --git a/src/Chain.svelte b/src/Chain.svelte index 7c510c8..35f6c9e 100644 --- a/src/Chain.svelte +++ b/src/Chain.svelte @@ -37,124 +37,91 @@ let initial = $state(true); let prevChainCount = $state(0); let stepperDone = $state(false); - + let hasErrors = $state(false); let errors: { [field: string]: string } = $state({}); - // Helper function to check if a value is a valid number. const isValidNumber = (value: any) => value !== null && value !== undefined && !isNaN(parseFloat(value)) && isFinite(value); - function validateCurrentIteration(): boolean { - // Clear previous errors - errors = {}; - - // Validate Twinworld fields - if (chainIterations[currentChainIndex]?.twinworld) { - const value = chainIterations[currentChainIndex].twinworld.solarPanelCapacity; - if (value === null || value === undefined) { - errors[`twinworld-solarPanelCapacity-${currentChainIndex}`] = - "Solar Panel Capacity is required."; - } else if (!isValidNumber(value) || Number(value) < 0) { - errors[`twinworld-solarPanelCapacity-${currentChainIndex}`] = - "Please enter a valid positive number."; - } + function validateField(key: string, value: any, validateFn: (val: any) => string): void { + const errorMessage = validateFn(value); + if (errorMessage) { + errors = { ...errors, [key]: errorMessage }; + } else { + const { [key]: removed, ...rest } = errors; + errors = rest; } + } - // Validate Cost Model fields - if (chainIterations[currentChainIndex]?.costmodel) { - const costModel = chainIterations[currentChainIndex].costmodel; - - // Price Network Buy Consumer - if ( - costModel.priceNetworkBuyConsumer === null || - costModel.priceNetworkBuyConsumer === undefined - ) { - errors[`costmodel-priceNetworkBuyConsumer-${currentChainIndex}`] = - "Price Network Buy Consumer is required."; - } else if ( - !isValidNumber(costModel.priceNetworkBuyConsumer) || - Number(costModel.priceNetworkBuyConsumer) < 0 - ) { - errors[`costmodel-priceNetworkBuyConsumer-${currentChainIndex}`] = - "Please enter a valid positive number."; - } - - // Price Network Sell Consumer - if ( - costModel.priceNetworkSellConsumer === null || - costModel.priceNetworkSellConsumer === undefined - ) { - errors[`costmodel-priceNetworkSellConsumer-${currentChainIndex}`] = - "Price Network Sell Consumer is required."; - } else if ( - !isValidNumber(costModel.priceNetworkSellConsumer) || - Number(costModel.priceNetworkSellConsumer) < 0 - ) { - errors[`costmodel-priceNetworkSellConsumer-${currentChainIndex}`] = - "Please enter a valid positive number."; - } - - // Fixed Price Ratio - if (costModel.fixedPriceRatio === null || costModel.fixedPriceRatio === undefined) { - errors[`costmodel-fixedPriceRatio-${currentChainIndex}`] = - "Fixed Price Ratio is required."; - } else if ( - !isValidNumber(costModel.fixedPriceRatio) || - Number(costModel.fixedPriceRatio) < 0 - ) { - errors[`costmodel-fixedPriceRatio-${currentChainIndex}`] = - "Please enter a valid positive number."; - } + function validatePositiveNumber(val: any): string { + if (val === null || val === undefined || val === "") { + return "This field is required."; + } else if (!isValidNumber(val)) { + return "Please enter a valid number."; + } else if (Number(val) < 0) { + return "Please enter a valid positive number."; } + return ""; + } - // Validate Algorithm fields - if (chainIterations[currentChainIndex]?.algo) { - const algo = chainIterations[currentChainIndex].algo; - if (algo.maxTemperature === null || algo.maxTemperature === undefined) { - errors[`algo-maxTemperature-${currentChainIndex}`] = "Max Temperature is required."; - } else if (!isValidNumber(algo.maxTemperature) || Number(algo.maxTemperature) < 0) { - errors[`algo-maxTemperature-${currentChainIndex}`] = - "Please enter a valid positive number."; - } + function validateCurrentIteration(): boolean { + errors = {}; + const twinworld = chainIterations[currentChainIndex]?.twinworld; + if (twinworld) { + validateField( + `twinworld-solarPanelCapacity-${currentChainIndex}`, + twinworld.solarPanelCapacity, + validatePositiveNumber + ); } - - // Validate Energyflow fields - if (chainIterations[currentChainIndex]?.energyflow) { - const energyflow = chainIterations[currentChainIndex].energyflow; - if (energyflow.solarPanelsFactor === null || energyflow.solarPanelsFactor === undefined) { - errors[`energyflow-solarPanelsFactor-${currentChainIndex}`] = - "Solar Panels Factor is required."; - } else if ( - !isValidNumber(energyflow.solarPanelsFactor) || - Number(energyflow.solarPanelsFactor) < 0 - ) { - errors[`energyflow-solarPanelsFactor-${currentChainIndex}`] = - "Please enter a valid positive number."; - } - - if (energyflow.energyUsageFactor === null || energyflow.energyUsageFactor === undefined) { - errors[`energyflow-energyUsageFactor-${currentChainIndex}`] = - "Energy Usage Factor is required."; - } else if ( - !isValidNumber(energyflow.energyUsageFactor) || - Number(energyflow.energyUsageFactor) < 0 - ) { - errors[`energyflow-energyUsageFactor-${currentChainIndex}`] = - "Please enter a valid positive number."; - } + const costmodel = chainIterations[currentChainIndex]?.costmodel; + if (costmodel) { + validateField( + `costmodel-priceNetworkBuyConsumer-${currentChainIndex}`, + costmodel.priceNetworkBuyConsumer, + validatePositiveNumber + ); + validateField( + `costmodel-priceNetworkSellConsumer-${currentChainIndex}`, + costmodel.priceNetworkSellConsumer, + validatePositiveNumber + ); + validateField( + `costmodel-fixedPriceRatio-${currentChainIndex}`, + costmodel.fixedPriceRatio, + validatePositiveNumber + ); + } + const algo = chainIterations[currentChainIndex]?.algo; + if (algo) { + validateField( + `algo-maxTemperature-${currentChainIndex}`, + algo.maxTemperature, + validatePositiveNumber + ); + } + const energyflow = chainIterations[currentChainIndex]?.energyflow; + if (energyflow) { + validateField( + `energyflow-solarPanelsFactor-${currentChainIndex}`, + energyflow.solarPanelsFactor, + validatePositiveNumber + ); + validateField( + `energyflow-energyUsageFactor-${currentChainIndex}`, + energyflow.energyUsageFactor, + validatePositiveNumber + ); } - // Return true if no errors, false otherwise. return Object.keys(errors).length === 0; } function nextChainIteration() { - // Validate current chain settings before proceeding if (!validateCurrentIteration()) { alert("Please correct the errors before proceeding."); return; } - if (currentChainIndex < chainCount - 1) { currentChainIndex++; loadEditors("costmodelalgo", chainIterations[currentChainIndex].costmodel.algorithm); @@ -178,7 +145,6 @@ } async function runChain() { - // Validate current iteration before starting the chain run. if (!validateCurrentIteration()) { alert("Please correct the errors before running the chain."); return; @@ -211,7 +177,7 @@ function loadEditors(id: string, value: string) { if (!document.getElementById(id)) return; - // @ts-ignore + // @ts-ignore - aceEditor is defined in the global scope const aceEditor = ace.edit(id, { mode: "ace/mode/javascript", selectionStyle: "text", @@ -254,6 +220,10 @@ loadEditors("costmodelalgo", chainIterations[currentChainIndex].costmodel.algorithm); loadEditors("algorithm", chainIterations[currentChainIndex].algo.algorithm); }); + + $effect(() => { + hasErrors = Object.keys(errors).length > 0; + }); @@ -272,14 +242,12 @@ Please select a chain length to continue {/if} - {/if} diff --git a/src/excel.svelte.ts b/src/excel.svelte.ts index a3aec84..71dc24c 100644 --- a/src/excel.svelte.ts +++ b/src/excel.svelte.ts @@ -150,7 +150,7 @@ export function downloadExcel(name?: string): void { const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; - a.download = name || "session-data.xlsx"; + a.download = name + ".xlsx" || "session-data.xlsx"; document.body.appendChild(a); a.click(); document.body.removeChild(a);