313 lines
14 KiB
Plaintext
313 lines
14 KiB
Plaintext
@page "/transformations"
|
|
@attribute [Authorize(Policy = TrafagSalesExporter.Security.SecurityPolicies.AdminOnly)]
|
|
@using System.Reflection
|
|
@using TrafagSalesExporter.Models
|
|
@using TrafagSalesExporter.Services
|
|
@inject ITransformationsPageService TransformationsPageActions
|
|
@inject ITransformationCatalog TransformationCatalog
|
|
@inject ISnackbar Snackbar
|
|
@inject IUiTextService UiText
|
|
|
|
<PageTitle>@T("Transformationen", "Transformations")</PageTitle>
|
|
|
|
<MudText Typo="Typo.h4" Class="mb-4">@T("Transformer Ansicht", "Transformation view")</MudText>
|
|
<MudText Typo="Typo.body1" Class="mb-4">@T("Definiere pro Quellsystem einfache Feldregeln und komplexe record-basierte Strategien.", "Define simple field rules and complex record-based strategies per source system.")</MudText>
|
|
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudAlert Severity="Severity.Info" Dense="true" Variant="Variant.Outlined" Class="mb-3">
|
|
`Value`-Regeln arbeiten feldweise. `Record`-Regeln rufen eine registrierte C#-Strategie auf und koennen mehrere Felder eines Datensatzes verwenden.
|
|
</MudAlert>
|
|
|
|
<MudStack Row="true" Spacing="2" Class="mb-3">
|
|
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add" OnClick="AddRule">
|
|
@T("Regel hinzufuegen", "Add rule")
|
|
</MudButton>
|
|
<MudButton Variant="Variant.Outlined" Color="Color.Secondary" StartIcon="@Icons.Material.Filled.Save" OnClick="SaveAllAsync">
|
|
@T("Alle speichern", "Save all")
|
|
</MudButton>
|
|
</MudStack>
|
|
|
|
<MudTable Items="_rules" Dense Hover Striped>
|
|
<HeaderContent>
|
|
<MudTh>Aktiv</MudTh>
|
|
<MudTh>System</MudTh>
|
|
<MudTh>Scope</MudTh>
|
|
<MudTh>Source</MudTh>
|
|
<MudTh>Target</MudTh>
|
|
<MudTh>Typ / Klasse</MudTh>
|
|
<MudTh>Argument</MudTh>
|
|
<MudTh>Sort</MudTh>
|
|
<MudTh>Info</MudTh>
|
|
<MudTh>Aktionen</MudTh>
|
|
</HeaderContent>
|
|
<RowTemplate>
|
|
<MudTd><MudCheckBox @bind-Value="context.IsActive" /></MudTd>
|
|
<MudTd>
|
|
<MudSelect T="string" Value="@context.SourceSystem" ValueChanged="@(v => context.SourceSystem = v)" Dense>
|
|
@foreach (var system in _sourceSystems.Where(x => x.IsActive))
|
|
{
|
|
<MudSelectItem Value="@system.Code">@system.DisplayName (@system.Code)</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudTd>
|
|
<MudTd>
|
|
<MudSelect T="string" Value="@context.RuleScope" ValueChanged="@(v => ChangeRuleScope(context, v))" Dense>
|
|
@foreach (var scope in _ruleScopes)
|
|
{
|
|
<MudSelectItem Value="@scope">@scope</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudTd>
|
|
<MudTd>
|
|
@if (IsRecordScope(context))
|
|
{
|
|
<MudChip T="string" Color="Color.Default" Variant="Variant.Outlined" Size="Size.Small" Text="Record-Regel" />
|
|
}
|
|
else
|
|
{
|
|
<MudSelect T="string" Value="@context.SourceField" ValueChanged="@(v => context.SourceField = v)" Dense>
|
|
@foreach (var field in _recordFields)
|
|
{
|
|
<MudSelectItem Value="@field">@field</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
}
|
|
</MudTd>
|
|
<MudTd>
|
|
<MudSelect T="string" Value="@context.TargetField" ValueChanged="@(v => context.TargetField = v)" Dense>
|
|
@foreach (var field in _recordFields)
|
|
{
|
|
<MudSelectItem Value="@field">@field</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
</MudTd>
|
|
<MudTd>
|
|
@{
|
|
var availableTypes = GetTypesForScope(context.RuleScope);
|
|
}
|
|
<MudSelect T="string"
|
|
@key="@GetTypeSelectKey(context)"
|
|
Value="@context.TransformationType"
|
|
ValueChanged="@(v => context.TransformationType = v)"
|
|
Dense
|
|
HelperText="@GetTypeHelperText(context)">
|
|
@foreach (var type in availableTypes)
|
|
{
|
|
<MudSelectItem Value="@type.Key">@(IsRecordScope(context) ? $"Klasse: {type.Key}" : type.Key)</MudSelectItem>
|
|
}
|
|
</MudSelect>
|
|
@if (IsRecordScope(context))
|
|
{
|
|
<MudText Typo="Typo.caption" Class="mt-1">
|
|
Hier waehlt man die registrierte C#-Strategie.
|
|
</MudText>
|
|
}
|
|
</MudTd>
|
|
<MudTd>
|
|
<MudTextField T="string" Value="@context.Argument" ValueChanged="@(v => context.Argument = v)"
|
|
HelperText="@GetArgumentHelperText(context)" />
|
|
</MudTd>
|
|
<MudTd>
|
|
<MudNumericField T="int" Value="@context.SortOrder" ValueChanged="@(v => context.SortOrder = v)" Dense />
|
|
</MudTd>
|
|
<MudTd>
|
|
@{
|
|
var catalogItem = GetCatalogItem(context);
|
|
}
|
|
<MudStack Spacing="1">
|
|
<MudText Typo="Typo.caption">@((catalogItem?.Description ?? T("Keine Beschreibung.", "No description.")) )</MudText>
|
|
<MudButton Variant="Variant.Text" Color="Color.Info" Size="Size.Small"
|
|
StartIcon="@Icons.Material.Filled.Code"
|
|
Disabled="@(catalogItem is null)"
|
|
OnClick="() => ShowCode(context)">
|
|
@T("Code anzeigen", "Show code")
|
|
</MudButton>
|
|
</MudStack>
|
|
</MudTd>
|
|
<MudTd>
|
|
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" Size="Size.Small"
|
|
OnClick="() => RemoveRule(context)" />
|
|
</MudTd>
|
|
</RowTemplate>
|
|
</MudTable>
|
|
</MudPaper>
|
|
|
|
<MudDialog @bind-Visible="_codeDialogVisible" Options="_codeDialogOptions">
|
|
<TitleContent>
|
|
<MudText Typo="Typo.h6">@T("Transformationscode", "Transformation code")</MudText>
|
|
</TitleContent>
|
|
<DialogContent>
|
|
@if (_selectedCatalogItem is not null)
|
|
{
|
|
<MudStack Spacing="2">
|
|
<MudText Typo="Typo.subtitle2">@_selectedCatalogItem.Key (@_selectedCatalogItem.RuleScope)</MudText>
|
|
<MudText Typo="Typo.body2">@_selectedCatalogItem.Description</MudText>
|
|
<MudText Typo="Typo.caption">Klasse: @_selectedCatalogItem.TypeName</MudText>
|
|
<MudText Typo="Typo.caption">
|
|
Datei:
|
|
<MudLink Href="@GetSourceViewerUrl(_selectedCatalogItem.SourceFile, _selectedCatalogItem.TypeName)" Target="_blank">
|
|
@_selectedCatalogItem.SourceFile
|
|
</MudLink>
|
|
</MudText>
|
|
<MudPaper Class="pa-3">
|
|
<MudText Typo="Typo.caption">Snippet</MudText>
|
|
<pre style="margin:0; white-space:pre-wrap;">@_selectedCatalogItem.CodeSnippet</pre>
|
|
</MudPaper>
|
|
@if (_selectedRule is not null)
|
|
{
|
|
<MudPaper Class="pa-3">
|
|
<MudText Typo="Typo.caption">Aktuelle Regel</MudText>
|
|
<MudText Typo="Typo.body2">System: @_selectedRule.SourceSystem</MudText>
|
|
<MudText Typo="Typo.body2">Target: @_selectedRule.TargetField</MudText>
|
|
@if (!string.IsNullOrWhiteSpace(_selectedRule.SourceField))
|
|
{
|
|
<MudText Typo="Typo.body2">Source: @_selectedRule.SourceField</MudText>
|
|
}
|
|
<MudText Typo="Typo.body2">Argument: @(string.IsNullOrWhiteSpace(_selectedRule.Argument) ? "-" : _selectedRule.Argument)</MudText>
|
|
</MudPaper>
|
|
}
|
|
</MudStack>
|
|
}
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<MudButton Variant="Variant.Text" OnClick="CloseCodeDialog">@T("Schliessen", "Close")</MudButton>
|
|
</DialogActions>
|
|
</MudDialog>
|
|
|
|
@code {
|
|
private readonly string[] _ruleScopes = ["Value", "Record"];
|
|
private readonly string[] _recordFields = typeof(SalesRecord)
|
|
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
|
.Select(p => p.Name)
|
|
.OrderBy(n => n)
|
|
.ToArray();
|
|
|
|
private List<FieldTransformationRule> _rules = new();
|
|
private List<SourceSystemDefinition> _sourceSystems = [];
|
|
private IReadOnlyList<TransformationCatalogItem> _catalogItems = [];
|
|
private bool _codeDialogVisible;
|
|
private FieldTransformationRule? _selectedRule;
|
|
private TransformationCatalogItem? _selectedCatalogItem;
|
|
private readonly DialogOptions _codeDialogOptions = new() { CloseButton = true, MaxWidth = MaxWidth.Medium, FullWidth = true };
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
_catalogItems = TransformationCatalog.GetAll();
|
|
await LoadAsync();
|
|
}
|
|
|
|
private async Task LoadAsync()
|
|
{
|
|
var state = await TransformationsPageActions.LoadAsync();
|
|
_sourceSystems = state.SourceSystems;
|
|
_rules = state.Rules;
|
|
|
|
foreach (var rule in _rules)
|
|
{
|
|
rule.RuleScope = string.IsNullOrWhiteSpace(rule.RuleScope) ? "Value" : rule.RuleScope;
|
|
if (!GetTypesForScope(rule.RuleScope).Any(x => string.Equals(x.Key, rule.TransformationType, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
rule.TransformationType = GetTypesForScope(rule.RuleScope).FirstOrDefault()?.Key ?? "Copy";
|
|
}
|
|
}
|
|
}
|
|
|
|
private void AddRule()
|
|
{
|
|
var nextSort = _rules.Count == 0 ? 10 : _rules.Max(r => r.SortOrder) + 10;
|
|
_rules.Add(new FieldTransformationRule
|
|
{
|
|
SourceSystem = _sourceSystems.FirstOrDefault(x => x.IsActive)?.Code ?? "SAP",
|
|
RuleScope = "Value",
|
|
SourceField = nameof(SalesRecord.Material),
|
|
TargetField = nameof(SalesRecord.Material),
|
|
TransformationType = "Copy",
|
|
SortOrder = nextSort,
|
|
IsActive = true
|
|
});
|
|
}
|
|
|
|
private void RemoveRule(FieldTransformationRule rule)
|
|
{
|
|
_rules.Remove(rule);
|
|
}
|
|
|
|
private async Task SaveAllAsync()
|
|
{
|
|
_rules = await TransformationsPageActions.SaveAllAsync(_rules);
|
|
|
|
Snackbar.Add(T("Transformationsregeln gespeichert.", "Transformation rules saved."), Severity.Success);
|
|
await LoadAsync();
|
|
}
|
|
|
|
private IReadOnlyList<TransformationCatalogItem> GetTypesForScope(string? ruleScope)
|
|
{
|
|
var scope = string.IsNullOrWhiteSpace(ruleScope) ? "Value" : ruleScope;
|
|
return TransformationCatalog.GetByScope(scope);
|
|
}
|
|
|
|
private static bool IsRecordScope(FieldTransformationRule rule)
|
|
=> string.Equals(rule.RuleScope, "Record", StringComparison.OrdinalIgnoreCase);
|
|
|
|
private void ChangeRuleScope(FieldTransformationRule rule, string scope)
|
|
{
|
|
rule.RuleScope = scope;
|
|
var firstType = GetTypesForScope(scope).FirstOrDefault()?.Key;
|
|
if (!string.IsNullOrWhiteSpace(firstType))
|
|
rule.TransformationType = firstType;
|
|
|
|
if (IsRecordScope(rule))
|
|
rule.SourceField = string.Empty;
|
|
else if (string.IsNullOrWhiteSpace(rule.SourceField))
|
|
rule.SourceField = nameof(SalesRecord.Material);
|
|
}
|
|
|
|
private string GetArgumentHelperText(FieldTransformationRule rule)
|
|
{
|
|
var item = _catalogItems.FirstOrDefault(x =>
|
|
string.Equals(x.RuleScope, rule.RuleScope, StringComparison.OrdinalIgnoreCase) &&
|
|
string.Equals(x.Key, rule.TransformationType, StringComparison.OrdinalIgnoreCase));
|
|
|
|
return item?.Description ?? T("Optionales Argument.", "Optional argument.");
|
|
}
|
|
|
|
private TransformationCatalogItem? GetCatalogItem(FieldTransformationRule rule)
|
|
=> _catalogItems.FirstOrDefault(x =>
|
|
string.Equals(x.RuleScope, rule.RuleScope, StringComparison.OrdinalIgnoreCase) &&
|
|
string.Equals(x.Key, rule.TransformationType, StringComparison.OrdinalIgnoreCase));
|
|
|
|
private void ShowCode(FieldTransformationRule rule)
|
|
{
|
|
_selectedRule = rule;
|
|
_selectedCatalogItem = GetCatalogItem(rule);
|
|
_codeDialogVisible = _selectedCatalogItem is not null;
|
|
}
|
|
|
|
private void CloseCodeDialog()
|
|
{
|
|
_codeDialogVisible = false;
|
|
_selectedRule = null;
|
|
_selectedCatalogItem = null;
|
|
}
|
|
|
|
private static string GetSourceViewerUrl(string sourceFile, string typeName)
|
|
=> $"/source-viewer?path={Uri.EscapeDataString(sourceFile)}&type={Uri.EscapeDataString(typeName)}";
|
|
|
|
private static string GetTypeSelectKey(FieldTransformationRule rule)
|
|
=> $"{rule.Id}:{rule.RuleScope}:{rule.TransformationType}";
|
|
|
|
private string GetTypeHelperText(FieldTransformationRule rule)
|
|
{
|
|
var types = GetTypesForScope(rule.RuleScope);
|
|
return types.Count == 0
|
|
? T("Keine Typen fuer diesen Scope registriert.", "No types registered for this scope.")
|
|
: IsRecordScope(rule)
|
|
? string.Format(T("Verfuegbare Klassen: {0}", "Available classes: {0}"), string.Join(", ", types.Select(x => x.Key)))
|
|
: string.Format(T("Verfuegbare Typen: {0}", "Available types: {0}"), string.Join(", ", types.Select(x => x.Key)));
|
|
}
|
|
}
|
|
|
|
@code {
|
|
private string T(string german, string english) => UiText.Text(german, english);
|
|
}
|