Title field now validates on input, not just on blur. Added OnTitleChanged handler to clear validation messages for CatTitle as the user types. OnFieldChanged now only clears messages for UpdateProcedure. This enhances real-time validation feedback for users.
361 lines
12 KiB
Plaintext
361 lines
12 KiB
Plaintext
@using Microsoft.AspNetCore.Components.Forms
|
|
@inject CatalogApiClient Api
|
|
|
|
<style>
|
|
.action-panel { margin-bottom: 16px; }
|
|
.grid-section { margin-top: 12px; }
|
|
.catalog-grid .dxbl-grid-sort-asc,
|
|
.catalog-grid .dxbl-grid-sort-desc {
|
|
display: none;
|
|
}
|
|
.catalog-grid th.dxbl-grid-header-sortable {
|
|
position: relative;
|
|
padding-right: 1.5rem;
|
|
}
|
|
.catalog-grid th.dxbl-grid-header-sortable::before,
|
|
.catalog-grid th.dxbl-grid-header-sortable::after {
|
|
content: "";
|
|
position: absolute;
|
|
right: 0.45rem;
|
|
width: 0.7rem;
|
|
height: 0.7rem;
|
|
background-repeat: no-repeat;
|
|
background-size: 0.7rem 0.7rem;
|
|
opacity: 0.35;
|
|
pointer-events: none;
|
|
}
|
|
.catalog-grid th.dxbl-grid-header-sortable::before {
|
|
top: 38%;
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.957 10.999a1 1 0 0 1-.821-1.571l2.633-3.785a1.5 1.5 0 0 1 2.462 0l2.633 3.785a1 1 0 0 1-.821 1.57H4.957Z' fill='%23888888'/%3E%3C/svg%3E");
|
|
}
|
|
.catalog-grid th.dxbl-grid-header-sortable::after {
|
|
top: 58%;
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.957 5a1 1 0 0 0-.821 1.571l2.633 3.784a1.5 1.5 0 0 0 2.462 0l2.633-3.784A1 1 0 0 0 11.043 5H4.957Z' fill='%23888888'/%3E%3C/svg%3E");
|
|
}
|
|
.catalog-grid th.dxbl-grid-header-sortable[aria-sort="ascending"]::after {
|
|
opacity: 0;
|
|
}
|
|
.catalog-grid th.dxbl-grid-header-sortable[aria-sort="descending"]::before {
|
|
opacity: 0;
|
|
}
|
|
.catalog-grid .filter-search-input input {
|
|
padding-right: 1.75rem;
|
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M9.309 10.016a4.5 4.5 0 1 1 .707-.707l3.838 3.837a.5.5 0 0 1-.708.708L9.31 10.016ZM10 6.5a3.5 3.5 0 1 0-7 0 3.5 3.5 0 0 0 7 0Z' fill='%23666666'/%3E%3C/svg%3E");
|
|
background-repeat: no-repeat;
|
|
background-position: right 0.5rem center;
|
|
background-size: 0.9rem;
|
|
}
|
|
.catalog-edit-popup {
|
|
min-width: 720px;
|
|
}
|
|
</style>
|
|
|
|
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
|
{
|
|
<div class="alert alert-danger" role="alert">@errorMessage</div>
|
|
}
|
|
else if (!string.IsNullOrWhiteSpace(infoMessage))
|
|
{
|
|
<div class="alert alert-success" role="alert">@infoMessage</div>
|
|
}
|
|
|
|
@if (isLoading)
|
|
{
|
|
<p><em>Lade Daten...</em></p>
|
|
}
|
|
else if (items.Count == 0)
|
|
{
|
|
<p>Keine Einträge vorhanden.</p>
|
|
}
|
|
else
|
|
{
|
|
<div class="grid-section">
|
|
<DxGrid Data="@items"
|
|
TItem="CatalogReadDto"
|
|
KeyFieldName="@nameof(CatalogReadDto.Guid)"
|
|
ShowFilterRow="true"
|
|
PageSize="10"
|
|
CssClass="mb-4 catalog-grid"
|
|
EditMode="GridEditMode.PopupEditForm"
|
|
PopupEditFormCssClass="catalog-edit-popup"
|
|
CustomizeEditModel="OnCustomizeEditModel"
|
|
EditModelSaving="OnEditModelSaving"
|
|
DataItemDeleting="OnDataItemDeleting">
|
|
<Columns>
|
|
<DxGridCommandColumn Width="120px" />
|
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.Guid)" Caption="Id" Width="140px" SortIndex="0" SortOrder="GridColumnSortOrder.Ascending" />
|
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.CatTitle)" Caption="Titel" />
|
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.CatString)" Caption="String" />
|
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.AddedWho)" Caption="Angelegt von" ReadOnly="true" />
|
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.AddedWhen)" Caption="Angelegt am" ReadOnly="true" />
|
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.ChangedWho)" Caption="Geändert von" ReadOnly="true" />
|
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.ChangedWhen)" Caption="Geändert am" ReadOnly="true" />
|
|
</Columns>
|
|
<EditFormTemplate Context="editFormContext">
|
|
@{ SetEditContext(editFormContext.EditContext); var editModel = (CatalogEditModel)editFormContext.EditModel; }
|
|
<DxFormLayout ColCount="2">
|
|
<DxFormLayoutItem Caption="Titel">
|
|
<DxTextBox @bind-Text="editModel.CatTitle"
|
|
@bind-Text:after="OnTitleChanged"
|
|
BindValueMode="BindValueMode.OnInput"
|
|
Width="100%" />
|
|
</DxFormLayoutItem>
|
|
<DxFormLayoutItem Caption="Kennung">
|
|
<DxTextBox @bind-Text="editModel.CatString" Width="100%" />
|
|
</DxFormLayoutItem>
|
|
<DxFormLayoutItem Caption="Update-Prozedur">
|
|
<DxComboBox Data="@procedureOptions"
|
|
TData="ProcedureOption"
|
|
TValue="int"
|
|
TextFieldName="Text"
|
|
ValueFieldName="Value"
|
|
@bind-Value="editModel.UpdateProcedure"
|
|
Width="100%" />
|
|
</DxFormLayoutItem>
|
|
<DxFormLayoutItem ColSpanMd="12">
|
|
<ValidationSummary />
|
|
</DxFormLayoutItem>
|
|
</DxFormLayout>
|
|
</EditFormTemplate>
|
|
</DxGrid>
|
|
</div>
|
|
}
|
|
|
|
@code {
|
|
private List<CatalogReadDto> items = new();
|
|
private bool isLoading;
|
|
private string? errorMessage;
|
|
private string? infoMessage;
|
|
private EditContext? editContext;
|
|
private ValidationMessageStore? validationMessageStore;
|
|
|
|
private readonly List<ProcedureOption> procedureOptions = new()
|
|
{
|
|
new() { Value = 0, Text = "PRTBMY_CATALOG_UPDATE" },
|
|
new() { Value = 1, Text = "PRTBMY_CATALOG_SAVE" }
|
|
};
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
await LoadCatalogs();
|
|
}
|
|
|
|
private void SetEditContext(EditContext context)
|
|
{
|
|
if (editContext == context)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (editContext != null)
|
|
{
|
|
editContext.OnFieldChanged -= OnEditFieldChanged;
|
|
}
|
|
|
|
editContext = context;
|
|
validationMessageStore = new ValidationMessageStore(editContext);
|
|
editContext.OnFieldChanged += OnEditFieldChanged;
|
|
}
|
|
|
|
private void OnEditFieldChanged(object? sender, FieldChangedEventArgs e)
|
|
{
|
|
if (validationMessageStore == null || editContext == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (e.FieldIdentifier.FieldName == nameof(CatalogEditModel.UpdateProcedure))
|
|
{
|
|
validationMessageStore.Clear();
|
|
editContext.NotifyValidationStateChanged();
|
|
}
|
|
}
|
|
|
|
private void OnTitleChanged()
|
|
{
|
|
if (validationMessageStore == null || editContext == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var field = new FieldIdentifier(editContext.Model, nameof(CatalogEditModel.CatTitle));
|
|
validationMessageStore.Clear(field);
|
|
editContext.NotifyValidationStateChanged();
|
|
}
|
|
|
|
private void OnCustomizeEditModel(GridCustomizeEditModelEventArgs e)
|
|
{
|
|
if (e.IsNew)
|
|
{
|
|
e.EditModel = new CatalogEditModel();
|
|
return;
|
|
}
|
|
|
|
var item = (CatalogReadDto)e.DataItem;
|
|
e.EditModel = new CatalogEditModel
|
|
{
|
|
Guid = item.Guid,
|
|
CatTitle = item.CatTitle,
|
|
CatString = item.CatString,
|
|
UpdateProcedure = 0,
|
|
OriginalCatTitle = item.CatTitle
|
|
};
|
|
}
|
|
|
|
private async Task LoadCatalogs()
|
|
{
|
|
isLoading = true;
|
|
errorMessage = null;
|
|
try
|
|
{
|
|
items = await Api.GetAllAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
errorMessage = $"Kataloge konnten nicht geladen werden: {ex.Message}";
|
|
}
|
|
finally
|
|
{
|
|
isLoading = false;
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
private async Task OnEditModelSaving(GridEditModelSavingEventArgs e)
|
|
{
|
|
errorMessage = null;
|
|
infoMessage = null;
|
|
|
|
validationMessageStore?.Clear();
|
|
editContext?.NotifyValidationStateChanged();
|
|
|
|
var editModel = (CatalogEditModel)e.EditModel;
|
|
if (!ValidateEditModel(editModel, e.IsNew))
|
|
{
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
var dto = new CatalogWriteDto
|
|
{
|
|
CatTitle = editModel.CatTitle,
|
|
CatString = editModel.CatString,
|
|
UpdateProcedure = editModel.UpdateProcedure
|
|
};
|
|
|
|
try
|
|
{
|
|
if (e.IsNew)
|
|
{
|
|
var created = await Api.CreateAsync(dto);
|
|
if (!created.Success || created.Value == null)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(created.Error))
|
|
{
|
|
AddValidationError(editModel, nameof(CatalogEditModel.CatTitle), created.Error);
|
|
}
|
|
else
|
|
{
|
|
errorMessage = "Anlegen fehlgeschlagen.";
|
|
}
|
|
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
infoMessage = "Katalog angelegt.";
|
|
}
|
|
else
|
|
{
|
|
var updated = await Api.UpdateAsync(editModel.Guid, dto);
|
|
if (!updated.Success)
|
|
{
|
|
errorMessage = updated.Error ?? "Aktualisierung fehlgeschlagen.";
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
infoMessage = "Katalog aktualisiert.";
|
|
}
|
|
|
|
await LoadCatalogs();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
errorMessage = $"Fehler beim Speichern: {ex.Message}";
|
|
e.Cancel = true;
|
|
}
|
|
}
|
|
|
|
private void AddValidationError(CatalogEditModel editModel, string fieldName, string message)
|
|
{
|
|
if (editContext == null || validationMessageStore == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var field = new FieldIdentifier(editModel, fieldName);
|
|
validationMessageStore.Add(field, message);
|
|
editContext.NotifyValidationStateChanged();
|
|
}
|
|
|
|
private bool ValidateEditModel(CatalogEditModel editModel, bool isNew)
|
|
{
|
|
if (isNew)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (editModel.UpdateProcedure == 0 &&
|
|
!string.Equals(editModel.CatTitle, editModel.OriginalCatTitle, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
AddValidationError(editModel, nameof(CatalogEditModel.CatTitle), "Titel kann nicht geändert werden.");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private async Task OnDataItemDeleting(GridDataItemDeletingEventArgs e)
|
|
{
|
|
errorMessage = null;
|
|
infoMessage = null;
|
|
|
|
var item = (CatalogReadDto)e.DataItem;
|
|
|
|
try
|
|
{
|
|
var deleted = await Api.DeleteAsync(item.Guid);
|
|
if (!deleted.Success)
|
|
{
|
|
errorMessage = deleted.Error ?? "Löschen fehlgeschlagen.";
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
infoMessage = "Katalog gelöscht.";
|
|
await LoadCatalogs();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
errorMessage = $"Fehler beim Löschen: {ex.Message}";
|
|
e.Cancel = true;
|
|
}
|
|
}
|
|
|
|
private sealed class CatalogEditModel
|
|
{
|
|
public int Guid { get; set; }
|
|
public string CatTitle { get; set; } = string.Empty;
|
|
public string CatString { get; set; } = string.Empty;
|
|
public int UpdateProcedure { get; set; }
|
|
public string OriginalCatTitle { get; set; } = string.Empty;
|
|
}
|
|
|
|
private sealed class ProcedureOption
|
|
{
|
|
public int Value { get; set; }
|
|
public string Text { get; set; } = string.Empty;
|
|
}
|
|
}
|