using DiunaBI.Application.DTOModels; using DiunaBI.UI.Shared.Services; using Microsoft.AspNetCore.Components; using MudBlazor; using System.Reflection; namespace DiunaBI.UI.Shared.Pages.Layers; public partial class Details : ComponentBase { [Parameter] public Guid Id { get; set; } [Inject] private IDialogService DialogService { get; set; } = null!; [Inject] private LayerService LayerService { get; set; } = null!; [Inject] private JobService JobService { get; set; } = null!; [Inject] private NavigationManager NavigationManager { get; set; } = null!; [Inject] private ISnackbar Snackbar { get; set; } = null!; private LayerDto? layer; private List records = new(); private List displayedColumns = new(); private double valueSum = 0; private bool isLoading = false; private Guid? editingRecordId = null; private RecordDto? editingRecord = null; private bool isAddingNew = false; private RecordDto newRecord = new(); private bool isEditable => layer?.Type == LayerType.Dictionary || layer?.Type == LayerType.Administration; // History tab state private bool isLoadingHistory = false; private bool isHistoryTabInitialized = false; private RecordDto? selectedRecordForHistory = null; private DeletedRecordDto? selectedDeletedRecordForHistory = null; private List recordHistory = new(); private List deletedRecords = new(); private Dictionary userCache = new(); protected override async Task OnInitializedAsync() { await LoadLayer(); } protected override async Task OnParametersSetAsync() { await LoadLayer(); } protected override async Task OnAfterRenderAsync(bool firstRender) { if (!isHistoryTabInitialized && !isLoadingHistory && selectedRecordForHistory == null && selectedDeletedRecordForHistory == null && deletedRecords.Count == 0) { await LoadDeletedRecordsAsync(); } } private async Task LoadLayer() { isLoading = true; StateHasChanged(); try { layer = await LayerService.GetLayerByIdAsync(Id); if (layer != null && layer.Records != null) { records = layer.Records.OrderBy(r => r.Code).ToList(); CalculateDisplayedColumns(); CalculateValueSum(); BuildUserCache(); } } catch (Exception ex) { Console.Error.WriteLine($"Error loading layer: {ex.Message}"); Snackbar.Add("Error loading layer", Severity.Error); } finally { isLoading = false; StateHasChanged(); } } private void CalculateDisplayedColumns() { displayedColumns.Clear(); // Check which Value columns have data for (int i = 1; i <= 32; i++) { var columnName = $"Value{i}"; var hasData = records.Any(r => GetRecordValueByName(r, columnName) != null); if (hasData) { displayedColumns.Add(columnName); } } // Check if Desc1 has data if (records.Any(r => !string.IsNullOrEmpty(r.Desc1))) { displayedColumns.Add("Description1"); } } private void CalculateValueSum() { valueSum = records .Where(r => r.Value1.HasValue) .Sum(r => r.Value1!.Value); } private string GetRecordValue(RecordDto record, string columnName) { if (columnName == "Description1") { return record.Desc1 ?? string.Empty; } var value = GetRecordValueByName(record, columnName); return value.HasValue ? value.Value.ToString("N2") : string.Empty; } private double? GetRecordValueByName(RecordDto record, string columnName) { var property = typeof(RecordDto).GetProperty(columnName, BindingFlags.Public | BindingFlags.Instance); if (property != null && property.PropertyType == typeof(double?)) { return property.GetValue(record) as double?; } return null; } private void Export() { // TODO: Implement export functionality Snackbar.Add("Export functionality coming soon", Severity.Error); } private void ProcessLayer() { // TODO: Implement process layer functionality Snackbar.Add("Process layer functionality coming soon", Severity.Error); } private void GoBack() { NavigationManager.NavigateTo("/layers"); } // Record editing methods private void StartEdit(RecordDto record) { editingRecordId = record.Id; editingRecord = new RecordDto { Id = record.Id, Code = record.Code, Desc1 = record.Desc1, LayerId = record.LayerId }; } private void CancelEdit() { editingRecordId = null; editingRecord = null; } private async Task SaveEdit() { if (editingRecord == null || layer == null) return; if (string.IsNullOrWhiteSpace(editingRecord.Code)) { Snackbar.Add("Code is required", Severity.Warning); return; } if (string.IsNullOrWhiteSpace(editingRecord.Desc1)) { Snackbar.Add("Description is required", Severity.Warning); return; } try { var updated = await LayerService.UpdateRecordAsync(layer.Id, editingRecord.Id, editingRecord); if (updated != null) { var record = records.FirstOrDefault(r => r.Id == editingRecord.Id); if (record != null) { record.Code = updated.Code; record.Desc1 = updated.Desc1; record.ModifiedAt = updated.ModifiedAt; } editingRecordId = null; editingRecord = null; Snackbar.Add("Record updated successfully", Severity.Success); StateHasChanged(); } else { Snackbar.Add("Failed to update record", Severity.Error); } } catch (Exception ex) { Console.Error.WriteLine($"Error updating record: {ex.Message}"); Snackbar.Add("Error updating record", Severity.Error); } } private async Task DeleteRecord(RecordDto record) { if (layer == null) return; var result = await DialogService.ShowMessageBox( "Confirm Delete", $"Are you sure you want to delete record '{record.Code}'?", yesText: "Delete", cancelText: "Cancel"); if (result == true) { try { var success = await LayerService.DeleteRecordAsync(layer.Id, record.Id); if (success) { records.Remove(record); CalculateDisplayedColumns(); CalculateValueSum(); Snackbar.Add("Record deleted successfully", Severity.Success); } else { Snackbar.Add("Failed to delete record", Severity.Error); } } catch (Exception ex) { Console.Error.WriteLine($"Error deleting record: {ex.Message}"); Snackbar.Add("Error deleting record", Severity.Error); } } } private void StartAddNew() { isAddingNew = true; newRecord = new RecordDto { LayerId = layer?.Id ?? Guid.Empty }; } private void CancelAddNew() { isAddingNew = false; newRecord = new(); } private async Task SaveNewRecord() { if (layer == null) return; if (string.IsNullOrWhiteSpace(newRecord.Code)) { Snackbar.Add("Code is required", Severity.Warning); return; } if (string.IsNullOrWhiteSpace(newRecord.Desc1)) { Snackbar.Add("Description is required", Severity.Warning); return; } try { var created = await LayerService.CreateRecordAsync(layer.Id, newRecord); if (created != null) { records.Add(created); CalculateDisplayedColumns(); CalculateValueSum(); isAddingNew = false; newRecord = new(); Snackbar.Add("Record added successfully", Severity.Success); } else { Snackbar.Add("Failed to add record", Severity.Error); } } catch (Exception ex) { Console.Error.WriteLine($"Error adding record: {ex.Message}"); Snackbar.Add("Error adding record", Severity.Error); } } // History tab methods private async Task LoadDeletedRecordsAsync() { if (isHistoryTabInitialized || layer == null) return; isHistoryTabInitialized = true; try { Console.WriteLine($"Loading deleted records for layer {layer.Id}"); deletedRecords = await LayerService.GetDeletedRecordsAsync(layer.Id); Console.WriteLine($"Loaded {deletedRecords.Count} deleted records"); StateHasChanged(); } catch (Exception ex) { Console.Error.WriteLine($"Error loading deleted records: {ex.Message}"); Console.Error.WriteLine($"Stack trace: {ex.StackTrace}"); deletedRecords = new List(); } } private async Task OnRecordClickForHistory(TableRowClickEventArgs args) { if (args.Item == null || layer == null) return; selectedRecordForHistory = args.Item; isLoadingHistory = true; StateHasChanged(); try { recordHistory = await LayerService.GetRecordHistoryAsync(layer.Id, args.Item.Id); } catch (Exception ex) { Console.Error.WriteLine($"Error loading record history: {ex.Message}"); Snackbar.Add("Error loading record history", Severity.Error); recordHistory = new List(); } finally { isLoadingHistory = false; StateHasChanged(); } } private void ClearHistorySelection() { selectedRecordForHistory = null; selectedDeletedRecordForHistory = null; recordHistory.Clear(); isHistoryTabInitialized = false; // Reset so deleted records reload when returning to list } private async Task OnDeletedRecordClickForHistory(TableRowClickEventArgs args) { if (args.Item == null || layer == null) return; selectedDeletedRecordForHistory = args.Item; selectedRecordForHistory = null; isLoadingHistory = true; StateHasChanged(); try { recordHistory = await LayerService.GetRecordHistoryAsync(layer.Id, args.Item.RecordId); } catch (Exception ex) { Console.Error.WriteLine($"Error loading deleted record history: {ex.Message}"); Snackbar.Add("Error loading record history", Severity.Error); recordHistory = new List(); } finally { isLoadingHistory = false; StateHasChanged(); } } private Color GetHistoryColor(string changeType) { return changeType switch { "Created" => Color.Success, "Updated" => Color.Info, "Deleted" => Color.Error, _ => Color.Default }; } private void BuildUserCache() { userCache.Clear(); if (layer == null) return; // Add layer-level users to cache if (layer.CreatedBy != null) userCache.TryAdd(layer.CreatedBy.Id, layer.CreatedBy.Username ?? string.Empty); if (layer.ModifiedBy != null) userCache.TryAdd(layer.ModifiedBy.Id, layer.ModifiedBy.Username ?? string.Empty); } private string GetModifiedByUsername(Guid userId) { return userCache.TryGetValue(userId, out var username) ? username : string.Empty; } // Run Now button methods private bool isRunningJob = false; private bool IsWorkerLayer() { if (layer?.Records == null) return false; var typeRecord = layer.Records.FirstOrDefault(x => x.Code == "Type"); return typeRecord?.Desc1 == "ImportWorker" || typeRecord?.Desc1 == "ProcessWorker"; } private async Task RunNow() { if (layer == null) return; isRunningJob = true; try { var result = await JobService.CreateJobForLayerAsync(layer.Id); if (result != null && result.Success) { if (result.Existing) { Snackbar.Add($"Job already exists: {result.Message}", Severity.Info); } else { Snackbar.Add("Job created successfully! Watch real-time status updates.", Severity.Success); } // Navigate to job detail page to see real-time updates NavigationManager.NavigateTo($"/jobs/{result.JobId}"); } else { Snackbar.Add("Failed to create job", Severity.Error); } } catch (Exception ex) { Console.WriteLine($"Error creating job: {ex.Message}"); Snackbar.Add($"Error creating job: {ex.Message}", Severity.Error); } finally { isRunningJob = false; } } }