@using ScadaLink.CentralUI.ScriptAnalysis
@using System.Text.Json
@*
Renders an input row per declared parameter so the user can supply values
for a script test run. Primitive types get typed inputs (text / number /
checkbox); Object and List fall back to a JSON textarea with inline parse
errors. The companion SchemaBuilder edits the schema; this edits values.
*@
@if (Shapes.Count == 0)
{
No parameters declared.
}
else
{
@foreach (var shape in Shapes)
{
@shape.Name
@shape.Type@(shape.Required ? "" : "?")
@RenderInput(shape)
@if (_parseErrors.TryGetValue(shape.Name, out var err))
{
@err
}
}
}
@code {
[Parameter] public string? ParameterDefinitions { get; set; }
[Parameter] public Dictionary Values { get; set; } = new();
[Parameter] public EventCallback> ValuesChanged { get; set; }
private IReadOnlyList Shapes =>
ScriptParameterNames.ParseShapes(ParameterDefinitions);
private readonly Dictionary _rawText = new();
private readonly Dictionary _parseErrors = new();
private static string FieldId(ParameterShape shape) => $"param-{shape.Name}";
private RenderFragment RenderInput(ParameterShape shape) => __builder =>
{
switch (shape.Type)
{
case "Boolean":
SetBool(shape.Name, (bool)(e.Value ?? false))" />
break;
case "Integer":
SetNumeric(shape.Name, (string?)e.Value, integerOnly: true)" />
break;
case "Float":
SetNumeric(shape.Name, (string?)e.Value, integerOnly: false)" />
break;
case "String":
SetString(shape.Name, (string?)e.Value)" />
break;
default: // Object, List, List<...>, unknown
break;
}
};
private string AsRaw(string name) =>
_rawText.TryGetValue(name, out var raw) ? raw : "";
private bool AsBool(string name) =>
Values.TryGetValue(name, out var v) && v is bool b && b;
private async Task SetString(string name, string? raw)
{
_rawText[name] = raw ?? "";
_parseErrors.Remove(name);
Values[name] = raw ?? "";
await ValuesChanged.InvokeAsync(Values);
}
private async Task SetBool(string name, bool value)
{
_parseErrors.Remove(name);
Values[name] = value;
await ValuesChanged.InvokeAsync(Values);
}
private async Task SetNumeric(string name, string? raw, bool integerOnly)
{
_rawText[name] = raw ?? "";
if (string.IsNullOrWhiteSpace(raw))
{
_parseErrors.Remove(name);
Values.Remove(name);
await ValuesChanged.InvokeAsync(Values);
return;
}
if (integerOnly && long.TryParse(raw, out var i))
{
_parseErrors.Remove(name);
Values[name] = i;
}
else if (!integerOnly && double.TryParse(raw,
System.Globalization.NumberStyles.Float,
System.Globalization.CultureInfo.InvariantCulture, out var d))
{
_parseErrors.Remove(name);
Values[name] = d;
}
else
{
_parseErrors[name] = integerOnly ? "Not a valid integer." : "Not a valid number.";
Values.Remove(name);
}
await ValuesChanged.InvokeAsync(Values);
}
private async Task SetJson(string name, string? raw)
{
_rawText[name] = raw ?? "";
if (string.IsNullOrWhiteSpace(raw))
{
_parseErrors.Remove(name);
Values.Remove(name);
await ValuesChanged.InvokeAsync(Values);
return;
}
try
{
using var doc = JsonDocument.Parse(raw);
Values[name] = JsonElementToObject(doc.RootElement.Clone());
_parseErrors.Remove(name);
}
catch (JsonException ex)
{
_parseErrors[name] = $"JSON parse error: {ex.Message}";
Values.Remove(name);
}
await ValuesChanged.InvokeAsync(Values);
}
private static object? JsonElementToObject(JsonElement element)
{
return element.ValueKind switch
{
JsonValueKind.String => element.GetString(),
JsonValueKind.Number => element.TryGetInt64(out var i) ? (object)i : element.GetDouble(),
JsonValueKind.True => true,
JsonValueKind.False => false,
JsonValueKind.Null => null,
JsonValueKind.Array => element.EnumerateArray().Select(JsonElementToObject).ToList(),
JsonValueKind.Object => element.EnumerateObject()
.ToDictionary(p => p.Name, p => JsonElementToObject(p.Value)),
_ => null
};
}
}