354 lines
12 KiB
Plaintext
354 lines
12 KiB
Plaintext
@using OpenArchival.Blazor.Components.CustomComponents;
|
|
@using OpenArchival.Blazor.Components.Pages.Administration.Categories
|
|
@using OpenArchival.DataAccess;
|
|
@using System.ComponentModel.DataAnnotations
|
|
|
|
@inject IDialogService DialogService
|
|
@inject NavigationManager NavigationManager;
|
|
@inject IArchiveCategoryProvider CategoryProvider;
|
|
@inject ArtifactEntrySharedHelpers Helpers;
|
|
|
|
<MudPaper Class="pa-4 ma-2 rounded" Elevation="3">
|
|
<MudText Typo="Typo.h5" Color="Color.Primary">Add an Archive Item</MudText>
|
|
|
|
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
|
|
|
@foreach (var result in ValidationResults)
|
|
{
|
|
<MudAlert Severity="Severity.Error">@result.ErrorMessage</MudAlert>
|
|
}
|
|
|
|
<MudGrid Justify="Justify.Center" Class="pt-4">
|
|
<MudItem>
|
|
<MudAutocomplete
|
|
T="ArchiveCategory"
|
|
ToStringFunc="@(val => val?.Name)"
|
|
Label="Category"
|
|
@bind-Value="Model.Category"
|
|
@bind-Value:after=OnCategoryChanged
|
|
SearchFunc="SearchCategory"
|
|
CoerceValue=false
|
|
CoerceText=false
|
|
/>
|
|
</MudItem>
|
|
|
|
<MudItem>
|
|
<MudFab Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add" OnClick="OnAddCategoryClicked"/>
|
|
</MudItem>
|
|
|
|
</MudGrid>
|
|
</MudPaper>
|
|
|
|
<div @ref="_formDiv" style="@_formDivStyle">
|
|
|
|
<MudPaper Class="pa-4 ma-2 rounded" Elevation="3">
|
|
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Archive Item Identifier</MudText>
|
|
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
|
<IdentifierTextBox @ref="_identifierTextBox" IdentifierFields="@Model.IdentifierFieldValues"></IdentifierTextBox>
|
|
</MudPaper>
|
|
|
|
<MudPaper Class="pa-4 ma-2 rounded" Elevation="3">
|
|
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Grouping Title</MudText>
|
|
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
|
<MudTextField
|
|
T="string"
|
|
For="@(() => Model.Title)"
|
|
Placeholder="Grouping Title"
|
|
@bind-Value=Model.Title></MudTextField>
|
|
|
|
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Grouping Description</MudText>
|
|
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
|
<MudTextField
|
|
T="string"
|
|
For="@(() => Model.Description)"
|
|
Lines="5"
|
|
Placeholder="Grouping Description"
|
|
@bind-Value=Model.Description></MudTextField>
|
|
|
|
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Grouping Type</MudText>
|
|
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
|
<MudAutocomplete
|
|
For="@(() => Model.Type)"
|
|
T="string"
|
|
Label="Artifact Type"
|
|
Class="pt-0 mt-0 pl-2 pr-2"
|
|
@bind-Value=Model.Type
|
|
SearchFunc="Helpers.SearchItemTypes"
|
|
CoerceValue=true></MudAutocomplete>
|
|
</MudPaper>
|
|
|
|
<MudPaper Class="pa-4 ma-2 rounded" Elevation="3">
|
|
<UploadDropBox
|
|
@ref="@_uploadComponent"
|
|
FilesUploaded="OnFilesUploaded"
|
|
ClearClicked="OnClearFilesClicked"
|
|
ExistingFiles="ExistingFiles"></UploadDropBox>
|
|
</MudPaper>
|
|
@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];
|
|
|
|
<ArchiveEntryCreatorCard Model="currentEntry"
|
|
ModelChanged="(updatedEntry) => HandleEntryUpdate(currentEntry, updatedEntry)"
|
|
InputsChanged="OnChanged"
|
|
@key="currentEntry"
|
|
ArtifactEntryIndex="index"
|
|
OnEntryDeletedClicked="OnDeleteEntryClicked"/>
|
|
}
|
|
}
|
|
</div>
|
|
|
|
<MudGrid Justify="Justify.FlexEnd" Class="pt-6">
|
|
<MudItem>
|
|
<MudCheckBox Label="Publicly Visible" T="bool"></MudCheckBox>
|
|
@*<MudCheckBox Label="Publicly Visible" T="bool" @bind-Value=Model.IsPublic></MudCheckBox>*@
|
|
</MudItem>
|
|
|
|
@if (FormButtonsEnabled)
|
|
{
|
|
<MudItem Class="pr-0">
|
|
<MudButton Color="Color.Primary" Variant="Variant.Filled" Class="ml-4" OnClick="CancelClicked">Cancel</MudButton>
|
|
</MudItem>
|
|
|
|
<MudItem Class="pl-2">
|
|
<MudButton Color="Color.Primary" Variant="Variant.Filled" Class="ml-4" OnClick="PublishClicked" Disabled="@(!IsValid)" >Publish</MudButton>
|
|
</MudItem>
|
|
}
|
|
</MudGrid>
|
|
|
|
|
|
@code {
|
|
[Parameter]
|
|
public bool ClearOnPublish { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// The URI to navigate to if cancel is pressed. null to navigate to no page
|
|
/// </summary>
|
|
[Parameter]
|
|
public string? BackLink { get; set; } = null;
|
|
|
|
/// <summary>
|
|
/// The URI to navigate to if publish is pressed, null to navigate to no page
|
|
/// </summary>
|
|
[Parameter]
|
|
public string? ForwardLink { get; set; } = null;
|
|
|
|
/// <summary>
|
|
/// Called when publish is clicked
|
|
/// </summary>
|
|
[Parameter]
|
|
public EventCallback<ArtifactGroupingValidationModel> GroupingPublished { get; set; }
|
|
|
|
/// <summary>
|
|
/// The model to display on the form
|
|
/// </summary>
|
|
[Parameter]
|
|
public ArtifactGroupingValidationModel Model { get; set; } = new();
|
|
|
|
/// <summary>
|
|
/// 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)
|
|
/// </summary>
|
|
[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<string> DatesData { get; set; } = [];
|
|
|
|
public List<ArchiveCategory> Categories { get; set; } = new();
|
|
|
|
private List<FilePathListing> _filePathListings = new();
|
|
|
|
private bool _categorySelected = false;
|
|
|
|
public bool IsValid { get; set; } = false;
|
|
|
|
public List<ValidationResult> 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<FilePathListing> 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;
|
|
}
|
|
|
|
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<ValidationResult>();
|
|
|
|
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<FilePathListing> 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<ArtifactEntryValidationModel>();
|
|
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<ValidationResult>();
|
|
|
|
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<CategoryCreatorDialog>("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<IEnumerable<ArchiveCategory>> SearchCategory(string value, CancellationToken cancellationToken)
|
|
{
|
|
List<ArchiveCategory> 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)
|
|
{
|
|
Model.ArtifactEntries.RemoveAt(index);
|
|
_uploadComponent.Files.RemoveAt(index);
|
|
StateHasChanged();
|
|
}
|
|
}
|