@namespace OpenArchival.Blazor.AdminPages.Shared @using Microsoft.AspNetCore.Components.Web @using System.ComponentModel.DataAnnotations @using MudBlazor @using global::OpenArchival.DataAccess @using OpenArchival.Blazor.CustomComponents @inject IDialogService DialogService @inject NavigationManager NavigationManager; @inject IArchiveCategoryProvider CategoryProvider; @inject ArtifactEntrySharedHelpers Helpers; Add an Archive Item @foreach (var result in ValidationResults) { @result.ErrorMessage }
Archive Item Identifier Grouping Title Grouping Description Grouping Type @if (Model is not null) { @for (int index = 0; index < Model.ArtifactEntries.Count; ++index) { // Capture the current item in a local variable for the lambda var currentEntry = Model.ArtifactEntries[index]; } }
@if (FormButtonsEnabled) { Cancel Publish } @code { [Parameter] public bool ClearOnPublish { get; set; } = true; /// /// The URI to navigate to if cancel is pressed. null to navigate to no page /// [Parameter] public string? BackLink { get; set; } = null; /// /// The URI to navigate to if publish is pressed, null to navigate to no page /// [Parameter] public string? ForwardLink { get; set; } = null; /// /// Called when publish is clicked /// [Parameter] public EventCallback GroupingPublished { get; set; } /// /// The model to display on the form /// [Parameter] public ArtifactGroupingValidationModel Model { get; set; } = new(); /// /// Determines if the cancel and publish buttons should be show to the user or if the containing component will /// handle their functionality (ie if used in a dialog and you want to use the dialog buttons instead of this component's handlers) /// [Parameter] public bool FormButtonsEnabled { get; set; } = true; private UploadDropBox _uploadComponent = default!; private IdentifierTextBox _identifierTextBox = default!; private ElementReference _formDiv = default!; private bool _isFormDivVisible = false; private string _formDivStyle => _isFormDivVisible ? "" : "display: none;"; public List DatesData { get; set; } = []; public List Categories { get; set; } = new(); private List _filePathListings = new(); private bool _categorySelected = false; public bool IsValid { get; set; } = false; public List ValidationResults { get; private set; } = []; // Used to store the files that have already been uploaded if this component is being displayed // with a filled in model. Used to populate the upload drop box private List ExistingFiles { get; set; } = new(); protected override async Task OnParametersSetAsync() { // Ensure to reload the component if a model has been supplied so that the full // component will render if (Model?.Category is not null) { await OnCategoryChanged(); } if (Model is not null && Model.Category is not null) { // The data entry should only be shown if a category has been selected _isFormDivVisible = true; } else { _isFormDivVisible = false; } if (Model is not null) { ExistingFiles = Model.ArtifactEntries .Where(e => e.Files.Any()) .Select(e => e.Files[0]) .ToList(); } StateHasChanged(); } private async Task PublishClicked(MouseEventArgs args) { var validationContext = new ValidationContext(Model); var validationResult = new List(); IsValid = Validator.TryValidateObject(Model, validationContext, validationResult); ArtifactGroupingValidationModel oldModel = Model; if (ForwardLink is not null) { if (IsValid) { NavigationManager.NavigateTo(ForwardLink); } } if (IsValid && ClearOnPublish) { Model = new(); await _uploadComponent.ClearClicked.InvokeAsync(); StateHasChanged(); } await GroupingPublished.InvokeAsync(oldModel); } private void CancelClicked(MouseEventArgs args) { if (BackLink is not null) { NavigationManager.NavigateTo(BackLink); } else { throw new ArgumentNullException("No back link provided for the add archive item page."); } } private async Task HandleEntryUpdate(ArtifactEntryValidationModel originalEntry, ArtifactEntryValidationModel updatedEntry) { // Find the index of the original object in our list var index = Model.ArtifactEntries.IndexOf(originalEntry); // If found, replace it with the updated version if (index != -1) { Model.ArtifactEntries[index] = updatedEntry; } // Now, run the validation logic await OnChanged(); } // You can now simplify your OnFilesUploaded method slightly private async Task OnFilesUploaded(List args) { _filePathListings = args; // This part is tricky. Adding items while iterating can be problematic. // A better approach is to create the entries first, then tell Blazor to render. var newEntries = new List(); foreach (var file in args) { // Associate the file with the entry if needed newEntries.Add(new ArtifactEntryValidationModel { Files = [file]}); } Model.ArtifactEntries.AddRange(newEntries); // StateHasChanged() is implicitly called by OnChanged() if validation passes await OnChanged(); } private async Task OnClearFilesClicked() { _filePathListings = []; Model.ArtifactEntries = []; StateHasChanged(); await OnChanged(); } async Task OnChanged() { var validationContext = new ValidationContext(Model); var validationResult = new List(); IsValid = Validator.TryValidateObject(Model, validationContext, validationResult); if (IsValid) { StateHasChanged(); } } async Task OnCategoryChanged() { if (Model.Category is not null && _identifierTextBox is not null) { _identifierTextBox.VerifyFormatCategory = Model.Category; _isFormDivVisible = true; if (!_categorySelected) { _categorySelected = true; } StateHasChanged(); } await OnChanged(); } public async Task OnAddCategoryClicked() { var options = new DialogOptions { CloseOnEscapeKey = true, BackdropClick = false }; var dialog = await DialogService.ShowAsync("Create a Category", options); var result = await dialog.Result; if (result is not null && !result.Canceled) { await CategoryProvider.CreateCategoryAsync(CategoryValidationModel.ToArchiveCategory((CategoryValidationModel)result.Data)); StateHasChanged(); await OnChanged(); } } private async Task> SearchCategory(string value, CancellationToken cancellationToken) { List categories; if (string.IsNullOrEmpty(value)) { categories = new(await CategoryProvider.Top(25) ?? []); } else { categories = new((await CategoryProvider.Search(value) ?? [])); } return categories; } private async void OnDeleteEntryClicked((int index, string filename) data) { Model.ArtifactEntries.RemoveAt(data.index); _uploadComponent.RemoveFile(data.filename); StateHasChanged(); } }