200 lines
5.9 KiB
Plaintext
200 lines
5.9 KiB
Plaintext
@using System.Text.Json
|
|
@using MudBlazor
|
|
@using Microsoft.AspNetCore.Components.Forms
|
|
|
|
<MudDialog>
|
|
|
|
<!-- ================= TITLE ================= -->
|
|
<TitleContent>
|
|
<MudStack Row AlignItems="AlignItems.Center" Spacing="2">
|
|
<MudIcon Icon="@Icons.Material.Filled.UploadFile"
|
|
Color="Color.Primary" />
|
|
<MudText Typo="Typo.h6">
|
|
Import Order JSON
|
|
</MudText>
|
|
</MudStack>
|
|
</TitleContent>
|
|
|
|
<!-- ================= CONTENT ================= -->
|
|
<DialogContent>
|
|
|
|
@if (ShowWarning)
|
|
{
|
|
<MudAlert Severity="Severity.Warning"
|
|
Variant="Variant.Outlined"
|
|
Class="mb-4">
|
|
Only valid <b>VDA 5050 Order JSON</b> is accepted.<br />
|
|
Invalid structure will be rejected.
|
|
</MudAlert>
|
|
}
|
|
|
|
<!-- ===== FILE INPUT (PURE BLAZOR) ===== -->
|
|
<div class="mb-4">
|
|
<label class="mud-button-root mud-button mud-button-outlined mud-button-outlined-primary"
|
|
style="cursor:pointer;">
|
|
<MudIcon Icon="@Icons.Material.Filled.AttachFile" Class="mr-2" />
|
|
Choose JSON file
|
|
<InputFile OnChange="OnFileSelected"
|
|
accept=".json"
|
|
style="display:none" />
|
|
</label>
|
|
</div>
|
|
|
|
<MudDivider Class="my-3" />
|
|
|
|
<!-- ===== PASTE JSON ===== -->
|
|
<MudTextField @bind-Value="JsonText"
|
|
Label="Paste Order JSON"
|
|
Lines="20"
|
|
Variant="Variant.Filled"
|
|
Immediate
|
|
Style="font-family: monospace" />
|
|
|
|
@if (!string.IsNullOrEmpty(ErrorMessage))
|
|
{
|
|
<MudAlert Severity="Severity.Error" Class="mt-3">
|
|
@ErrorMessage
|
|
</MudAlert>
|
|
}
|
|
|
|
</DialogContent>
|
|
|
|
<!-- ================= ACTIONS ================= -->
|
|
<DialogActions>
|
|
<MudButton OnClick="Cancel">
|
|
Cancel
|
|
</MudButton>
|
|
|
|
<MudButton Variant="Variant.Filled"
|
|
Color="Color.Primary"
|
|
OnClick="ValidateAndImport">
|
|
Import
|
|
</MudButton>
|
|
</DialogActions>
|
|
|
|
</MudDialog>
|
|
|
|
@code {
|
|
[CascadingParameter]
|
|
public IMudDialogInstance MudDialog { get; set; } = default!;
|
|
|
|
public string JsonText { get; set; } = "";
|
|
public string? ErrorMessage;
|
|
public bool ShowWarning { get; set; }
|
|
|
|
private void Cancel() => MudDialog.Cancel();
|
|
|
|
// ================= FILE HANDLER =================
|
|
private async Task OnFileSelected(InputFileChangeEventArgs e)
|
|
{
|
|
ErrorMessage = null;
|
|
ShowWarning = false;
|
|
|
|
var file = e.File;
|
|
if (file == null)
|
|
return;
|
|
|
|
if (!file.Name.EndsWith(".json", StringComparison.OrdinalIgnoreCase) &&
|
|
!file.Name.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
ShowWarning = true;
|
|
ErrorMessage = "Only .json or .txt files are supported.";
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
using var stream = file.OpenReadStream(maxAllowedSize: 1_048_576);
|
|
using var reader = new StreamReader(stream);
|
|
|
|
JsonText = await reader.ReadToEndAsync();
|
|
StateHasChanged();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ShowWarning = true;
|
|
ErrorMessage = $"Failed to read file: {ex.Message}";
|
|
}
|
|
}
|
|
|
|
// ================= VALIDATE & IMPORT =================
|
|
private void ValidateAndImport()
|
|
{
|
|
ErrorMessage = null;
|
|
ShowWarning = false;
|
|
|
|
if (string.IsNullOrWhiteSpace(JsonText))
|
|
{
|
|
ShowWarning = true;
|
|
ErrorMessage = "JSON content is empty.";
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
using var doc = JsonDocument.Parse(JsonText);
|
|
var root = doc.RootElement;
|
|
|
|
// ===== BASIC STRUCTURE CHECK =====
|
|
if (!root.TryGetProperty("nodes", out _) ||
|
|
!root.TryGetProperty("edges", out _))
|
|
{
|
|
throw new Exception("Missing 'nodes' or 'edges' field.");
|
|
}
|
|
|
|
var order = OrderMessage.FromSchemaObject(root);
|
|
ValidateOrder(order);
|
|
|
|
MudDialog.Close(DialogResult.Ok(order));
|
|
}
|
|
catch (JsonException)
|
|
{
|
|
ShowWarning = true;
|
|
ErrorMessage = "Invalid JSON format.";
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ShowWarning = true;
|
|
ErrorMessage = ex.Message;
|
|
}
|
|
}
|
|
|
|
// ================= DOMAIN VALIDATION =================
|
|
private void ValidateOrder(OrderMessage order)
|
|
{
|
|
if (order.Nodes.Count == 0)
|
|
throw new Exception("Order must contain at least one node.");
|
|
|
|
if (order.Nodes.Count != order.Edges.Count + 1)
|
|
throw new Exception(
|
|
$"Invalid path structure: Nodes count ({order.Nodes.Count}) " +
|
|
$"must equal Edges count + 1 ({order.Edges.Count + 1})."
|
|
);
|
|
|
|
var nodeIds = order.Nodes
|
|
.Select(n => n.NodeId)
|
|
.ToHashSet(StringComparer.Ordinal);
|
|
|
|
foreach (var e in order.Edges)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(e.StartNodeId) ||
|
|
string.IsNullOrWhiteSpace(e.EndNodeId))
|
|
{
|
|
throw new Exception(
|
|
$"Edge '{e.EdgeId}' must define both StartNodeId and EndNodeId."
|
|
);
|
|
}
|
|
|
|
if (!nodeIds.Contains(e.StartNodeId))
|
|
throw new Exception(
|
|
$"Edge '{e.EdgeId}' references unknown StartNodeId '{e.StartNodeId}'."
|
|
);
|
|
|
|
if (!nodeIds.Contains(e.EndNodeId))
|
|
throw new Exception(
|
|
$"Edge '{e.EdgeId}' references unknown EndNodeId '{e.EndNodeId}'."
|
|
);
|
|
}
|
|
}
|
|
}
|