From dc74d214263c443f82572019306d7e165211b55e Mon Sep 17 00:00:00 2001 From: OlgunR Date: Wed, 25 Mar 2026 17:04:19 +0100 Subject: [PATCH] Refactor: centralize grid band layout logic in service Introduce BandLayoutService and shared models to manage grid band layouts across components. Refactor CatalogsGrid and MassDataGrid to use the new service, removing duplicated layout logic. Update _Imports.razor and register the service in Program.cs for improved maintainability and code reuse. --- .../Components/CatalogsGrid.razor | 591 ++++++------------ .../Components/MassDataGrid.razor | 358 +++-------- .../Components/_Imports.razor | 6 +- .../Models/Grid/BandLayoutModels.cs | 43 ++ DbFirst.BlazorWebApp/Program.cs | 1 + .../Services/BandLayoutService.cs | 103 +++ 6 files changed, 422 insertions(+), 680 deletions(-) create mode 100644 DbFirst.BlazorWebApp/Models/Grid/BandLayoutModels.cs create mode 100644 DbFirst.BlazorWebApp/Services/BandLayoutService.cs diff --git a/DbFirst.BlazorWebApp/Components/CatalogsGrid.razor b/DbFirst.BlazorWebApp/Components/CatalogsGrid.razor index 0400eff..c425798 100644 --- a/DbFirst.BlazorWebApp/Components/CatalogsGrid.razor +++ b/DbFirst.BlazorWebApp/Components/CatalogsGrid.razor @@ -1,6 +1,5 @@ @inject CatalogApiClient Api -@inject LayoutApiClient LayoutApi -@inject IJSRuntime JsRuntime +@inject BandLayoutService BandLayoutService @if (!string.IsNullOrWhiteSpace(errorMessage)) { @@ -101,7 +100,9 @@ else @RenderColumns() - @{ SetEditContext(editFormContext.EditContext); var editModel = (CatalogEditModel)editFormContext.EditModel; SetPopupHeaderText(editModel.IsNew); } + @{ + SetEditContext(editFormContext.EditContext); var editModel = (CatalogEditModel)editFormContext.EditModel; SetPopupHeaderText(editModel.IsNew); + } @@ -143,13 +144,13 @@ else private string popupHeaderText = "Edit"; private const string LayoutType = "GRID_BANDS"; private const string LayoutKey = "CatalogsGrid"; - private const string LayoutUserStorageKey = "layoutUser"; private string? layoutUser; private BandLayout bandLayout = new(); private Dictionary columnBandAssignments = new(); private List bandOptions = new(); private Dictionary columnLookup = new(); - private readonly JsonSerializerOptions jsonOptions = new(JsonSerializerDefaults.Web); + private bool gridLayoutApplied; + private List columnDefinitions = new() { new() { FieldName = nameof(CatalogReadDto.Guid), Caption = "Id", Width = "140px", FilterType = ColumnFilterType.Text }, @@ -168,30 +169,32 @@ else }; private bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser); - private bool gridLayoutApplied; - private DevExpress.Blazor.SizeMode _sizeMode = DevExpress.Blazor.SizeMode.Medium; - private static readonly List _sizeModes = - Enum.GetValues().ToList(); + private SizeMode _sizeMode = SizeMode.Medium; + private static readonly List _sizeModes = Enum.GetValues().ToList(); - private string FormatSizeText(DevExpress.Blazor.SizeMode size) => size switch + private string FormatSizeText(SizeMode size) => size switch { - DevExpress.Blazor.SizeMode.Small => "Klein", - DevExpress.Blazor.SizeMode.Medium => "Mittel", - DevExpress.Blazor.SizeMode.Large => "Groß", + SizeMode.Small => "Klein", + SizeMode.Medium => "Mittel", + SizeMode.Large => "Groß", _ => size.ToString() }; private void OnSizeChange(DropDownButtonItemClickEventArgs args) { - _sizeMode = Enum.Parse(args.ItemInfo.Id); + _sizeMode = Enum.Parse(args.ItemInfo.Id); } protected override async Task OnInitializedAsync() { - columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase); - await EnsureLayoutUserAsync(); - await LoadBandLayoutAsync(); + columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase); + layoutUser = await BandLayoutService.EnsureLayoutUserAsync(); + bandLayout = await BandLayoutService.LoadBandLayoutAsync(LayoutType, LayoutKey, layoutUser, columnLookup); + columnBandAssignments = BandLayoutService.BuildAssignmentsFromLayout(bandLayout); + ApplyColumnLayoutFromStorage(); + _sizeMode = bandLayout.SizeMode; + UpdateBandOptions(); await LoadCatalogs(); } @@ -205,71 +208,6 @@ else } } - 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(); - return; - } - - if (e.FieldIdentifier.FieldName == nameof(CatalogEditModel.CatTitle)) - { - var field = new FieldIdentifier(editContext.Model, nameof(CatalogEditModel.CatTitle)); - validationMessageStore.Clear(field); - editContext.NotifyValidationStateChanged(); - } - } - - private void SetPopupHeaderText(bool isNew) - { - popupHeaderText = isNew ? "Neu" : "Edit"; - } - - private void OnCustomizeEditModel(GridCustomizeEditModelEventArgs e) - { - popupHeaderText = e.IsNew ? "Neu" : "Edit"; - if (e.IsNew) - { - e.EditModel = new CatalogEditModel { IsNew = true }; - return; - } - - var item = (CatalogReadDto)e.DataItem; - e.EditModel = new CatalogEditModel - { - Guid = item.Guid, - CatTitle = item.CatTitle, - CatString = item.CatString, - UpdateProcedure = 0, - OriginalCatTitle = item.CatTitle, - IsNew = false - }; - } - private async Task LoadCatalogs() { isLoading = true; @@ -290,159 +228,15 @@ else } } - 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."; - focusedRowKey = created.Value.Guid; - } - else - { - var updated = await Api.UpdateAsync(editModel.Guid, dto); - if (!updated.Success) - { - errorMessage = updated.Error ?? "Aktualisierung fehlgeschlagen."; - e.Cancel = true; - return; - } - - infoMessage = "Katalog aktualisiert."; - focusedRowKey = editModel.Guid; - } - - 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 async Task EnsureLayoutUserAsync() - { - layoutUser = await JsRuntime.InvokeAsync("localStorage.getItem", LayoutUserStorageKey); - if (string.IsNullOrWhiteSpace(layoutUser)) - { - layoutUser = Guid.NewGuid().ToString("N"); - await JsRuntime.InvokeVoidAsync("localStorage.setItem", LayoutUserStorageKey, layoutUser); - } - } - private async Task SaveLayoutAsync() { if (string.IsNullOrWhiteSpace(layoutUser)) - { return; - } try { CaptureColumnLayoutFromGrid(); - - var layoutData = JsonSerializer.Serialize(bandLayout, jsonOptions); - await LayoutApi.UpsertAsync(new LayoutDto - { - LayoutType = LayoutType, - LayoutKey = LayoutKey, - UserName = layoutUser, - LayoutData = layoutData - }); + await BandLayoutService.SaveBandLayoutAsync(LayoutType, LayoutKey, layoutUser, bandLayout); infoMessage = "Layout gespeichert."; errorMessage = null; } @@ -452,65 +246,12 @@ else } } - private void CaptureColumnLayoutFromGrid() - { - if (gridRef == null) - { - return; - } - - var layout = gridRef.SaveLayout(); - bandLayout.GridLayout = layout; - bandLayout.SizeMode = _sizeMode; - - var orderedColumns = layout.Columns - .Where(column => !string.IsNullOrWhiteSpace(column.FieldName)) - .OrderBy(column => column.VisibleIndex) - .ToList(); - - bandLayout.ColumnOrder = orderedColumns - .Select(column => column.FieldName) - .ToList(); - - bandLayout.ColumnWidths = orderedColumns - .Where(column => !string.IsNullOrWhiteSpace(column.Width)) - .ToDictionary(column => column.FieldName, column => column.Width, StringComparer.OrdinalIgnoreCase); - } - - private async Task LoadBandLayoutAsync() - { - if (string.IsNullOrWhiteSpace(layoutUser)) - { - bandLayout = new BandLayout(); - UpdateBandOptions(); - return; - } - - var stored = await LayoutApi.GetAsync(LayoutType, LayoutKey, layoutUser); - if (stored != null && !string.IsNullOrWhiteSpace(stored.LayoutData)) - { - var parsed = JsonSerializer.Deserialize(stored.LayoutData, jsonOptions); - bandLayout = NormalizeBandLayout(parsed); - } - else - { - bandLayout = new BandLayout(); - } - - columnBandAssignments = BuildAssignmentsFromLayout(bandLayout); - ApplyColumnLayoutFromStorage(); - _sizeMode = bandLayout.SizeMode; - UpdateBandOptions(); - } - private async Task ResetLayoutAsync() { if (string.IsNullOrWhiteSpace(layoutUser)) - { return; - } - await LayoutApi.DeleteAsync(LayoutType, LayoutKey, layoutUser); + await BandLayoutService.ResetBandLayoutAsync(LayoutType, LayoutKey, layoutUser); bandLayout = new BandLayout(); columnBandAssignments.Clear(); @@ -520,49 +261,57 @@ else column.Width = null; columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase); - _sizeMode = DevExpress.Blazor.SizeMode.Medium; + _sizeMode = SizeMode.Medium; if (gridRef != null) gridRef.LoadLayout(new GridPersistentLayout()); gridLayoutApplied = false; - infoMessage = "Layout zur\u00fcckgesetzt."; + infoMessage = "Layout zurückgesetzt."; errorMessage = null; } + private void CaptureColumnLayoutFromGrid() + { + if (gridRef == null) + return; + + var layout = gridRef.SaveLayout(); + bandLayout.GridLayout = layout; + bandLayout.SizeMode = _sizeMode; + + var orderedColumns = layout.Columns + .Where(c => !string.IsNullOrWhiteSpace(c.FieldName)) + .OrderBy(c => c.VisibleIndex) + .ToList(); + + bandLayout.ColumnOrder = orderedColumns.Select(c => c.FieldName).ToList(); + bandLayout.ColumnWidths = orderedColumns + .Where(c => !string.IsNullOrWhiteSpace(c.Width)) + .ToDictionary(c => c.FieldName, c => c.Width, StringComparer.OrdinalIgnoreCase); + } + private void ApplyColumnLayoutFromStorage() { foreach (var column in columnDefinitions) { if (bandLayout.ColumnWidths.TryGetValue(column.FieldName, out var width) && !string.IsNullOrWhiteSpace(width)) - { column.Width = width; - } } - - columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase); + columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase); } private void AddBand() { - bandLayout.Bands.Add(new BandDefinition - { - Id = Guid.NewGuid().ToString("N"), - Caption = "Band" - }); + bandLayout.Bands.Add(new BandDefinition { Id = Guid.NewGuid().ToString("N"), Caption = "Band" }); UpdateBandOptions(); } private void RemoveBand(BandDefinition band) { bandLayout.Bands.Remove(band); - var removedColumns = columnBandAssignments.Where(pair => pair.Value == band.Id) - .Select(pair => pair.Key) - .ToList(); - foreach (var column in removedColumns) - { - columnBandAssignments.Remove(column); - } + foreach (var key in columnBandAssignments.Where(p => p.Value == band.Id).Select(p => p.Key).ToList()) + columnBandAssignments.Remove(key); UpdateBandOptions(); SyncBandsFromAssignments(); } @@ -576,77 +325,31 @@ else private void UpdateColumnBand(string fieldName, string? bandId) { if (string.IsNullOrWhiteSpace(bandId)) - { columnBandAssignments.Remove(fieldName); - } else - { columnBandAssignments[fieldName] = bandId; - } - SyncBandsFromAssignments(); } private string GetColumnBand(string fieldName) - { - return columnBandAssignments.TryGetValue(fieldName, out var bandId) ? bandId : string.Empty; - } + => columnBandAssignments.TryGetValue(fieldName, out var bandId) ? bandId : string.Empty; private void SyncBandsFromAssignments() { foreach (var band in bandLayout.Bands) { band.Columns = columnDefinitions - .Where(column => columnBandAssignments.TryGetValue(column.FieldName, out var bandId) && bandId == band.Id) - .Select(column => column.FieldName) + .Where(c => columnBandAssignments.TryGetValue(c.FieldName, out var id) && id == band.Id) + .Select(c => c.FieldName) .ToList(); } - StateHasChanged(); } private void UpdateBandOptions() { bandOptions = new List { new() { Id = string.Empty, Caption = "Ohne Band" } }; - bandOptions.AddRange(bandLayout.Bands.Select(band => new BandOption { Id = band.Id, Caption = band.Caption })); - } - - private BandLayout NormalizeBandLayout(BandLayout? layout) - { - layout ??= new BandLayout(); - layout.Bands ??= new List(); - layout.ColumnOrder ??= new List(); - layout.ColumnWidths ??= new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var band in layout.Bands) - { - if (string.IsNullOrWhiteSpace(band.Id)) - { - band.Id = Guid.NewGuid().ToString("N"); - } - - if (string.IsNullOrWhiteSpace(band.Caption)) - { - band.Caption = "Band"; - } - - band.Columns = band.Columns?.Where(columnLookup.ContainsKey).ToList() ?? new List(); - } - - return layout; - } - - private Dictionary BuildAssignmentsFromLayout(BandLayout layout) - { - var assignments = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var band in layout.Bands) - { - foreach (var column in band.Columns) - { - assignments[column] = band.Id; - } - } - - return assignments; + bandOptions.AddRange(bandLayout.Bands.Select(b => new BandOption { Id = b.Id, Caption = b.Caption })); } private RenderFragment RenderColumns() => builder => @@ -656,18 +359,13 @@ else builder.AddAttribute(seq++, "Width", "120px"); builder.CloseComponent(); - var grouped = bandLayout.Bands.SelectMany(band => band.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase); - foreach (var column in columnDefinitions.Where(column => !grouped.Contains(column.FieldName))) - { + var grouped = bandLayout.Bands.SelectMany(b => b.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase); + foreach (var column in columnDefinitions.Where(c => !grouped.Contains(c.FieldName))) BuildDataColumn(builder, ref seq, column); - } foreach (var band in bandLayout.Bands) { - if (band.Columns.Count == 0) - { - continue; - } + if (band.Columns.Count == 0) continue; builder.OpenComponent(seq++); builder.AddAttribute(seq++, "Caption", band.Caption); @@ -677,9 +375,7 @@ else foreach (var columnName in band.Columns) { if (columnLookup.TryGetValue(columnName, out var column)) - { BuildDataColumn(bandBuilder, ref bandSeq, column); - } } })); builder.CloseComponent(); @@ -692,59 +388,166 @@ else builder.AddAttribute(seq++, "FieldName", column.FieldName); builder.AddAttribute(seq++, "Caption", column.Caption); if (!string.IsNullOrWhiteSpace(column.Width)) - { builder.AddAttribute(seq++, "Width", column.Width); - } - if (!string.IsNullOrWhiteSpace(column.DisplayFormat)) - { builder.AddAttribute(seq++, "DisplayFormat", column.DisplayFormat); - } - if (column.ReadOnly) - { builder.AddAttribute(seq++, "ReadOnly", true); + builder.CloseComponent(); + } + + 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(); + return; } - builder.CloseComponent(); + if (e.FieldIdentifier.FieldName == nameof(CatalogEditModel.CatTitle)) + { + validationMessageStore.Clear(new FieldIdentifier(editContext.Model, nameof(CatalogEditModel.CatTitle))); + editContext.NotifyValidationStateChanged(); + } } - private sealed class BandLayout + private void SetPopupHeaderText(bool isNew) => popupHeaderText = isNew ? "Neu" : "Edit"; + + private void OnCustomizeEditModel(GridCustomizeEditModelEventArgs e) { - public List Bands { get; set; } = new(); - public List ColumnOrder { get; set; } = new(); - public Dictionary ColumnWidths { get; set; } = new(StringComparer.OrdinalIgnoreCase); - public GridPersistentLayout? GridLayout { get; set; } - public DevExpress.Blazor.SizeMode SizeMode { get; set; } = DevExpress.Blazor.SizeMode.Medium; + popupHeaderText = e.IsNew ? "Neu" : "Edit"; + if (e.IsNew) + { + e.EditModel = new CatalogEditModel { IsNew = true }; + return; + } + + var item = (CatalogReadDto)e.DataItem; + e.EditModel = new CatalogEditModel + { + Guid = item.Guid, + CatTitle = item.CatTitle, + CatString = item.CatString, + UpdateProcedure = 0, + OriginalCatTitle = item.CatTitle, + IsNew = false + }; } - private sealed class BandDefinition + private async Task OnEditModelSaving(GridEditModelSavingEventArgs e) { - public string Id { get; set; } = string.Empty; - public string Caption { get; set; } = string.Empty; - public List Columns { get; set; } = new(); + 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."; + focusedRowKey = created.Value.Guid; + } + else + { + var updated = await Api.UpdateAsync(editModel.Guid, dto); + if (!updated.Success) + { + errorMessage = updated.Error ?? "Aktualisierung fehlgeschlagen."; + e.Cancel = true; + return; + } + infoMessage = "Katalog aktualisiert."; + focusedRowKey = editModel.Guid; + } + + await LoadCatalogs(); + } + catch (Exception ex) + { + errorMessage = $"Fehler beim Speichern: {ex.Message}"; + e.Cancel = true; + } } - private sealed class BandOption + private void AddValidationError(CatalogEditModel editModel, string fieldName, string message) { - public string Id { get; set; } = string.Empty; - public string Caption { get; set; } = string.Empty; + if (editContext == null || validationMessageStore == null) return; + validationMessageStore.Add(new FieldIdentifier(editModel, fieldName), message); + editContext.NotifyValidationStateChanged(); } - private sealed class ColumnDefinition + private bool ValidateEditModel(CatalogEditModel editModel, bool isNew) { - public string FieldName { get; init; } = string.Empty; - public string Caption { get; init; } = string.Empty; - public string? Width { get; set; } - public string? DisplayFormat { get; init; } - public bool ReadOnly { get; init; } - public ColumnFilterType FilterType { get; init; } + 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 enum ColumnFilterType + private async Task OnDataItemDeleting(GridDataItemDeletingEventArgs e) { - Text, - Date + 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 @@ -762,10 +565,4 @@ else public int Value { get; set; } public string Text { get; set; } = string.Empty; } - - private sealed class FilterOperatorOption - { - public string Value { get; set; } = string.Empty; - public string Text { get; set; } = string.Empty; - } -} +} \ No newline at end of file diff --git a/DbFirst.BlazorWebApp/Components/MassDataGrid.razor b/DbFirst.BlazorWebApp/Components/MassDataGrid.razor index d2f281f..acbccae 100644 --- a/DbFirst.BlazorWebApp/Components/MassDataGrid.razor +++ b/DbFirst.BlazorWebApp/Components/MassDataGrid.razor @@ -1,6 +1,5 @@ @inject MassDataApiClient Api -@inject LayoutApiClient LayoutApi -@inject IJSRuntime JsRuntime +@inject BandLayoutService BandLayoutService @if (!string.IsNullOrWhiteSpace(errorMessage)) { @@ -113,7 +112,9 @@ else @RenderColumns() - @{ SetEditContext(editFormContext.EditContext); var editModel = (MassDataEditModel)editFormContext.EditModel; SetPopupHeaderText(editModel.IsNew); } + @{ + SetEditContext(editFormContext.EditContext); var editModel = (MassDataEditModel)editFormContext.EditModel; SetPopupHeaderText(editModel.IsNew); + } @@ -171,13 +172,13 @@ else private int? focusedRowKey; private const string LayoutType = "GRID_BANDS"; private const string LayoutKey = "MassDataGrid"; - private const string LayoutUserStorageKey = "layoutUser"; private string? layoutUser; private BandLayout bandLayout = new(); private Dictionary columnBandAssignments = new(); private List bandOptions = new(); private Dictionary columnLookup = new(); - private readonly JsonSerializerOptions jsonOptions = new(JsonSerializerDefaults.Web); + private bool gridLayoutApplied; + private List columnDefinitions = new() { new() { FieldName = nameof(MassDataReadDto.Id), Caption = "Id", Width = "90px", ReadOnly = true, FilterType = ColumnFilterType.Text }, @@ -189,15 +190,13 @@ else new() { FieldName = nameof(MassDataReadDto.ChangedWhen), Caption = "Changed", ReadOnly = true, FilterType = ColumnFilterType.Date } }; - private bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser); - private readonly List pageSizeOptions = new() { - new() { Value = 100, Text = "100" }, - new() { Value = 1000, Text = "1.000" }, - new() { Value = 10000, Text = "10.000" }, + new() { Value = 100, Text = "100" }, + new() { Value = 1000, Text = "1.000" }, + new() { Value = 10000, Text = "10.000" }, new() { Value = 100000, Text = "100.000" }, - new() { Value = null, Text = "Alle" } + new() { Value = null, Text = "Alle" } }; private readonly List procedureOptions = new() @@ -205,33 +204,46 @@ else new() { Value = 0, Text = "PRMassdata_UpsertByCustomerName" } }; - private bool gridLayoutApplied; + private bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser); - private DevExpress.Blazor.SizeMode _sizeMode = DevExpress.Blazor.SizeMode.Medium; - private static readonly List _sizeModes = - Enum.GetValues().ToList(); + private SizeMode _sizeMode = SizeMode.Medium; + private static readonly List _sizeModes = Enum.GetValues().ToList(); - private string FormatSizeText(DevExpress.Blazor.SizeMode size) => size switch + private string FormatSizeText(SizeMode size) => size switch { - DevExpress.Blazor.SizeMode.Small => "Klein", - DevExpress.Blazor.SizeMode.Medium => "Mittel", - DevExpress.Blazor.SizeMode.Large => "Groß", + SizeMode.Small => "Klein", + SizeMode.Medium => "Mittel", + SizeMode.Large => "Groß", _ => size.ToString() }; private void OnSizeChange(DropDownButtonItemClickEventArgs args) { - _sizeMode = Enum.Parse(args.ItemInfo.Id); + _sizeMode = Enum.Parse(args.ItemInfo.Id); } protected override async Task OnInitializedAsync() { - columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase); - await EnsureLayoutUserAsync(); - await LoadBandLayoutAsync(); + columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase); + layoutUser = await BandLayoutService.EnsureLayoutUserAsync(); + bandLayout = await BandLayoutService.LoadBandLayoutAsync(LayoutType, LayoutKey, layoutUser, columnLookup); + columnBandAssignments = BandLayoutService.BuildAssignmentsFromLayout(bandLayout); + ApplyColumnLayoutFromStorage(); + _sizeMode = bandLayout.SizeMode; + UpdateBandOptions(); await LoadPage(0); } + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (!gridLayoutApplied && gridRef != null && bandLayout.GridLayout != null) + { + gridRef.LoadLayout(bandLayout.GridLayout); + gridLayoutApplied = true; + await InvokeAsync(StateHasChanged); + } + } + private async Task LoadPage(int page) { isLoading = true; @@ -242,7 +254,6 @@ else var effectivePageSize = pageSize ?? (total == 0 ? 1 : total); pageCount = Math.Max(1, (int)Math.Ceiling(total / (double)effectivePageSize)); pageIndex = Math.Clamp(page, 0, pageCount - 1); - var skip = pageSize.HasValue ? pageIndex * pageSize.Value : 0; items = await Api.GetAllAsync(skip, pageSize); } @@ -258,10 +269,7 @@ else } } - private async Task OnPageChanged(int index) - { - await LoadPage(index); - } + private async Task OnPageChanged(int index) => await LoadPage(index); private async Task OnPageSizeChanged(int? size) { @@ -269,61 +277,15 @@ else await LoadPage(0); } - private async Task EnsureLayoutUserAsync() - { - layoutUser = await JsRuntime.InvokeAsync("localStorage.getItem", LayoutUserStorageKey); - if (string.IsNullOrWhiteSpace(layoutUser)) - { - layoutUser = Guid.NewGuid().ToString("N"); - await JsRuntime.InvokeVoidAsync("localStorage.setItem", LayoutUserStorageKey, layoutUser); - } - } - - private async Task LoadBandLayoutAsync() - { - if (string.IsNullOrWhiteSpace(layoutUser)) - { - bandLayout = new BandLayout(); - UpdateBandOptions(); - return; - } - - var stored = await LayoutApi.GetAsync(LayoutType, LayoutKey, layoutUser); - if (stored != null && !string.IsNullOrWhiteSpace(stored.LayoutData)) - { - var parsed = JsonSerializer.Deserialize(stored.LayoutData, jsonOptions); - bandLayout = NormalizeBandLayout(parsed); - } - else - { - bandLayout = new BandLayout(); - } - - columnBandAssignments = BuildAssignmentsFromLayout(bandLayout); - ApplyColumnLayoutFromStorage(); - _sizeMode = bandLayout.SizeMode; - UpdateBandOptions(); - } - private async Task SaveLayoutAsync() { if (string.IsNullOrWhiteSpace(layoutUser)) - { return; - } try { CaptureColumnLayoutFromGrid(); - - var layoutData = JsonSerializer.Serialize(bandLayout, jsonOptions); - await LayoutApi.UpsertAsync(new LayoutDto - { - LayoutType = LayoutType, - LayoutKey = LayoutKey, - UserName = layoutUser, - LayoutData = layoutData - }); + await BandLayoutService.SaveBandLayoutAsync(LayoutType, LayoutKey, layoutUser, bandLayout); infoMessage = "Layout gespeichert."; errorMessage = null; } @@ -333,41 +295,12 @@ else } } - private void CaptureColumnLayoutFromGrid() - { - if (gridRef == null) - { - return; - } - - var layout = gridRef.SaveLayout(); - bandLayout.GridLayout = layout; - bandLayout.SizeMode = _sizeMode; - - var orderedColumns = layout.Columns - .Where(column => !string.IsNullOrWhiteSpace(column.FieldName)) - .OrderBy(column => column.VisibleIndex) - .ToList(); - - bandLayout.ColumnOrder = orderedColumns - .Select(column => column.FieldName) - .ToList(); - - bandLayout.ColumnWidths = orderedColumns - .Where(column => !string.IsNullOrWhiteSpace(column.Width)) - .ToDictionary(column => column.FieldName, column => column.Width, StringComparer.OrdinalIgnoreCase); - - bandLayout.SizeMode = _sizeMode; - } - private async Task ResetLayoutAsync() { if (string.IsNullOrWhiteSpace(layoutUser)) - { return; - } - await LayoutApi.DeleteAsync(LayoutType, LayoutKey, layoutUser); + await BandLayoutService.ResetBandLayoutAsync(LayoutType, LayoutKey, layoutUser); bandLayout = new BandLayout(); columnBandAssignments.Clear(); @@ -377,49 +310,57 @@ else column.Width = null; columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase); - _sizeMode = DevExpress.Blazor.SizeMode.Medium; + _sizeMode = SizeMode.Medium; if (gridRef != null) gridRef.LoadLayout(new GridPersistentLayout()); gridLayoutApplied = false; - infoMessage = "Layout zur\u00fcckgesetzt."; + infoMessage = "Layout zurückgesetzt."; errorMessage = null; } + private void CaptureColumnLayoutFromGrid() + { + if (gridRef == null) + return; + + var layout = gridRef.SaveLayout(); + bandLayout.GridLayout = layout; + bandLayout.SizeMode = _sizeMode; + + var orderedColumns = layout.Columns + .Where(c => !string.IsNullOrWhiteSpace(c.FieldName)) + .OrderBy(c => c.VisibleIndex) + .ToList(); + + bandLayout.ColumnOrder = orderedColumns.Select(c => c.FieldName).ToList(); + bandLayout.ColumnWidths = orderedColumns + .Where(c => !string.IsNullOrWhiteSpace(c.Width)) + .ToDictionary(c => c.FieldName, c => c.Width, StringComparer.OrdinalIgnoreCase); + } + private void ApplyColumnLayoutFromStorage() { foreach (var column in columnDefinitions) { if (bandLayout.ColumnWidths.TryGetValue(column.FieldName, out var width) && !string.IsNullOrWhiteSpace(width)) - { column.Width = width; - } } - - columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase); + columnLookup = columnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase); } private void AddBand() { - bandLayout.Bands.Add(new BandDefinition - { - Id = Guid.NewGuid().ToString("N"), - Caption = "Band" - }); + bandLayout.Bands.Add(new BandDefinition { Id = Guid.NewGuid().ToString("N"), Caption = "Band" }); UpdateBandOptions(); } private void RemoveBand(BandDefinition band) { bandLayout.Bands.Remove(band); - var removedColumns = columnBandAssignments.Where(pair => pair.Value == band.Id) - .Select(pair => pair.Key) - .ToList(); - foreach (var column in removedColumns) - { - columnBandAssignments.Remove(column); - } + foreach (var key in columnBandAssignments.Where(p => p.Value == band.Id).Select(p => p.Key).ToList()) + columnBandAssignments.Remove(key); UpdateBandOptions(); SyncBandsFromAssignments(); } @@ -433,77 +374,31 @@ else private void UpdateColumnBand(string fieldName, string? bandId) { if (string.IsNullOrWhiteSpace(bandId)) - { columnBandAssignments.Remove(fieldName); - } else - { columnBandAssignments[fieldName] = bandId; - } - SyncBandsFromAssignments(); } private string GetColumnBand(string fieldName) - { - return columnBandAssignments.TryGetValue(fieldName, out var bandId) ? bandId : string.Empty; - } + => columnBandAssignments.TryGetValue(fieldName, out var bandId) ? bandId : string.Empty; private void SyncBandsFromAssignments() { foreach (var band in bandLayout.Bands) { band.Columns = columnDefinitions - .Where(column => columnBandAssignments.TryGetValue(column.FieldName, out var bandId) && bandId == band.Id) - .Select(column => column.FieldName) + .Where(c => columnBandAssignments.TryGetValue(c.FieldName, out var id) && id == band.Id) + .Select(c => c.FieldName) .ToList(); } - StateHasChanged(); } private void UpdateBandOptions() { bandOptions = new List { new() { Id = string.Empty, Caption = "Ohne Band" } }; - bandOptions.AddRange(bandLayout.Bands.Select(band => new BandOption { Id = band.Id, Caption = band.Caption })); - } - - private Dictionary BuildAssignmentsFromLayout(BandLayout layout) - { - var assignments = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var band in layout.Bands) - { - foreach (var column in band.Columns) - { - assignments[column] = band.Id; - } - } - - return assignments; - } - - private BandLayout NormalizeBandLayout(BandLayout? layout) - { - layout ??= new BandLayout(); - layout.Bands ??= new List(); - layout.ColumnOrder ??= new List(); - layout.ColumnWidths ??= new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var band in layout.Bands) - { - if (string.IsNullOrWhiteSpace(band.Id)) - { - band.Id = Guid.NewGuid().ToString("N"); - } - - if (string.IsNullOrWhiteSpace(band.Caption)) - { - band.Caption = "Band"; - } - - band.Columns = band.Columns?.Where(columnLookup.ContainsKey).ToList() ?? new List(); - } - - return layout; + bandOptions.AddRange(bandLayout.Bands.Select(b => new BandOption { Id = b.Id, Caption = b.Caption })); } private RenderFragment RenderColumns() => builder => @@ -513,18 +408,13 @@ else builder.AddAttribute(seq++, "Width", "120px"); builder.CloseComponent(); - var grouped = bandLayout.Bands.SelectMany(band => band.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase); - foreach (var column in columnDefinitions.Where(column => !grouped.Contains(column.FieldName))) - { + var grouped = bandLayout.Bands.SelectMany(b => b.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase); + foreach (var column in columnDefinitions.Where(c => !grouped.Contains(c.FieldName))) BuildDataColumn(builder, ref seq, column); - } foreach (var band in bandLayout.Bands) { - if (band.Columns.Count == 0) - { - continue; - } + if (band.Columns.Count == 0) continue; builder.OpenComponent(seq++); builder.AddAttribute(seq++, "Caption", band.Caption); @@ -534,9 +424,7 @@ else foreach (var columnName in band.Columns) { if (columnLookup.TryGetValue(columnName, out var column)) - { BuildDataColumn(bandBuilder, ref bandSeq, column); - } } })); builder.CloseComponent(); @@ -549,35 +437,19 @@ else builder.AddAttribute(seq++, "FieldName", column.FieldName); builder.AddAttribute(seq++, "Caption", column.Caption); if (!string.IsNullOrWhiteSpace(column.Width)) - { builder.AddAttribute(seq++, "Width", column.Width); - } - if (!string.IsNullOrWhiteSpace(column.DisplayFormat)) - { builder.AddAttribute(seq++, "DisplayFormat", column.DisplayFormat); - } - if (column.ReadOnly) - { builder.AddAttribute(seq++, "ReadOnly", true); - } - builder.CloseComponent(); } private void SetEditContext(EditContext context) { - if (editContext == context) - { - return; - } - + if (editContext == context) return; if (editContext != null) - { editContext.OnFieldChanged -= OnEditFieldChanged; - } - editContext = context; validationMessageStore = new ValidationMessageStore(editContext); editContext.OnFieldChanged += OnEditFieldChanged; @@ -585,10 +457,7 @@ else private void OnEditFieldChanged(object? sender, FieldChangedEventArgs e) { - if (validationMessageStore == null || editContext == null) - { - return; - } + if (validationMessageStore == null || editContext == null) return; if (e.FieldIdentifier.FieldName == nameof(MassDataEditModel.UpdateProcedure)) { @@ -599,16 +468,12 @@ else if (e.FieldIdentifier.FieldName == nameof(MassDataEditModel.CustomerName)) { - var field = new FieldIdentifier(editContext.Model, nameof(MassDataEditModel.CustomerName)); - validationMessageStore.Clear(field); + validationMessageStore.Clear(new FieldIdentifier(editContext.Model, nameof(MassDataEditModel.CustomerName))); editContext.NotifyValidationStateChanged(); } } - private void SetPopupHeaderText(bool isNew) - { - popupHeaderText = isNew ? "Neu" : "Edit"; - } + private void SetPopupHeaderText(bool isNew) => popupHeaderText = isNew ? "Neu" : "Edit"; private async Task OnCustomizeEditModel(GridCustomizeEditModelEventArgs e) { @@ -638,7 +503,6 @@ else { errorMessage = null; infoMessage = null; - validationMessageStore?.Clear(); editContext?.NotifyValidationStateChanged(); @@ -685,13 +549,8 @@ else private void AddValidationError(MassDataEditModel editModel, string fieldName, string message) { - if (editContext == null || validationMessageStore == null) - { - return; - } - - var field = new FieldIdentifier(editModel, fieldName); - validationMessageStore.Add(field, message); + if (editContext == null || validationMessageStore == null) return; + validationMessageStore.Add(new FieldIdentifier(editModel, fieldName), message); editContext.NotifyValidationStateChanged(); } @@ -703,55 +562,6 @@ else return Task.CompletedTask; } - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (!gridLayoutApplied && gridRef != null && bandLayout.GridLayout != null) - { - gridRef.LoadLayout(bandLayout.GridLayout); - gridLayoutApplied = true; - await InvokeAsync(StateHasChanged); - } - } - - private sealed class BandLayout - { - public List Bands { get; set; } = new(); - public List ColumnOrder { get; set; } = new(); - public Dictionary ColumnWidths { get; set; } = new(StringComparer.OrdinalIgnoreCase); - public GridPersistentLayout? GridLayout { get; set; } - public DevExpress.Blazor.SizeMode SizeMode { get; set; } = DevExpress.Blazor.SizeMode.Medium; - } - - private sealed class BandDefinition - { - public string Id { get; set; } = string.Empty; - public string Caption { get; set; } = string.Empty; - public List Columns { get; set; } = new(); - } - - private sealed class BandOption - { - public string Id { get; set; } = string.Empty; - public string Caption { get; set; } = string.Empty; - } - - private sealed class ColumnDefinition - { - public string FieldName { get; init; } = string.Empty; - public string Caption { get; init; } = string.Empty; - public string? Width { get; set; } - public string? DisplayFormat { get; init; } - public bool ReadOnly { get; init; } - public ColumnFilterType FilterType { get; init; } - } - - private enum ColumnFilterType - { - Text, - Bool, - Date - } - private sealed class MassDataEditModel { public int Id { get; set; } @@ -770,21 +580,9 @@ else public string Text { get; set; } = string.Empty; } - private sealed class BoolFilterOption - { - public bool? Value { get; set; } - public string Text { get; set; } = string.Empty; - } - private sealed class PageSizeOption { public int? Value { get; set; } public string Text { get; set; } = string.Empty; } - - private sealed class FilterOperatorOption - { - public string Value { get; set; } = string.Empty; - public string Text { get; set; } = string.Empty; - } -} +} \ No newline at end of file diff --git a/DbFirst.BlazorWebApp/Components/_Imports.razor b/DbFirst.BlazorWebApp/Components/_Imports.razor index 01e886f..8c2fedc 100644 --- a/DbFirst.BlazorWebApp/Components/_Imports.razor +++ b/DbFirst.BlazorWebApp/Components/_Imports.razor @@ -1,5 +1,6 @@ @using System.Net.Http @using System.Net.Http.Json +@using System.Text.Json @using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Rendering @using Microsoft.AspNetCore.Components.Routing @@ -11,10 +12,9 @@ @using DbFirst.BlazorWebApp @using DbFirst.BlazorWebApp.Components @using DbFirst.BlazorWebApp.Models +@using DbFirst.BlazorWebApp.Models.Grid @using DbFirst.BlazorWebApp.Services @using DevExpress.Blazor @using DevExpress.DashboardBlazor @using DevExpress.DashboardWeb -@using DevExpress.Data.Filtering -@using DbFirst.BlazorWebApp -@using System.Text.Json \ No newline at end of file +@using DevExpress.Data.Filtering \ No newline at end of file diff --git a/DbFirst.BlazorWebApp/Models/Grid/BandLayoutModels.cs b/DbFirst.BlazorWebApp/Models/Grid/BandLayoutModels.cs new file mode 100644 index 0000000..468ca1a --- /dev/null +++ b/DbFirst.BlazorWebApp/Models/Grid/BandLayoutModels.cs @@ -0,0 +1,43 @@ +using DevExpress.Blazor; + +namespace DbFirst.BlazorWebApp.Models.Grid +{ + public class BandLayout + { + public List Bands { get; set; } = new(); + public List ColumnOrder { get; set; } = new(); + public Dictionary ColumnWidths { get; set; } = new(StringComparer.OrdinalIgnoreCase); + public GridPersistentLayout? GridLayout { get; set; } + public SizeMode SizeMode { get; set; } = SizeMode.Medium; + } + + public class BandDefinition + { + public string Id { get; set; } = string.Empty; + public string Caption { get; set; } = string.Empty; + public List Columns { get; set; } = new(); + } + + public class BandOption + { + public string Id { get; set; } = string.Empty; + public string Caption { get; set; } = string.Empty; + } + + public class ColumnDefinition + { + public string FieldName { get; init; } = string.Empty; + public string Caption { get; init; } = string.Empty; + public string? Width { get; set; } + public string? DisplayFormat { get; init; } + public bool ReadOnly { get; init; } + public ColumnFilterType FilterType { get; init; } + } + + public enum ColumnFilterType + { + Text, + Bool, + Date + } +} \ No newline at end of file diff --git a/DbFirst.BlazorWebApp/Program.cs b/DbFirst.BlazorWebApp/Program.cs index 86dc0f1..e06db92 100644 --- a/DbFirst.BlazorWebApp/Program.cs +++ b/DbFirst.BlazorWebApp/Program.cs @@ -10,6 +10,7 @@ builder.Services.AddRazorComponents() builder.Services.AddDevExpressBlazor(options => options.BootstrapVersion = BootstrapVersion.v5); builder.Services.AddScoped(); +builder.Services.AddScoped(); var apiBaseUrl = builder.Configuration["ApiBaseUrl"]; if (!string.IsNullOrWhiteSpace(apiBaseUrl)) diff --git a/DbFirst.BlazorWebApp/Services/BandLayoutService.cs b/DbFirst.BlazorWebApp/Services/BandLayoutService.cs new file mode 100644 index 0000000..4e1ae96 --- /dev/null +++ b/DbFirst.BlazorWebApp/Services/BandLayoutService.cs @@ -0,0 +1,103 @@ +using DbFirst.BlazorWebApp.Models; +using DbFirst.BlazorWebApp.Models.Grid; +using Microsoft.JSInterop; +using System.Text.Json; + +namespace DbFirst.BlazorWebApp.Services +{ + public class BandLayoutService(LayoutApiClient layoutApi, IJSRuntime jsRuntime) + { + private const string LayoutUserStorageKey = "layoutUser"; + private readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web); + + public async Task EnsureLayoutUserAsync() + { + var layoutUser = await jsRuntime.InvokeAsync("localStorage.getItem", LayoutUserStorageKey); + if (string.IsNullOrWhiteSpace(layoutUser)) + { + layoutUser = Guid.NewGuid().ToString("N"); + await jsRuntime.InvokeVoidAsync("localStorage.setItem", LayoutUserStorageKey, layoutUser); + } + return layoutUser; + } + + public async Task LoadBandLayoutAsync( + string layoutType, + string layoutKey, + string layoutUser, + Dictionary columnLookup) + { + if (string.IsNullOrWhiteSpace(layoutUser)) + return new BandLayout(); + + var stored = await layoutApi.GetAsync(layoutType, layoutKey, layoutUser); + if (stored != null && !string.IsNullOrWhiteSpace(stored.LayoutData)) + { + var parsed = JsonSerializer.Deserialize(stored.LayoutData, _jsonOptions); + return NormalizeBandLayout(parsed, columnLookup); + } + + return new BandLayout(); + } + + public async Task SaveBandLayoutAsync( + string layoutType, + string layoutKey, + string layoutUser, + BandLayout bandLayout) + { + var layoutData = JsonSerializer.Serialize(bandLayout, _jsonOptions); + await layoutApi.UpsertAsync(new LayoutDto + { + LayoutType = layoutType, + LayoutKey = layoutKey, + UserName = layoutUser, + LayoutData = layoutData + }); + } + + public async Task ResetBandLayoutAsync( + string layoutType, + string layoutKey, + string layoutUser) + { + await layoutApi.DeleteAsync(layoutType, layoutKey, layoutUser); + } + + public Dictionary BuildAssignmentsFromLayout(BandLayout layout) + { + var assignments = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var band in layout.Bands) + { + foreach (var column in band.Columns) + { + assignments[column] = band.Id; + } + } + return assignments; + } + + public BandLayout NormalizeBandLayout( + BandLayout? layout, + Dictionary columnLookup) + { + layout ??= new BandLayout(); + layout.Bands ??= new List(); + layout.ColumnOrder ??= new List(); + layout.ColumnWidths ??= new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var band in layout.Bands) + { + if (string.IsNullOrWhiteSpace(band.Id)) + band.Id = Guid.NewGuid().ToString("N"); + + if (string.IsNullOrWhiteSpace(band.Caption)) + band.Caption = "Band"; + + band.Columns = band.Columns?.Where(columnLookup.ContainsKey).ToList() ?? new List(); + } + + return layout; + } + } +} \ No newline at end of file