@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();
}
}