Initial commit

This commit is contained in:
2026-07-03 16:31:37 +07:00
commit 899c7c637d
1939 changed files with 641750 additions and 0 deletions

View File

@@ -0,0 +1,238 @@
@page "/dock-station-config"
@rendermode InteractiveWebAssemblyNoPrerender
@using RobotNet10.RobotApp.Client.Services
@using RobotNet10.RobotApp.Client.Dialogs
@using RobotNet10.RobotApp.Shared.DockStation
@inject DockStationConfigState State
@inject IDialogService DialogService
@inject ISnackbar Snackbar
@implements IDisposable
<PageTitle>Dock Station Config</PageTitle>
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="mt-4">
<MudText Typo="Typo.h5" Class="mb-4">Dock Station Configuration</MudText>
@if (!string.IsNullOrEmpty(State.ErrorMessage))
{
<MudAlert Severity="Severity.Error" Class="mb-3">
@State.ErrorMessage
</MudAlert>
}
<MudGrid>
@* Left Panel - Config List *@
<MudItem xs="12" md="5" lg="4">
<MudPaper Class="pa-3" Elevation="2">
<MudStack Row AlignItems="AlignItems.Center" Class="mb-3">
<MudText Typo="Typo.h6">Stations</MudText>
<MudSpacer />
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add"
OnClick="OpenCreateDialog" Disabled="State.IsSaving">
New
</MudButton>
</MudStack>
@if (State.IsLoading && State.Configs.Count == 0)
{
<MudProgressLinear Indeterminate />
}
else if (State.Configs.Count == 0)
{
<MudText Typo="Typo.body2" Color="Color.Tertiary" Align="Align.Center" Class="pa-4">
No dock station configs found.
</MudText>
}
else
{
<MudList T="DockStationConfigSummaryDto" Dense SelectedValueChanged="OnConfigSelected">
@foreach (var config in State.Configs)
{
<MudListItem Value="config"
Icon="@(config.IsActive ? Icons.Material.Filled.EvStation : Icons.Material.Outlined.EvStation)"
IconColor="@(config.IsActive ? Color.Success : Color.Default)">
<MudStack Spacing="0">
<MudText Typo="Typo.body1"><b>@config.StationId</b></MudText>
<MudText Typo="Typo.caption" Color="Color.Tertiary">
@(string.IsNullOrEmpty(config.ConfigName) ? "No name" : config.ConfigName)
&middot; @config.MarkerEntryCount marker(s)
</MudText>
</MudStack>
</MudListItem>
}
</MudList>
}
</MudPaper>
</MudItem>
@* Right Panel - Detail View *@
<MudItem xs="12" md="7" lg="8">
@if (State.SelectedConfig is not null)
{
var cfg = State.SelectedConfig;
<MudPaper Class="pa-4" Elevation="2">
<MudStack Row AlignItems="AlignItems.Center" Class="mb-3">
<MudText Typo="Typo.h6">@cfg.StationId</MudText>
<MudChip T="string" Size="Size.Small"
Color="@(cfg.IsActive ? Color.Success : Color.Default)">
@(cfg.IsActive ? "Active" : "Inactive")
</MudChip>
<MudSpacer />
<MudIconButton Icon="@Icons.Material.Filled.Edit" Color="Color.Primary"
OnClick="OpenEditDialog" Disabled="State.IsSaving" />
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error"
OnClick="OpenDeleteDialog" Disabled="State.IsSaving" />
</MudStack>
@if (!string.IsNullOrEmpty(cfg.ConfigName))
{
<MudText Typo="Typo.subtitle1" Class="mb-1">@cfg.ConfigName</MudText>
}
@if (!string.IsNullOrEmpty(cfg.Description))
{
<MudText Typo="Typo.body2" Color="Color.Tertiary" Class="mb-3">@cfg.Description</MudText>
}
<MudDivider Class="my-3" />
<MudText Typo="Typo.subtitle2" Class="mb-2">Search Area</MudText>
<MudGrid Spacing="2">
<MudItem xs="4"><MudText Typo="Typo.body2">X: <b>@cfg.X.ToString("F3")</b> m</MudText></MudItem>
<MudItem xs="4"><MudText Typo="Typo.body2">Y: <b>@cfg.Y.ToString("F3")</b> m</MudText></MudItem>
<MudItem xs="4"><MudText Typo="Typo.body2">Yaw: <b>@cfg.Yaw.ToString("F4")</b> rad</MudText></MudItem>
<MudItem xs="6"><MudText Typo="Typo.body2">Width: <b>@cfg.Width.ToString("F3")</b> m</MudText></MudItem>
<MudItem xs="6"><MudText Typo="Typo.body2">Length: <b>@cfg.Length.ToString("F3")</b> m</MudText></MudItem>
</MudGrid>
<MudDivider Class="my-3" />
<MudText Typo="Typo.subtitle2" Class="mb-2">Marker Entries (@cfg.MarkerEntries.Count)</MudText>
<MudPaper Style="max-height: calc(100vh - 460px); overflow-y: auto; padding-right: 4px;" Elevation="0">
@foreach (var entry in cfg.MarkerEntries.OrderBy(e => e.Priority))
{
<MudPaper Class="pa-3 mb-2" Outlined>
<MudGrid Spacing="1">
<MudItem xs="4"><MudText Typo="Typo.body2">Marker: <b>@entry.MarkerId</b></MudText></MudItem>
<MudItem xs="4"><MudText Typo="Typo.body2">Type: <b>@entry.Type</b></MudText></MudItem>
<MudItem xs="4"><MudText Typo="Typo.body2">Priority: <b>@entry.Priority</b></MudText></MudItem>
<MudItem xs="6"><MudText Typo="Typo.body2">Device: <b>@(entry.DeviceId ?? "-")</b></MudText></MudItem>
<MudItem xs="6"><MudText Typo="Typo.body2">Code: <b>@(entry.Code ?? "-")</b></MudText></MudItem>
</MudGrid>
@if (entry.ReferencePoints.Count > 0)
{
<MudText Typo="Typo.caption" Class="mt-1">
Reference Points: @string.Join(", ", entry.ReferencePoints.Select(p => $"({p.X:F3}, {p.Y:F3})"))
</MudText>
}
</MudPaper>
}
</MudPaper>
<MudDivider Class="my-3" />
<MudText Typo="Typo.caption" Color="Color.Tertiary">
Created: @cfg.CreatedAt.ToString("yyyy-MM-dd HH:mm") | Updated: @cfg.UpdatedAt.ToString("yyyy-MM-dd HH:mm")
</MudText>
</MudPaper>
}
else
{
<MudPaper Class="pa-8 d-flex align-center justify-center" Elevation="0" Style="min-height:300px">
<MudText Typo="Typo.body1" Color="Color.Tertiary">Select a dock station config to view details</MudText>
</MudPaper>
}
</MudItem>
</MudGrid>
</MudContainer>
<MudThemeProvider IsDarkMode />
<MudPopoverProvider />
<MudDialogProvider />
<MudSnackbarProvider />
@code {
protected override async Task OnInitializedAsync()
{
State.OnStateChanged += StateHasChanged;
await State.LoadConfigsAsync();
}
public void Dispose()
{
State.OnStateChanged -= StateHasChanged;
}
private async Task OnConfigSelected(DockStationConfigSummaryDto? config)
{
if (config is not null)
await State.SelectConfigAsync(config.Id);
}
private async Task OpenCreateDialog()
{
var dialog = await DialogService.ShowAsync<DockStationConfigDialog>(
"Create Dock Station Config",
new DialogParameters { ["Config"] = null },
new DialogOptions { MaxWidth = MaxWidth.Medium, FullWidth = true });
var result = await dialog.Result;
if (result is not null && !result.Canceled && result.Data is CreateDockStationConfigRequest request)
{
try
{
await State.CreateConfigAsync(request);
Snackbar.Add("Dock station config created.", Severity.Success);
}
catch (Exception ex)
{
Snackbar.Add($"Error: {ex.Message}", Severity.Error);
}
}
}
private async Task OpenEditDialog()
{
if (State.SelectedConfig is null) return;
var dialog = await DialogService.ShowAsync<DockStationConfigDialog>(
"Edit Dock Station Config",
new DialogParameters { ["Config"] = State.SelectedConfig },
new DialogOptions { MaxWidth = MaxWidth.Medium, FullWidth = true });
var result = await dialog.Result;
if (result is not null && !result.Canceled && result.Data is UpdateDockStationConfigRequest request)
{
try
{
await State.UpdateConfigAsync(State.SelectedConfig.Id, request);
Snackbar.Add("Dock station config updated.", Severity.Success);
}
catch (Exception ex)
{
Snackbar.Add($"Error: {ex.Message}", Severity.Error);
}
}
}
private async Task OpenDeleteDialog()
{
if (State.SelectedConfig is null) return;
var confirm = await DialogService.ShowMessageBoxAsync(
"Confirm Delete",
$"Are you sure you want to delete dock station config '{State.SelectedConfig.StationId}'?",
yesText: "Delete", cancelText: "Cancel");
if (confirm == true)
{
try
{
await State.DeleteConfigAsync(State.SelectedConfig.Id);
Snackbar.Add("Dock station config deleted.", Severity.Success);
}
catch (Exception ex)
{
Snackbar.Add($"Error: {ex.Message}", Severity.Error);
}
}
}
}