326 lines
14 KiB
Plaintext
326 lines
14 KiB
Plaintext
@namespace OpenArchival.Blazor.AdminPages
|
|
@using Microsoft.AspNetCore.Components.Web
|
|
@using OpenArchival.DataAccess
|
|
@using System.ComponentModel.DataAnnotations
|
|
@using OpenArchival.Blazor.CustomComponents
|
|
@using MudBlazor
|
|
|
|
@inject ArtifactEntrySharedHelpers Helpers;
|
|
@inject IArtifactDefectProvider DefectsProvider;
|
|
@inject IArtifactStorageLocationProvider StorageLocationProvider;
|
|
@inject IArchiveEntryTagProvider TagsProvider;
|
|
@inject IArtifactTypeProvider TypesProvider;
|
|
@inject IListedNameProvider ListedNameProvider;
|
|
|
|
<MudPaper Class="pa-4 ma-2 rounded" Elevation="3">
|
|
<MudGrid>
|
|
<MudItem>
|
|
@if (Model.Files.Count > 0) {
|
|
<MudText Typo="Typo.h6">@(Model.Files[0].OriginalName)</MudText>
|
|
}
|
|
</MudItem>
|
|
<MudItem>
|
|
<MudButton
|
|
Variant="Variant.Filled"
|
|
StartIcon="@Icons.Material.Filled.Delete"
|
|
Color="Color.Error"
|
|
OnClick="OnDeleteEntryClicked"
|
|
>Delete</MudButton>
|
|
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
@foreach (var error in ValidationResults)
|
|
{
|
|
<MudAlert Severity="Severity.Error">
|
|
@error.ErrorMessage
|
|
</MudAlert>
|
|
}
|
|
|
|
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Archive Item Title</MudText>
|
|
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
|
<MudTextField For="@(() => Model.Title)" Required=true Placeholder="Archive Item Title" T="string" Class="pl-4 pr-4" @bind-Value=Model.Title @bind-Value:after=OnInputsChanged></MudTextField>
|
|
|
|
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Archive Item Numbering</MudText>
|
|
<MudText Typo="Typo.caption" Color="Color.Secondary" Class="pb-2">Enter a unique ID for this entry</MudText>
|
|
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
|
<MudTextField For="@(() => Model.ArtifactNumber)" Placeholder="Numbering" T="string" @bind-Value=Model.ArtifactNumber @bind-Value:after=OnInputsChanged/>
|
|
|
|
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Item Description</MudText>
|
|
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
|
<MudTextField For="@(() => Model.Description)" Lines=8 Placeholder="Description" T="string" Class="pl-4 pr-4" @bind-Value=Model.Description @bind-Value:after=OnInputsChanged></MudTextField>
|
|
|
|
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Storage Location</MudText>
|
|
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
|
<MudAutocomplete For="@(() => Model.StorageLocation)" T="string" Label="Storage Location" Class="pt-0 mt-0 pl-2 pr-2" @bind-Value=Model.StorageLocation @bind-Value:after=OnInputsChanged SearchFunc="Helpers.SearchStorageLocation" CoerceValue=true></MudAutocomplete>
|
|
|
|
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Artifact 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 @bind-Value:after=OnInputsChanged SearchFunc="Helpers.SearchItemTypes" CoerceValue=true></MudAutocomplete>
|
|
|
|
@* Tags entry *@
|
|
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Tags</MudText>
|
|
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
|
<ChipContainer T="string" @ref="@_tagsChipContainer" @bind-Items="Model.Tags">
|
|
<InputContent>
|
|
<MudAutocomplete T="string"
|
|
OnInternalInputChanged="OnInputsChanged"
|
|
SearchFunc="Helpers.SearchTags"
|
|
Value="_tagsInputValue"
|
|
ValueChanged="OnTagsInputTextChanged"
|
|
OnKeyDown="@(ev => HandleChipContainerEnter<string>(ev, _tagsChipContainer, _tagsInputValue, () => _tagsInputValue = string.Empty))"
|
|
CoerceValue=true
|
|
Placeholder="Add Tags..."
|
|
ShowProgressIndicator="true"
|
|
>
|
|
</MudAutocomplete>
|
|
</InputContent>
|
|
</ChipContainer>
|
|
|
|
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Listed Names</MudText>
|
|
<MudText Typo="Typo.caption" Color="Color.Secondary" Class="pb-2">Enter any names of the people associated with this entry.</MudText>
|
|
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
|
<ChipContainer T="string" @ref=_listedNamesChipContainer @bind-Items="Model.ListedNames">
|
|
<InputContent>
|
|
<MudAutocomplete T="string"
|
|
SearchFunc="Helpers.SearchListedNames"
|
|
OnInternalInputChanged="OnInputsChanged"
|
|
Value="_listedNamesInputValue"
|
|
ValueChanged="OnListedNamesTextChanged"
|
|
OnKeyDown="@(ev=>HandleChipContainerEnter<string>(ev, _listedNamesChipContainer, _listedNamesInputValue, () => _listedNamesInputValue = string.Empty))"
|
|
CoerceValue=true
|
|
Placeholder="Add Listed Names..."
|
|
ShowProgressIndicator=true>
|
|
</MudAutocomplete>
|
|
</InputContent>
|
|
</ChipContainer>
|
|
|
|
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Associated Dates</MudText>
|
|
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
|
<ChipContainer T="DateTime" @ref="_assocaitedDatesChipContainer" @bind-Items="Model.AssociatedDates" DisplayFunc="date => date.ToShortDateString()">
|
|
<InputContent>
|
|
<MudDatePicker @bind-Date=_associatedDateInputValue MinDate="new DateTime(1000, 1, 1)">
|
|
</MudDatePicker>
|
|
</InputContent>
|
|
<SubmitButton>
|
|
<MudButton Color="Color.Primary"
|
|
OnClick="HandleAssociatedDateChipContainerAdd">+</MudButton>
|
|
</SubmitButton>
|
|
</ChipContainer>
|
|
|
|
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Defects</MudText>
|
|
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
|
<ChipContainer T="string" @ref=_defectsChipContainer @bind-Items="Model.Defects">
|
|
<InputContent>
|
|
<MudAutocomplete T="string"
|
|
SearchFunc="Helpers.SearchDefects"
|
|
OnInternalInputChanged="OnInputsChanged"
|
|
Value="_defectsInputValue"
|
|
ValueChanged="OnDefectsValueChanged"
|
|
OnKeyDown="@(ev=>HandleChipContainerEnter<string>(ev, _defectsChipContainer, _defectsInputValue, () => _defectsInputValue = string.Empty))"
|
|
CoerceValue=true
|
|
Placeholder="Add Defects..."
|
|
ShowProgressIndicator=true>
|
|
</MudAutocomplete>
|
|
</InputContent>
|
|
</ChipContainer>
|
|
|
|
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Related Artifacts</MudText>
|
|
<MudText Typo="Typo.caption" Color="Color.Secondary" Class="pb-2">Tag this entry with the identifier of any other entry to link them.</MudText>
|
|
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
|
<ChipContainer T="ArtifactEntry" @ref="_assocaitedArtifactsChipContainer" @bind-Items="Model.RelatedArtifacts" DisplayFunc="artifact => artifact.ArtifactIdentifier">
|
|
<InputContent>
|
|
<MudAutocomplete T="ArtifactEntry"
|
|
OnInternalInputChanged="OnInputsChanged"
|
|
Value="_associatedArtifactValue"
|
|
ValueChanged="OnAssociatedArtifactChanged"
|
|
OnKeyDown="@(EventArgs=>HandleChipContainerEnter<ArtifactEntry>(EventArgs, _assocaitedArtifactsChipContainer, _associatedArtifactValue, () => _associatedArtifactValue = null))"
|
|
CoerceValue="false"
|
|
Placeholder="Link artifact groupings..."
|
|
ShowProgressIndicator=true>
|
|
</MudAutocomplete>
|
|
</InputContent>
|
|
</ChipContainer>
|
|
|
|
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Artifact Text Contents</MudText>
|
|
<MudText Typo="Typo.caption" Color="Color.Secondary" Class="pb-2">Input the text transcription of the words on the artifact if applicable to aid the search engine.</MudText>
|
|
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
|
<MudTextField T="string"
|
|
Value=Model.FileTextContent
|
|
ValueChanged="OnArtifactTextContentChanged"
|
|
Lines="5"
|
|
For="@(() => Model.FileTextContent)"></MudTextField>
|
|
|
|
<MudText Typo=Typo.h6 Color="Color.Primary">Additional files</MudText>
|
|
<UploadDropBox @ref=_uploadDropBox FilesUploaded="OnFilesUploaded"></UploadDropBox>
|
|
</MudPaper>
|
|
|
|
|
|
@code {
|
|
[Parameter]
|
|
public required FilePathListing MainFilePath { get; set; }
|
|
|
|
[Parameter]
|
|
public EventCallback<ArtifactEntryValidationModel> ModelChanged { get; set; }
|
|
|
|
[Parameter]
|
|
public EventCallback InputsChanged { get; set; }
|
|
|
|
[Parameter]
|
|
public required ArtifactEntryValidationModel Model { get; set; } = new() { StorageLocation = "hello", Title = "Hello" };
|
|
|
|
[Parameter]
|
|
public required int ArtifactEntryIndex {get; set;}
|
|
|
|
[Parameter]
|
|
public EventCallback<(int index, string filename)> OnEntryDeletedClicked { get; set; }
|
|
|
|
private ChipContainer<string> _tagsChipContainer;
|
|
|
|
private string _tagsInputValue { get; set; } = "";
|
|
|
|
private ChipContainer<DateTime> _assocaitedDatesChipContainer;
|
|
|
|
private DateTime? _associatedDateInputValue { get; set; } = default;
|
|
|
|
private ChipContainer<string> _listedNamesChipContainer;
|
|
|
|
private string _listedNamesInputValue { get; set; } = "";
|
|
|
|
private ChipContainer<string> _defectsChipContainer;
|
|
|
|
private string _defectsInputValue = "";
|
|
|
|
private ChipContainer<ArtifactEntry> _assocaitedArtifactsChipContainer;
|
|
|
|
private ArtifactEntry? _associatedArtifactValue = null;
|
|
|
|
private string _artifactTextContent = "";
|
|
|
|
public bool IsValid { get; set; }
|
|
|
|
public List<ValidationResult> ValidationResults { get; private set; } = [];
|
|
|
|
public UploadDropBox _uploadDropBox = default!;
|
|
|
|
protected override Task OnParametersSetAsync()
|
|
{
|
|
if (_uploadDropBox is not null && Model is not null && Model.Files is not null)
|
|
{
|
|
_uploadDropBox.ExistingFiles = Model.Files.GetRange(1, Model.Files.Count - 1);
|
|
}
|
|
|
|
if (Model.Files is not null && Model.Files.Any())
|
|
{
|
|
MainFilePath = Model.Files[0];
|
|
}
|
|
|
|
return base.OnParametersSetAsync();
|
|
}
|
|
|
|
public async Task OnInputsChanged()
|
|
{
|
|
// 1. Clear previous validation errors
|
|
ValidationResults.Clear();
|
|
|
|
var validationContext = new ValidationContext(Model);
|
|
|
|
// 2. Run the validator
|
|
IsValid = Validator.TryValidateObject(Model, validationContext, ValidationResults, validateAllProperties: true);
|
|
// 3. REMOVE this line. Let the parent's update trigger the re-render.
|
|
// StateHasChanged();
|
|
|
|
await InputsChanged.InvokeAsync();
|
|
}
|
|
|
|
private async Task OnFilesUploaded(List<FilePathListing> filePathListings)
|
|
{
|
|
if (MainFilePath is not null)
|
|
{
|
|
var oldFiles = Model.Files.GetRange(1, Model.Files.Count - 1);
|
|
Model.Files = [MainFilePath];
|
|
Model.Files.AddRange(oldFiles);
|
|
Model.Files.AddRange(filePathListings);
|
|
} else
|
|
{
|
|
Model.Files = [];
|
|
}
|
|
Model.Files.AddRange(filePathListings);
|
|
|
|
StateHasChanged();
|
|
}
|
|
|
|
private async Task OnFilesCleared()
|
|
{
|
|
if (MainFilePath is not null) {
|
|
Model.Files = [MainFilePath];
|
|
} else
|
|
{
|
|
Model.Files = [];
|
|
}
|
|
}
|
|
|
|
private Task OnDefectsValueChanged(string text)
|
|
{
|
|
_defectsInputValue = text;
|
|
return ModelChanged.InvokeAsync(Model);
|
|
}
|
|
|
|
private Task OnTagsInputTextChanged(string text)
|
|
{
|
|
_tagsInputValue = text;
|
|
return ModelChanged.InvokeAsync(Model);
|
|
}
|
|
|
|
private Task OnListedNamesTextChanged(string text)
|
|
{
|
|
_listedNamesInputValue = text;
|
|
return ModelChanged.InvokeAsync(Model);
|
|
}
|
|
|
|
private Task OnAssociatedArtifactChanged(ArtifactEntry grouping)
|
|
{
|
|
if (grouping is not null)
|
|
{
|
|
_associatedArtifactValue = grouping;
|
|
return ModelChanged.InvokeAsync(Model);
|
|
}
|
|
|
|
return ModelChanged.InvokeAsync(Model);
|
|
}
|
|
|
|
private Task OnArtifactTextContentChanged(string value)
|
|
{
|
|
Model.FileTextContent = value;
|
|
return ModelChanged.InvokeAsync(Model);
|
|
}
|
|
|
|
public async Task HandleChipContainerEnter<Type>(KeyboardEventArgs args, ChipContainer<Type> container, Type value, Action resetInputAction)
|
|
{
|
|
if (args.Key == "Enter")
|
|
{
|
|
await container.AddItem(value);
|
|
resetInputAction?.Invoke();
|
|
StateHasChanged();
|
|
await ModelChanged.InvokeAsync(Model);
|
|
}
|
|
}
|
|
|
|
public async Task HandleAssociatedDateChipContainerAdd(MouseEventArgs args)
|
|
{
|
|
if (_associatedDateInputValue is not null)
|
|
{
|
|
DateTime unspecifiedDate = (DateTime)_associatedDateInputValue;
|
|
|
|
DateTime utcDate = DateTime.SpecifyKind(unspecifiedDate, DateTimeKind.Utc);
|
|
|
|
await _assocaitedDatesChipContainer.AddItem(utcDate);
|
|
|
|
_associatedDateInputValue = default;
|
|
}
|
|
}
|
|
private async Task OnDeleteEntryClicked(MouseEventArgs args)
|
|
{
|
|
await OnEntryDeletedClicked.InvokeAsync((ArtifactEntryIndex, MainFilePath.OriginalName));
|
|
}
|
|
} |