Refactor grids to use DxGrid popup editing
Modernize CatalogsGrid.razor and MassDataGrid.razor to use DevExpress DxGrid's built-in popup editing with EditFormTemplate. Remove custom EditForm panels and manual editing state logic. Move CRUD operations and validation to grid event handlers. Add field-level validation and error display for catalogs. Update grid columns, add command columns, and set audit fields to read-only. Only editing is allowed in MassDataGrid; deletion is disabled. Streamline code and UI for improved maintainability and user experience.
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
@inject CatalogApiClient Api
|
@inject CatalogApiClient Api
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -44,6 +45,9 @@
|
|||||||
background-position: right 0.5rem center;
|
background-position: right 0.5rem center;
|
||||||
background-size: 0.9rem;
|
background-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
.catalog-edit-popup {
|
||||||
|
min-width: 720px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
||||||
@@ -55,41 +59,6 @@ else if (!string.IsNullOrWhiteSpace(infoMessage))
|
|||||||
<div class="alert alert-success" role="alert">@infoMessage</div>
|
<div class="alert alert-success" role="alert">@infoMessage</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Primary" Click="@StartCreate">Neuen Eintrag anlegen</DxButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (showForm)
|
|
||||||
{
|
|
||||||
<div class="action-panel">
|
|
||||||
<EditForm Model="formModel" OnValidSubmit="HandleSubmit" Context="editCtx">
|
|
||||||
<DxFormLayout ColCount="2">
|
|
||||||
<DxFormLayoutItem Caption="Titel" Context="itemCtx">
|
|
||||||
<DxTextBox @bind-Text="formModel.CatTitle" Enabled="@(isEditing ? formModel.UpdateProcedure != 0 : true)" />
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
<DxFormLayoutItem Caption="Kennung" Context="itemCtx">
|
|
||||||
<DxTextBox @bind-Text="formModel.CatString" />
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
@if (isEditing)
|
|
||||||
{
|
|
||||||
<DxFormLayoutItem Caption="Update-Prozedur" Context="itemCtx">
|
|
||||||
<DxComboBox Data="@procedureOptions"
|
|
||||||
TextFieldName="Text"
|
|
||||||
ValueFieldName="Value"
|
|
||||||
@bind-Value="formModel.UpdateProcedure" />
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
}
|
|
||||||
<DxFormLayoutItem Caption=" " Context="itemCtx">
|
|
||||||
<DxStack Orientation="Orientation.Horizontal" Spacing="8">
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Success" ButtonType="ButtonType.Submit" SubmitFormOnClick="true" Context="btnCtx">@((isEditing ? "Speichern" : "Anlegen"))</DxButton>
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Secondary" Click="@CancelEdit" Context="btnCtx">Abbrechen</DxButton>
|
|
||||||
</DxStack>
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
</DxFormLayout>
|
|
||||||
</EditForm>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (isLoading)
|
@if (isLoading)
|
||||||
{
|
{
|
||||||
<p><em>Lade Daten...</em></p>
|
<p><em>Lade Daten...</em></p>
|
||||||
@@ -101,68 +70,61 @@ else if (items.Count == 0)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="grid-section">
|
<div class="grid-section">
|
||||||
<DxGrid Data="@items" TItem="CatalogReadDto" KeyFieldName="@nameof(CatalogReadDto.Guid)" ShowFilterRow="true" PageSize="10" CssClass="mb-4 catalog-grid">
|
<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>
|
<Columns>
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.Guid)" Caption="Id" Width="140px" SortIndex="0" SortOrder="GridColumnSortOrder.Ascending">
|
<DxGridCommandColumn Width="120px" />
|
||||||
<FilterRowCellTemplate Context="filter">
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.Guid)" Caption="Id" Width="140px" SortIndex="0" SortOrder="GridColumnSortOrder.Ascending" />
|
||||||
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.CatTitle)" Caption="Titel" />
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.CatString)" Caption="String" />
|
||||||
CssClass="filter-search-input" />
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.AddedWho)" Caption="Angelegt von" ReadOnly="true" />
|
||||||
</FilterRowCellTemplate>
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.AddedWhen)" Caption="Angelegt am" ReadOnly="true" />
|
||||||
</DxGridDataColumn>
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.ChangedWho)" Caption="Geändert von" ReadOnly="true" />
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.CatTitle)" Caption="Titel">
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.ChangedWhen)" Caption="Geändert am" ReadOnly="true" />
|
||||||
<FilterRowCellTemplate Context="filter">
|
|
||||||
<DxTextBox Text="@(filter.FilterRowValue as string)"
|
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
|
||||||
CssClass="filter-search-input" />
|
|
||||||
</FilterRowCellTemplate>
|
|
||||||
</DxGridDataColumn>
|
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.CatString)" Caption="String">
|
|
||||||
<FilterRowCellTemplate Context="filter">
|
|
||||||
<DxTextBox Text="@(filter.FilterRowValue as string)"
|
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
|
||||||
CssClass="filter-search-input" />
|
|
||||||
</FilterRowCellTemplate>
|
|
||||||
</DxGridDataColumn>
|
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.AddedWho)" Caption="Angelegt von">
|
|
||||||
<FilterRowCellTemplate Context="filter">
|
|
||||||
<DxTextBox Text="@(filter.FilterRowValue as string)"
|
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
|
||||||
CssClass="filter-search-input" />
|
|
||||||
</FilterRowCellTemplate>
|
|
||||||
</DxGridDataColumn>
|
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.AddedWhen)" Caption="Angelegt am" />
|
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.ChangedWho)" Caption="Geändert von">
|
|
||||||
<FilterRowCellTemplate Context="filter">
|
|
||||||
<DxTextBox Text="@(filter.FilterRowValue as string)"
|
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
|
||||||
CssClass="filter-search-input" />
|
|
||||||
</FilterRowCellTemplate>
|
|
||||||
</DxGridDataColumn>
|
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.ChangedWhen)" Caption="Geändert am" />
|
|
||||||
<DxGridDataColumn Caption="" Width="220px" AllowSort="false">
|
|
||||||
<CellDisplayTemplate Context="cell">
|
|
||||||
@{ var item = (CatalogReadDto)cell.DataItem; }
|
|
||||||
<div style="white-space: nowrap;">
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Secondary" Size="ButtonSize.Small" Click="@(() => StartEdit(item))">Bearbeiten</DxButton>
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Danger" Size="ButtonSize.Small" Click="@(() => DeleteCatalog(item.Guid))">Löschen</DxButton>
|
|
||||||
</div>
|
|
||||||
</CellDisplayTemplate>
|
|
||||||
</DxGridDataColumn>
|
|
||||||
</Columns>
|
</Columns>
|
||||||
|
<EditFormTemplate Context="editFormContext">
|
||||||
|
@{ SetEditContext(editFormContext.EditContext); var editModel = (CatalogEditModel)editFormContext.EditModel; }
|
||||||
|
<DxFormLayout ColCount="2">
|
||||||
|
<DxFormLayoutItem Caption="Titel">
|
||||||
|
<DxTextBox @bind-Text="editModel.CatTitle" 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>
|
</DxGrid>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<CatalogReadDto> items = new();
|
private List<CatalogReadDto> items = new();
|
||||||
private CatalogWriteDto formModel = new();
|
|
||||||
private int editingId;
|
|
||||||
private bool isLoading;
|
private bool isLoading;
|
||||||
private bool isEditing;
|
|
||||||
private bool showForm;
|
|
||||||
private string? errorMessage;
|
private string? errorMessage;
|
||||||
private string? infoMessage;
|
private string? infoMessage;
|
||||||
|
private EditContext? editContext;
|
||||||
|
private ValidationMessageStore? validationMessageStore;
|
||||||
|
|
||||||
private readonly List<ProcedureOption> procedureOptions = new()
|
private readonly List<ProcedureOption> procedureOptions = new()
|
||||||
{
|
{
|
||||||
@@ -175,6 +137,57 @@ else
|
|||||||
await LoadCatalogs();
|
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) ||
|
||||||
|
e.FieldIdentifier.FieldName == nameof(CatalogEditModel.CatTitle))
|
||||||
|
{
|
||||||
|
validationMessageStore.Clear();
|
||||||
|
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()
|
private async Task LoadCatalogs()
|
||||||
{
|
{
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
@@ -194,88 +207,115 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartCreate()
|
private async Task OnEditModelSaving(GridEditModelSavingEventArgs e)
|
||||||
{
|
{
|
||||||
formModel = new CatalogWriteDto();
|
|
||||||
editingId = 0;
|
|
||||||
isEditing = false;
|
|
||||||
showForm = true;
|
|
||||||
infoMessage = null;
|
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
}
|
infoMessage = null;
|
||||||
|
|
||||||
private void StartEdit(CatalogReadDto item)
|
validationMessageStore?.Clear();
|
||||||
{
|
editContext?.NotifyValidationStateChanged();
|
||||||
formModel = new CatalogWriteDto
|
|
||||||
|
var editModel = (CatalogEditModel)e.EditModel;
|
||||||
|
if (!ValidateEditModel(editModel, e.IsNew))
|
||||||
{
|
{
|
||||||
CatTitle = item.CatTitle,
|
e.Cancel = true;
|
||||||
CatString = item.CatString,
|
return;
|
||||||
UpdateProcedure = 0
|
}
|
||||||
};
|
|
||||||
editingId = item.Guid;
|
|
||||||
isEditing = true;
|
|
||||||
showForm = true;
|
|
||||||
infoMessage = null;
|
|
||||||
errorMessage = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HandleSubmit()
|
var dto = new CatalogWriteDto
|
||||||
{
|
{
|
||||||
errorMessage = null;
|
CatTitle = editModel.CatTitle,
|
||||||
infoMessage = null;
|
CatString = editModel.CatString,
|
||||||
|
UpdateProcedure = editModel.UpdateProcedure
|
||||||
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (isEditing)
|
if (e.IsNew)
|
||||||
{
|
{
|
||||||
var updated = await Api.UpdateAsync(editingId, formModel);
|
var created = await Api.CreateAsync(dto);
|
||||||
if (!updated.Success)
|
|
||||||
{
|
|
||||||
errorMessage = updated.Error ?? "Aktualisierung fehlgeschlagen.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
infoMessage = "Katalog aktualisiert.";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var created = await Api.CreateAsync(formModel);
|
|
||||||
if (!created.Success || created.Value == null)
|
if (!created.Success || created.Value == null)
|
||||||
{
|
{
|
||||||
errorMessage = created.Error ?? "Anlegen fehlgeschlagen.";
|
if (!string.IsNullOrWhiteSpace(created.Error))
|
||||||
|
{
|
||||||
|
AddValidationError(editModel, nameof(CatalogEditModel.CatTitle), created.Error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorMessage = "Anlegen fehlgeschlagen.";
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Cancel = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
infoMessage = "Katalog angelegt.";
|
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.";
|
||||||
|
}
|
||||||
|
|
||||||
showForm = false;
|
|
||||||
await LoadCatalogs();
|
await LoadCatalogs();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
errorMessage = $"Fehler beim Speichern: {ex.Message}";
|
errorMessage = $"Fehler beim Speichern: {ex.Message}";
|
||||||
|
e.Cancel = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelEdit()
|
private void AddValidationError(CatalogEditModel editModel, string fieldName, string message)
|
||||||
{
|
{
|
||||||
showForm = false;
|
if (editContext == null || validationMessageStore == null)
|
||||||
infoMessage = null;
|
{
|
||||||
errorMessage = null;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var field = new FieldIdentifier(editModel, fieldName);
|
||||||
|
validationMessageStore.Add(field, message);
|
||||||
|
editContext.NotifyValidationStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DeleteCatalog(int id)
|
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;
|
errorMessage = null;
|
||||||
infoMessage = null;
|
infoMessage = null;
|
||||||
|
|
||||||
|
var item = (CatalogReadDto)e.DataItem;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var deleted = await Api.DeleteAsync(id);
|
var deleted = await Api.DeleteAsync(item.Guid);
|
||||||
if (!deleted.Success)
|
if (!deleted.Success)
|
||||||
{
|
{
|
||||||
errorMessage = deleted.Error ?? "Löschen fehlgeschlagen.";
|
errorMessage = deleted.Error ?? "Löschen fehlgeschlagen.";
|
||||||
|
e.Cancel = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,9 +325,19 @@ else
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
errorMessage = $"Fehler beim Löschen: {ex.Message}";
|
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
|
private sealed class ProcedureOption
|
||||||
{
|
{
|
||||||
public int Value { get; set; }
|
public int Value { get; set; }
|
||||||
|
|||||||
@@ -61,38 +61,6 @@ else if (!string.IsNullOrWhiteSpace(infoMessage))
|
|||||||
<div class="alert alert-success" role="alert">@infoMessage</div>
|
<div class="alert alert-success" role="alert">@infoMessage</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Primary" Click="@StartCreate">Neuen Eintrag anlegen</DxButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (showForm)
|
|
||||||
{
|
|
||||||
<div class="action-panel">
|
|
||||||
<EditForm Model="formModel" OnValidSubmit="HandleSubmit" Context="editCtx">
|
|
||||||
<DxFormLayout ColCount="2">
|
|
||||||
<DxFormLayoutItem Caption="CustomerName" Context="itemCtx">
|
|
||||||
<DxTextBox @bind-Text="formModel.CustomerName" />
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
<DxFormLayoutItem Caption="Amount" Context="itemCtx">
|
|
||||||
<DxTextBox @bind-Text="amountText" />
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
<DxFormLayoutItem Caption="Category" Context="itemCtx">
|
|
||||||
<DxTextBox @bind-Text="formModel.Category" ReadOnly="true" />
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
<DxFormLayoutItem Caption="Status" Context="itemCtx">
|
|
||||||
<DxCheckBox @bind-Checked="formModel.StatusFlag" ReadOnly="true" />
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
<DxFormLayoutItem Caption=" " Context="itemCtx">
|
|
||||||
<DxStack Orientation="Orientation.Horizontal" Spacing="8">
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Success" ButtonType="ButtonType.Submit" SubmitFormOnClick="true" Context="btnCtx">Speichern</DxButton>
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Secondary" Click="@CancelEdit" Context="btnCtx">Abbrechen</DxButton>
|
|
||||||
</DxStack>
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
</DxFormLayout>
|
|
||||||
</EditForm>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (isLoading)
|
@if (isLoading)
|
||||||
{
|
{
|
||||||
<p><em>Lade Daten...</em></p>
|
<p><em>Lade Daten...</em></p>
|
||||||
@@ -104,9 +72,21 @@ else if (items.Count == 0)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="grid-section">
|
<div class="grid-section">
|
||||||
<DxGrid Data="@items" TItem="MassDataReadDto" KeyFieldName="@nameof(MassDataReadDto.Id)" ShowFilterRow="true" ShowGroupPanel="true" AllowColumnResize="true" PagerVisible="false" PageSize="100" CssClass="mb-3 massdata-grid">
|
<DxGrid Data="@items"
|
||||||
|
TItem="MassDataReadDto"
|
||||||
|
KeyFieldName="@nameof(MassDataReadDto.Id)"
|
||||||
|
ShowFilterRow="true"
|
||||||
|
ShowGroupPanel="true"
|
||||||
|
AllowColumnResize="true"
|
||||||
|
PagerVisible="false"
|
||||||
|
PageSize="100"
|
||||||
|
CssClass="mb-3 massdata-grid"
|
||||||
|
EditMode="GridEditMode.PopupEditForm"
|
||||||
|
EditModelSaving="OnEditModelSaving"
|
||||||
|
DataItemDeleting="OnDataItemDeleting">
|
||||||
<Columns>
|
<Columns>
|
||||||
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.Id)" Caption="Id" Width="90px">
|
<DxGridCommandColumn Width="120px" NewButtonVisible="false" />
|
||||||
|
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.Id)" Caption="Id" Width="90px" ReadOnly="true">
|
||||||
<FilterRowCellTemplate Context="filter">
|
<FilterRowCellTemplate Context="filter">
|
||||||
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
TextChanged="@(value => filter.FilterRowValue = value)"
|
||||||
@@ -127,44 +107,51 @@ else
|
|||||||
CssClass="filter-search-input" />
|
CssClass="filter-search-input" />
|
||||||
</FilterRowCellTemplate>
|
</FilterRowCellTemplate>
|
||||||
</DxGridDataColumn>
|
</DxGridDataColumn>
|
||||||
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.Category)" Caption="Category">
|
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.Category)" Caption="Category" ReadOnly="true">
|
||||||
<FilterRowCellTemplate Context="filter">
|
<FilterRowCellTemplate Context="filter">
|
||||||
<DxTextBox Text="@(filter.FilterRowValue as string)"
|
<DxTextBox Text="@(filter.FilterRowValue as string)"
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
TextChanged="@(value => filter.FilterRowValue = value)"
|
||||||
CssClass="filter-search-input" />
|
CssClass="filter-search-input" />
|
||||||
</FilterRowCellTemplate>
|
</FilterRowCellTemplate>
|
||||||
</DxGridDataColumn>
|
</DxGridDataColumn>
|
||||||
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.StatusFlag)" Caption="Status">
|
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.StatusFlag)" Caption="Status" ReadOnly="true">
|
||||||
<FilterRowCellTemplate Context="filter">
|
<FilterRowCellTemplate Context="filter">
|
||||||
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
TextChanged="@(value => filter.FilterRowValue = value)"
|
||||||
CssClass="filter-search-input" />
|
CssClass="filter-search-input" />
|
||||||
</FilterRowCellTemplate>
|
</FilterRowCellTemplate>
|
||||||
</DxGridDataColumn>
|
</DxGridDataColumn>
|
||||||
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.AddedWhen)" Caption="Added">
|
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.AddedWhen)" Caption="Added" ReadOnly="true">
|
||||||
<FilterRowCellTemplate Context="filter">
|
<FilterRowCellTemplate Context="filter">
|
||||||
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
TextChanged="@(value => filter.FilterRowValue = value)"
|
||||||
CssClass="filter-search-input" />
|
CssClass="filter-search-input" />
|
||||||
</FilterRowCellTemplate>
|
</FilterRowCellTemplate>
|
||||||
</DxGridDataColumn>
|
</DxGridDataColumn>
|
||||||
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.ChangedWhen)" Caption="Changed">
|
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.ChangedWhen)" Caption="Changed" ReadOnly="true">
|
||||||
<FilterRowCellTemplate Context="filter">
|
<FilterRowCellTemplate Context="filter">
|
||||||
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
TextChanged="@(value => filter.FilterRowValue = value)"
|
||||||
CssClass="filter-search-input" />
|
CssClass="filter-search-input" />
|
||||||
</FilterRowCellTemplate>
|
</FilterRowCellTemplate>
|
||||||
</DxGridDataColumn>
|
</DxGridDataColumn>
|
||||||
<DxGridDataColumn Caption="" Width="220px" AllowSort="false">
|
|
||||||
<CellDisplayTemplate Context="cell">
|
|
||||||
@{ var item = (MassDataReadDto)cell.DataItem; }
|
|
||||||
<div style="white-space: nowrap;">
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Secondary" Size="ButtonSize.Small" Click="@(() => StartEdit(item))">Bearbeiten</DxButton>
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Danger" Size="ButtonSize.Small" Click="@ShowDeleteNotReady">Löschen</DxButton>
|
|
||||||
</div>
|
|
||||||
</CellDisplayTemplate>
|
|
||||||
</DxGridDataColumn>
|
|
||||||
</Columns>
|
</Columns>
|
||||||
|
<EditFormTemplate Context="editFormContext">
|
||||||
|
<DxFormLayout>
|
||||||
|
<DxFormLayoutItem Caption="CustomerName">
|
||||||
|
@editFormContext.GetEditor(nameof(MassDataReadDto.CustomerName))
|
||||||
|
</DxFormLayoutItem>
|
||||||
|
<DxFormLayoutItem Caption="Amount">
|
||||||
|
@editFormContext.GetEditor(nameof(MassDataReadDto.Amount))
|
||||||
|
</DxFormLayoutItem>
|
||||||
|
<DxFormLayoutItem Caption="Category">
|
||||||
|
@editFormContext.GetEditor(nameof(MassDataReadDto.Category))
|
||||||
|
</DxFormLayoutItem>
|
||||||
|
<DxFormLayoutItem Caption="Status">
|
||||||
|
@editFormContext.GetEditor(nameof(MassDataReadDto.StatusFlag))
|
||||||
|
</DxFormLayoutItem>
|
||||||
|
</DxFormLayout>
|
||||||
|
</EditFormTemplate>
|
||||||
</DxGrid>
|
</DxGrid>
|
||||||
|
|
||||||
<div class="pager-container">
|
<div class="pager-container">
|
||||||
@@ -176,10 +163,7 @@ else
|
|||||||
@code {
|
@code {
|
||||||
private const int PageSize = 100;
|
private const int PageSize = 100;
|
||||||
private List<MassDataReadDto> items = new();
|
private List<MassDataReadDto> items = new();
|
||||||
private MassDataWriteDto formModel = new();
|
|
||||||
private string amountText = string.Empty;
|
|
||||||
private bool isLoading;
|
private bool isLoading;
|
||||||
private bool showForm;
|
|
||||||
private string? errorMessage;
|
private string? errorMessage;
|
||||||
private string? infoMessage;
|
private string? infoMessage;
|
||||||
private int pageIndex;
|
private int pageIndex;
|
||||||
@@ -219,61 +203,38 @@ else
|
|||||||
await LoadPage(index);
|
await LoadPage(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartCreate()
|
private async Task OnEditModelSaving(GridEditModelSavingEventArgs e)
|
||||||
{
|
{
|
||||||
infoMessage = "Anlegen ist aktuell noch nicht verfügbar.";
|
errorMessage = null;
|
||||||
}
|
infoMessage = null;
|
||||||
|
|
||||||
private void StartEdit(MassDataReadDto item)
|
var editModel = (MassDataReadDto)e.EditModel;
|
||||||
{
|
var dto = new MassDataWriteDto
|
||||||
formModel = new MassDataWriteDto
|
|
||||||
{
|
{
|
||||||
CustomerName = item.CustomerName,
|
CustomerName = editModel.CustomerName,
|
||||||
Amount = item.Amount,
|
Amount = editModel.Amount,
|
||||||
Category = item.Category,
|
Category = editModel.Category,
|
||||||
StatusFlag = item.StatusFlag
|
StatusFlag = editModel.StatusFlag
|
||||||
};
|
};
|
||||||
amountText = item.Amount.ToString("0.00");
|
|
||||||
showForm = true;
|
|
||||||
infoMessage = null;
|
|
||||||
errorMessage = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HandleSubmit()
|
|
||||||
{
|
|
||||||
errorMessage = null;
|
|
||||||
infoMessage = null;
|
|
||||||
|
|
||||||
if (!decimal.TryParse(amountText, out var amount))
|
|
||||||
{
|
|
||||||
errorMessage = "Amount ist ungültig.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
formModel.Amount = amount;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Api.UpsertAsync(formModel);
|
await Api.UpsertAsync(dto);
|
||||||
infoMessage = "MassData aktualisiert.";
|
infoMessage = "MassData aktualisiert.";
|
||||||
showForm = false;
|
|
||||||
await LoadPage(pageIndex);
|
await LoadPage(pageIndex);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
errorMessage = $"Fehler beim Speichern: {ex.Message}";
|
errorMessage = $"Fehler beim Speichern: {ex.Message}";
|
||||||
|
e.Cancel = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelEdit()
|
private Task OnDataItemDeleting(GridDataItemDeletingEventArgs e)
|
||||||
{
|
{
|
||||||
showForm = false;
|
|
||||||
infoMessage = null;
|
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
}
|
|
||||||
|
|
||||||
private void ShowDeleteNotReady()
|
|
||||||
{
|
|
||||||
infoMessage = "Löschen ist aktuell noch nicht verfügbar.";
|
infoMessage = "Löschen ist aktuell noch nicht verfügbar.";
|
||||||
|
e.Cancel = true;
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
@inject CatalogApiClient Api
|
@inject CatalogApiClient Api
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.action-panel { margin-bottom: 16px; }
|
.action-panel { margin-bottom: 16px; }
|
||||||
.grid-section { margin-top: 12px; }
|
.grid-section { margin-top: 12px; }
|
||||||
|
.catalog-edit-popup {
|
||||||
|
min-width: 720px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
||||||
@@ -14,41 +18,6 @@ else if (!string.IsNullOrWhiteSpace(infoMessage))
|
|||||||
<div class="alert alert-success" role="alert">@infoMessage</div>
|
<div class="alert alert-success" role="alert">@infoMessage</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Primary" Click="@StartCreate">Neuen Eintrag anlegen</DxButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (showForm)
|
|
||||||
{
|
|
||||||
<div class="action-panel">
|
|
||||||
<EditForm Model="formModel" OnValidSubmit="HandleSubmit" Context="editCtx">
|
|
||||||
<DxFormLayout ColCount="2">
|
|
||||||
<DxFormLayoutItem Caption="Titel" Context="itemCtx">
|
|
||||||
<DxTextBox @bind-Text="formModel.CatTitle" Enabled="@(isEditing ? formModel.UpdateProcedure != 0 : true)" />
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
<DxFormLayoutItem Caption="Kennung" Context="itemCtx">
|
|
||||||
<DxTextBox @bind-Text="formModel.CatString" />
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
@if (isEditing)
|
|
||||||
{
|
|
||||||
<DxFormLayoutItem Caption="Update-Prozedur" Context="itemCtx">
|
|
||||||
<DxComboBox Data="@procedureOptions"
|
|
||||||
TextFieldName="Text"
|
|
||||||
ValueFieldName="Value"
|
|
||||||
@bind-Value="formModel.UpdateProcedure" />
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
}
|
|
||||||
<DxFormLayoutItem Caption=" " Context="itemCtx">
|
|
||||||
<DxStack Orientation="Orientation.Horizontal" Spacing="8">
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Success" ButtonType="ButtonType.Submit" SubmitFormOnClick="true" Context="btnCtx">@((isEditing ? "Speichern" : "Anlegen"))</DxButton>
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Secondary" Click="@CancelEdit" Context="btnCtx">Abbrechen</DxButton>
|
|
||||||
</DxStack>
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
</DxFormLayout>
|
|
||||||
</EditForm>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (isLoading)
|
@if (isLoading)
|
||||||
{
|
{
|
||||||
<p><em>Lade Daten...</em></p>
|
<p><em>Lade Daten...</em></p>
|
||||||
@@ -60,38 +29,61 @@ else if (items.Count == 0)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="grid-section">
|
<div class="grid-section">
|
||||||
<DxGrid Data="@items" TItem="CatalogReadDto" KeyFieldName="@nameof(CatalogReadDto.Guid)" ShowFilterRow="true" PageSize="10" CssClass="mb-4 catalog-grid">
|
<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>
|
<Columns>
|
||||||
|
<DxGridCommandColumn Width="120px" />
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.Guid)" Caption="Id" Width="140px" SortIndex="0" SortOrder="GridColumnSortOrder.Ascending" />
|
<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.CatTitle)" Caption="Titel" />
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.CatString)" Caption="String" />
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.CatString)" Caption="String" />
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.AddedWho)" Caption="Angelegt von" />
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.AddedWho)" Caption="Angelegt von" ReadOnly="true" />
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.AddedWhen)" Caption="Angelegt am" />
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.AddedWhen)" Caption="Angelegt am" ReadOnly="true" />
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.ChangedWho)" Caption="Geändert von" />
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.ChangedWho)" Caption="Geändert von" ReadOnly="true" />
|
||||||
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.ChangedWhen)" Caption="Geändert am" />
|
<DxGridDataColumn FieldName="@nameof(CatalogReadDto.ChangedWhen)" Caption="Geändert am" ReadOnly="true" />
|
||||||
<DxGridDataColumn Caption="" Width="220px" AllowSort="false">
|
|
||||||
<CellDisplayTemplate Context="cell">
|
|
||||||
@{ var item = (CatalogReadDto)cell.DataItem; }
|
|
||||||
<div style="white-space: nowrap;">
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Secondary" Size="ButtonSize.Small" Click="@(() => StartEdit(item))">Bearbeiten</DxButton>
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Danger" Size="ButtonSize.Small" Click="@(() => DeleteCatalog(item.Guid))">Löschen</DxButton>
|
|
||||||
</div>
|
|
||||||
</CellDisplayTemplate>
|
|
||||||
</DxGridDataColumn>
|
|
||||||
</Columns>
|
</Columns>
|
||||||
|
<EditFormTemplate Context="editFormContext">
|
||||||
|
@{ SetEditContext(editFormContext.EditContext); var editModel = (CatalogEditModel)editFormContext.EditModel; }
|
||||||
|
<DxFormLayout ColCount="2">
|
||||||
|
<DxFormLayoutItem Caption="Titel">
|
||||||
|
<DxTextBox @bind-Text="editModel.CatTitle" 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>
|
</DxGrid>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private List<CatalogReadDto> items = new();
|
private List<CatalogReadDto> items = new();
|
||||||
private CatalogWriteDto formModel = new();
|
|
||||||
private int editingId;
|
|
||||||
private bool isLoading;
|
private bool isLoading;
|
||||||
private bool isEditing;
|
|
||||||
private bool showForm;
|
|
||||||
private string? errorMessage;
|
private string? errorMessage;
|
||||||
private string? infoMessage;
|
private string? infoMessage;
|
||||||
|
private EditContext? editContext;
|
||||||
|
private ValidationMessageStore? validationMessageStore;
|
||||||
|
|
||||||
private readonly List<ProcedureOption> procedureOptions = new()
|
private readonly List<ProcedureOption> procedureOptions = new()
|
||||||
{
|
{
|
||||||
@@ -104,6 +96,57 @@ else
|
|||||||
await LoadCatalogs();
|
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) ||
|
||||||
|
e.FieldIdentifier.FieldName == nameof(CatalogEditModel.CatTitle))
|
||||||
|
{
|
||||||
|
validationMessageStore.Clear();
|
||||||
|
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()
|
private async Task LoadCatalogs()
|
||||||
{
|
{
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
@@ -123,88 +166,115 @@ else
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartCreate()
|
private async Task OnEditModelSaving(GridEditModelSavingEventArgs e)
|
||||||
{
|
{
|
||||||
formModel = new CatalogWriteDto();
|
|
||||||
editingId = 0;
|
|
||||||
isEditing = false;
|
|
||||||
showForm = true;
|
|
||||||
infoMessage = null;
|
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
}
|
infoMessage = null;
|
||||||
|
|
||||||
private void StartEdit(CatalogReadDto item)
|
validationMessageStore?.Clear();
|
||||||
{
|
editContext?.NotifyValidationStateChanged();
|
||||||
formModel = new CatalogWriteDto
|
|
||||||
|
var editModel = (CatalogEditModel)e.EditModel;
|
||||||
|
if (!ValidateEditModel(editModel, e.IsNew))
|
||||||
{
|
{
|
||||||
CatTitle = item.CatTitle,
|
e.Cancel = true;
|
||||||
CatString = item.CatString,
|
return;
|
||||||
UpdateProcedure = 0
|
}
|
||||||
};
|
|
||||||
editingId = item.Guid;
|
|
||||||
isEditing = true;
|
|
||||||
showForm = true;
|
|
||||||
infoMessage = null;
|
|
||||||
errorMessage = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HandleSubmit()
|
var dto = new CatalogWriteDto
|
||||||
{
|
{
|
||||||
errorMessage = null;
|
CatTitle = editModel.CatTitle,
|
||||||
infoMessage = null;
|
CatString = editModel.CatString,
|
||||||
|
UpdateProcedure = editModel.UpdateProcedure
|
||||||
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (isEditing)
|
if (e.IsNew)
|
||||||
{
|
{
|
||||||
var updated = await Api.UpdateAsync(editingId, formModel);
|
var created = await Api.CreateAsync(dto);
|
||||||
if (!updated.Success)
|
|
||||||
{
|
|
||||||
errorMessage = updated.Error ?? "Aktualisierung fehlgeschlagen.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
infoMessage = "Katalog aktualisiert.";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var created = await Api.CreateAsync(formModel);
|
|
||||||
if (!created.Success || created.Value == null)
|
if (!created.Success || created.Value == null)
|
||||||
{
|
{
|
||||||
errorMessage = created.Error ?? "Anlegen fehlgeschlagen.";
|
if (!string.IsNullOrWhiteSpace(created.Error))
|
||||||
|
{
|
||||||
|
AddValidationError(editModel, nameof(CatalogEditModel.CatTitle), created.Error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorMessage = "Anlegen fehlgeschlagen.";
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Cancel = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
infoMessage = "Katalog angelegt.";
|
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.";
|
||||||
|
}
|
||||||
|
|
||||||
showForm = false;
|
|
||||||
await LoadCatalogs();
|
await LoadCatalogs();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
errorMessage = $"Fehler beim Speichern: {ex.Message}";
|
errorMessage = $"Fehler beim Speichern: {ex.Message}";
|
||||||
|
e.Cancel = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelEdit()
|
private void AddValidationError(CatalogEditModel editModel, string fieldName, string message)
|
||||||
{
|
{
|
||||||
showForm = false;
|
if (editContext == null || validationMessageStore == null)
|
||||||
infoMessage = null;
|
{
|
||||||
errorMessage = null;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var field = new FieldIdentifier(editModel, fieldName);
|
||||||
|
validationMessageStore.Add(field, message);
|
||||||
|
editContext.NotifyValidationStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DeleteCatalog(int id)
|
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;
|
errorMessage = null;
|
||||||
infoMessage = null;
|
infoMessage = null;
|
||||||
|
|
||||||
|
var item = (CatalogReadDto)e.DataItem;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var deleted = await Api.DeleteAsync(id);
|
var deleted = await Api.DeleteAsync(item.Guid);
|
||||||
if (!deleted.Success)
|
if (!deleted.Success)
|
||||||
{
|
{
|
||||||
errorMessage = deleted.Error ?? "Löschen fehlgeschlagen.";
|
errorMessage = deleted.Error ?? "Löschen fehlgeschlagen.";
|
||||||
|
e.Cancel = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,9 +284,19 @@ else
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
errorMessage = $"Fehler beim Löschen: {ex.Message}";
|
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
|
private sealed class ProcedureOption
|
||||||
{
|
{
|
||||||
public int Value { get; set; }
|
public int Value { get; set; }
|
||||||
|
|||||||
@@ -61,38 +61,6 @@ else if (!string.IsNullOrWhiteSpace(infoMessage))
|
|||||||
<div class="alert alert-success" role="alert">@infoMessage</div>
|
<div class="alert alert-success" role="alert">@infoMessage</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Primary" Click="@StartCreate">Neuen Eintrag anlegen</DxButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@if (showForm)
|
|
||||||
{
|
|
||||||
<div class="action-panel">
|
|
||||||
<EditForm Model="formModel" OnValidSubmit="HandleSubmit" Context="editCtx">
|
|
||||||
<DxFormLayout ColCount="2">
|
|
||||||
<DxFormLayoutItem Caption="CustomerName" Context="itemCtx">
|
|
||||||
<DxTextBox @bind-Text="formModel.CustomerName" />
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
<DxFormLayoutItem Caption="Amount" Context="itemCtx">
|
|
||||||
<DxTextBox @bind-Text="amountText" />
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
<DxFormLayoutItem Caption="Category" Context="itemCtx">
|
|
||||||
<DxTextBox @bind-Text="formModel.Category" ReadOnly="true" />
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
<DxFormLayoutItem Caption="Status" Context="itemCtx">
|
|
||||||
<DxCheckBox @bind-Checked="formModel.StatusFlag" ReadOnly="true" />
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
<DxFormLayoutItem Caption=" " Context="itemCtx">
|
|
||||||
<DxStack Orientation="Orientation.Horizontal" Spacing="8">
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Success" ButtonType="ButtonType.Submit" SubmitFormOnClick="true" Context="btnCtx">Speichern</DxButton>
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Secondary" Click="@CancelEdit" Context="btnCtx">Abbrechen</DxButton>
|
|
||||||
</DxStack>
|
|
||||||
</DxFormLayoutItem>
|
|
||||||
</DxFormLayout>
|
|
||||||
</EditForm>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (isLoading)
|
@if (isLoading)
|
||||||
{
|
{
|
||||||
<p><em>Lade Daten...</em></p>
|
<p><em>Lade Daten...</em></p>
|
||||||
@@ -104,9 +72,21 @@ else if (items.Count == 0)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="grid-section">
|
<div class="grid-section">
|
||||||
<DxGrid Data="@items" TItem="MassDataReadDto" KeyFieldName="@nameof(MassDataReadDto.Id)" ShowFilterRow="true" ShowGroupPanel="true" AllowColumnResize="true" PagerVisible="false" PageSize="100" CssClass="mb-3 massdata-grid">
|
<DxGrid Data="@items"
|
||||||
|
TItem="MassDataReadDto"
|
||||||
|
KeyFieldName="@nameof(MassDataReadDto.Id)"
|
||||||
|
ShowFilterRow="true"
|
||||||
|
ShowGroupPanel="true"
|
||||||
|
AllowColumnResize="true"
|
||||||
|
PagerVisible="false"
|
||||||
|
PageSize="100"
|
||||||
|
CssClass="mb-3 massdata-grid"
|
||||||
|
EditMode="GridEditMode.PopupEditForm"
|
||||||
|
EditModelSaving="OnEditModelSaving"
|
||||||
|
DataItemDeleting="OnDataItemDeleting">
|
||||||
<Columns>
|
<Columns>
|
||||||
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.Id)" Caption="Id" Width="90px">
|
<DxGridCommandColumn Width="120px" NewButtonVisible="false" />
|
||||||
|
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.Id)" Caption="Id" Width="90px" ReadOnly="true">
|
||||||
<FilterRowCellTemplate Context="filter">
|
<FilterRowCellTemplate Context="filter">
|
||||||
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
TextChanged="@(value => filter.FilterRowValue = value)"
|
||||||
@@ -127,44 +107,51 @@ else
|
|||||||
CssClass="filter-search-input" />
|
CssClass="filter-search-input" />
|
||||||
</FilterRowCellTemplate>
|
</FilterRowCellTemplate>
|
||||||
</DxGridDataColumn>
|
</DxGridDataColumn>
|
||||||
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.Category)" Caption="Category">
|
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.Category)" Caption="Category" ReadOnly="true">
|
||||||
<FilterRowCellTemplate Context="filter">
|
<FilterRowCellTemplate Context="filter">
|
||||||
<DxTextBox Text="@(filter.FilterRowValue as string)"
|
<DxTextBox Text="@(filter.FilterRowValue as string)"
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
TextChanged="@(value => filter.FilterRowValue = value)"
|
||||||
CssClass="filter-search-input" />
|
CssClass="filter-search-input" />
|
||||||
</FilterRowCellTemplate>
|
</FilterRowCellTemplate>
|
||||||
</DxGridDataColumn>
|
</DxGridDataColumn>
|
||||||
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.StatusFlag)" Caption="Status">
|
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.StatusFlag)" Caption="Status" ReadOnly="true">
|
||||||
<FilterRowCellTemplate Context="filter">
|
<FilterRowCellTemplate Context="filter">
|
||||||
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
TextChanged="@(value => filter.FilterRowValue = value)"
|
||||||
CssClass="filter-search-input" />
|
CssClass="filter-search-input" />
|
||||||
</FilterRowCellTemplate>
|
</FilterRowCellTemplate>
|
||||||
</DxGridDataColumn>
|
</DxGridDataColumn>
|
||||||
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.AddedWhen)" Caption="Added">
|
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.AddedWhen)" Caption="Added" ReadOnly="true">
|
||||||
<FilterRowCellTemplate Context="filter">
|
<FilterRowCellTemplate Context="filter">
|
||||||
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
TextChanged="@(value => filter.FilterRowValue = value)"
|
||||||
CssClass="filter-search-input" />
|
CssClass="filter-search-input" />
|
||||||
</FilterRowCellTemplate>
|
</FilterRowCellTemplate>
|
||||||
</DxGridDataColumn>
|
</DxGridDataColumn>
|
||||||
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.ChangedWhen)" Caption="Changed">
|
<DxGridDataColumn FieldName="@nameof(MassDataReadDto.ChangedWhen)" Caption="Changed" ReadOnly="true">
|
||||||
<FilterRowCellTemplate Context="filter">
|
<FilterRowCellTemplate Context="filter">
|
||||||
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
<DxTextBox Text="@(filter.FilterRowValue?.ToString())"
|
||||||
TextChanged="@(value => filter.FilterRowValue = value)"
|
TextChanged="@(value => filter.FilterRowValue = value)"
|
||||||
CssClass="filter-search-input" />
|
CssClass="filter-search-input" />
|
||||||
</FilterRowCellTemplate>
|
</FilterRowCellTemplate>
|
||||||
</DxGridDataColumn>
|
</DxGridDataColumn>
|
||||||
<DxGridDataColumn Caption="" Width="220px" AllowSort="false">
|
|
||||||
<CellDisplayTemplate Context="cell">
|
|
||||||
@{ var item = (MassDataReadDto)cell.DataItem; }
|
|
||||||
<div style="white-space: nowrap;">
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Secondary" Size="ButtonSize.Small" Click="@(() => StartEdit(item))">Bearbeiten</DxButton>
|
|
||||||
<DxButton RenderStyle="ButtonRenderStyle.Danger" Size="ButtonSize.Small" Click="@ShowDeleteNotReady">Löschen</DxButton>
|
|
||||||
</div>
|
|
||||||
</CellDisplayTemplate>
|
|
||||||
</DxGridDataColumn>
|
|
||||||
</Columns>
|
</Columns>
|
||||||
|
<EditFormTemplate Context="editFormContext">
|
||||||
|
<DxFormLayout>
|
||||||
|
<DxFormLayoutItem Caption="CustomerName">
|
||||||
|
@editFormContext.GetEditor(nameof(MassDataReadDto.CustomerName))
|
||||||
|
</DxFormLayoutItem>
|
||||||
|
<DxFormLayoutItem Caption="Amount">
|
||||||
|
@editFormContext.GetEditor(nameof(MassDataReadDto.Amount))
|
||||||
|
</DxFormLayoutItem>
|
||||||
|
<DxFormLayoutItem Caption="Category">
|
||||||
|
@editFormContext.GetEditor(nameof(MassDataReadDto.Category))
|
||||||
|
</DxFormLayoutItem>
|
||||||
|
<DxFormLayoutItem Caption="Status">
|
||||||
|
@editFormContext.GetEditor(nameof(MassDataReadDto.StatusFlag))
|
||||||
|
</DxFormLayoutItem>
|
||||||
|
</DxFormLayout>
|
||||||
|
</EditFormTemplate>
|
||||||
</DxGrid>
|
</DxGrid>
|
||||||
|
|
||||||
<div class="pager-container">
|
<div class="pager-container">
|
||||||
@@ -176,10 +163,7 @@ else
|
|||||||
@code {
|
@code {
|
||||||
private const int PageSize = 100;
|
private const int PageSize = 100;
|
||||||
private List<MassDataReadDto> items = new();
|
private List<MassDataReadDto> items = new();
|
||||||
private MassDataWriteDto formModel = new();
|
|
||||||
private string amountText = string.Empty;
|
|
||||||
private bool isLoading;
|
private bool isLoading;
|
||||||
private bool showForm;
|
|
||||||
private string? errorMessage;
|
private string? errorMessage;
|
||||||
private string? infoMessage;
|
private string? infoMessage;
|
||||||
private int pageIndex;
|
private int pageIndex;
|
||||||
@@ -219,61 +203,38 @@ else
|
|||||||
await LoadPage(index);
|
await LoadPage(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartCreate()
|
private async Task OnEditModelSaving(GridEditModelSavingEventArgs e)
|
||||||
{
|
{
|
||||||
infoMessage = "Anlegen ist aktuell noch nicht verfügbar.";
|
errorMessage = null;
|
||||||
}
|
infoMessage = null;
|
||||||
|
|
||||||
private void StartEdit(MassDataReadDto item)
|
var editModel = (MassDataReadDto)e.EditModel;
|
||||||
{
|
var dto = new MassDataWriteDto
|
||||||
formModel = new MassDataWriteDto
|
|
||||||
{
|
{
|
||||||
CustomerName = item.CustomerName,
|
CustomerName = editModel.CustomerName,
|
||||||
Amount = item.Amount,
|
Amount = editModel.Amount,
|
||||||
Category = item.Category,
|
Category = editModel.Category,
|
||||||
StatusFlag = item.StatusFlag
|
StatusFlag = editModel.StatusFlag
|
||||||
};
|
};
|
||||||
amountText = item.Amount.ToString("0.00");
|
|
||||||
showForm = true;
|
|
||||||
infoMessage = null;
|
|
||||||
errorMessage = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HandleSubmit()
|
|
||||||
{
|
|
||||||
errorMessage = null;
|
|
||||||
infoMessage = null;
|
|
||||||
|
|
||||||
if (!decimal.TryParse(amountText, out var amount))
|
|
||||||
{
|
|
||||||
errorMessage = "Amount ist ungültig.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
formModel.Amount = amount;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Api.UpsertAsync(formModel);
|
await Api.UpsertAsync(dto);
|
||||||
infoMessage = "MassData aktualisiert.";
|
infoMessage = "MassData aktualisiert.";
|
||||||
showForm = false;
|
|
||||||
await LoadPage(pageIndex);
|
await LoadPage(pageIndex);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
errorMessage = $"Fehler beim Speichern: {ex.Message}";
|
errorMessage = $"Fehler beim Speichern: {ex.Message}";
|
||||||
|
e.Cancel = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelEdit()
|
private Task OnDataItemDeleting(GridDataItemDeletingEventArgs e)
|
||||||
{
|
{
|
||||||
showForm = false;
|
|
||||||
infoMessage = null;
|
|
||||||
errorMessage = null;
|
errorMessage = null;
|
||||||
}
|
|
||||||
|
|
||||||
private void ShowDeleteNotReady()
|
|
||||||
{
|
|
||||||
infoMessage = "Löschen ist aktuell noch nicht verfügbar.";
|
infoMessage = "Löschen ist aktuell noch nicht verfügbar.";
|
||||||
|
e.Cancel = true;
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user