Got the new ui flow working and the models updated. Need to get changes written to the database.
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
@page "/add"
|
||||
|
||||
@using OpenArchival.Blazor.Components.CustomComponents;
|
||||
@using OpenArchival.Blazor.Components.Pages.Administration.Categories
|
||||
@using OpenArchival.DataAccess;
|
||||
|
||||
<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>
|
||||
@if (!IsValid && _isFormDivVisible)
|
||||
{
|
||||
<MudAlert Severity="Severity.Error" Class="mt-4">
|
||||
All identifier fields must be filled in.
|
||||
</MudAlert>
|
||||
}
|
||||
|
||||
<MudGrid Justify="Justify.Center" Class="pt-4">
|
||||
<MudItem>
|
||||
<MudAutocomplete T="string" 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.IdentifierFields"></IdentifierTextBox>
|
||||
</MudPaper>
|
||||
|
||||
<MudPaper Class="pa-4 ma-2 rounded" Elevation="3">
|
||||
<UploadDropBox FilesUploaded="OnFilesUploaded" ClearClicked="OnClearFilesClicked"></UploadDropBox>
|
||||
</MudPaper>
|
||||
|
||||
@foreach (FilePathListing listing in _filePathListings)
|
||||
{
|
||||
<ArchiveEntryCreatorCard FilePath="listing" OnValueChanged="OnChanged"></ArchiveEntryCreatorCard>
|
||||
}
|
||||
</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>
|
||||
|
||||
<MudItem Style="pr-0">
|
||||
<MudButton Color="Color.Primary" Variant="Variant.Filled" Class="ml-4" OnClick="CancelClicked">Cancel</MudButton>
|
||||
</MudItem>
|
||||
|
||||
<MudItem Style="pl-2">
|
||||
<MudButton Color="Color.Primary" Variant="Variant.Filled" Class="ml-4" OnClick="PublishClicked">Publish</MudButton>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@inject IDialogService DialogService
|
||||
@inject NavigationManager NavigationManager;
|
||||
@inject IArchiveCategoryProvider CategoryProvider;
|
||||
|
||||
@code {
|
||||
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 List<IdentifierFieldValidationModel> IdentifierFields { get; set; } = [new IdentifierFieldValidationModel() { Name = "Field One", Value = "" }, new IdentifierFieldValidationModel() { Name = "Field Two", Value = "" }, new IdentifierFieldValidationModel() { Name = "Field Three", Value = "" }];
|
||||
|
||||
public ArchiveItemValidationModel Model { get; set; } = new();
|
||||
|
||||
public bool IsValid { get; set; } = false;
|
||||
/// <summary>
|
||||
/// The URI to navigate to if cancel is pressed
|
||||
/// </summary>
|
||||
public string? BackLink { get; set; } = "/";
|
||||
|
||||
public string? ForwardLink { get; set; } = "/";
|
||||
|
||||
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 OnFilesUploaded(List<FilePathListing> args)
|
||||
{
|
||||
_filePathListings = args;
|
||||
StateHasChanged();
|
||||
await OnChanged();
|
||||
}
|
||||
|
||||
private async Task OnClearFilesClicked()
|
||||
{
|
||||
_filePathListings = [];
|
||||
StateHasChanged();
|
||||
await OnChanged();
|
||||
}
|
||||
|
||||
private void PublishClicked(MouseEventArgs args)
|
||||
{
|
||||
var validationContext = new ValidationContext(Model);
|
||||
var validationResult = new List<ValidationResult>();
|
||||
|
||||
IsValid = Validator.TryValidateObject(Model, validationContext, validationResult);
|
||||
/*
|
||||
if (ForwardLink is not null)
|
||||
{
|
||||
if (IsValid)
|
||||
{
|
||||
NavigationManager.NavigateTo(ForwardLink);
|
||||
}
|
||||
else
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentNullException("No forward link provided for the add archive item page.");
|
||||
}
|
||||
|
||||
// TODO: assign parents to all file path listings
|
||||
throw new NotImplementedException();
|
||||
*/
|
||||
}
|
||||
|
||||
async Task OnChanged()
|
||||
{
|
||||
var validationContext = new ValidationContext(Model);
|
||||
var validationResult = new List<ValidationResult>();
|
||||
|
||||
IsValid = Validator.TryValidateObject(Model, validationContext, validationResult);
|
||||
}
|
||||
|
||||
async Task OnCategoryChanged()
|
||||
{
|
||||
List<ArchiveCategory>? newCategory = await CategoryProvider.GetArchiveCategory(Model.Category);
|
||||
|
||||
if (newCategory.Count != 1)
|
||||
{
|
||||
throw new ArgumentException(nameof(Model.Category), $"Got {newCategory.Count} rows for category name={Model.Category}");
|
||||
}
|
||||
if (newCategory is not null)
|
||||
{
|
||||
_identifierTextBox.VerifyFormatCategory = newCategory[0];
|
||||
_isFormDivVisible = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
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<string>> 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) ?? []));
|
||||
}
|
||||
|
||||
List<string> categoryStrings = [];
|
||||
foreach (var category in categories)
|
||||
{
|
||||
categoryStrings.Add(category.Name);
|
||||
}
|
||||
|
||||
return categoryStrings;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,308 +0,0 @@
|
||||
@page "/add"
|
||||
@using OpenArchival.Blazor.Components.CustomComponents;
|
||||
@using OpenArchival.Blazor.Components.Pages.Administration.Categories
|
||||
@using OpenArchival.Core
|
||||
|
||||
<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>
|
||||
@if (!IsValid && _isFormDivVisible)
|
||||
{
|
||||
<MudAlert Severity="Severity.Error" Class="mt-4">
|
||||
All identifier fields must be filled in.
|
||||
</MudAlert>
|
||||
}
|
||||
|
||||
@* Archive item category *@
|
||||
<MudGrid Justify="Justify.Center" Class="pt-4">
|
||||
<MudItem>
|
||||
<MudAutocomplete T="string" 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>
|
||||
|
||||
<div @ref="_formDiv" style="@_formDivStyle">
|
||||
@* ID Creation *@
|
||||
<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.IdentifierFields"></IdentifierTextBox>
|
||||
|
||||
@* Title *@
|
||||
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Archive Item Title</MudText>
|
||||
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
||||
<MudTextField Required=true Placeholder="Archive Item Title" T="string" Class="pl-4 pr-4" @bind-Value=Model.Title @bind-Value:after=OnChanged></MudTextField>
|
||||
|
||||
@* Description *@
|
||||
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Item Description</MudText>
|
||||
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
||||
<MudTextField Lines=8 Placeholder="Description" T="string" Class="pl-4 pr-4" @bind-Value=Model.Description @bind-Value:after=OnChanged></MudTextField>
|
||||
|
||||
@* Storage Location *@
|
||||
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Storage Location</MudText>
|
||||
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
||||
<MudAutocomplete T="string" Label="Storage Location" Class="pt-0 mt-0 pl-2 pr-2" @bind-Value=Model.StorageLocation @bind-Value:after=OnChanged CoerceText=false CoerceValue=false></MudAutocomplete>
|
||||
|
||||
@* Artifact Type *@
|
||||
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Artifact Type</MudText>
|
||||
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
||||
<MudAutocomplete T="string" Label="Artifact Type" Class="pt-0 mt-0 pl-2 pr-2" @bind-Value=Model.ArtifactType @bind-Value:after=OnChanged SearchFunc="SearchItemTypes"></MudAutocomplete>
|
||||
|
||||
@* Tags *@
|
||||
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Tags</MudText>
|
||||
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
||||
<ChipTagInput @bind-Items=Model.Tags OnChanged="OnChanged" AutocompleteSearchFunc="SearchTags" InputType="ChipTagInput.ChipTagInputType.AutoComplete" Placeholder="Add tags..."></ChipTagInput>
|
||||
|
||||
@* Names *@
|
||||
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Listed Names</MudText>
|
||||
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
||||
<ChipTagInput @bind-Items=Model.AssociatedNames OnChanged="OnChanged" AutocompleteSearchFunc="SearchListedNames" InputType="ChipTagInput.ChipTagInputType.AutoComplete" Placeholder="Add names..."></ChipTagInput>
|
||||
|
||||
@* Associated Dates *@
|
||||
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Associated Dates</MudText>
|
||||
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
||||
<ChipTagInput @bind-Items=DatesData OnChanged="OnChanged"></ChipTagInput>
|
||||
|
||||
@* Defects *@
|
||||
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Defects</MudText>
|
||||
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
||||
<ChipTagInput @bind-Items=Model.Defects OnChanged="OnChanged" AutocompleteSearchFunc="SearchDefects" InputType="ChipTagInput.ChipTagInputType.AutoComplete" Placeholder="Add defects..."></ChipTagInput>
|
||||
|
||||
@* Related Artifacts *@
|
||||
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Related Artifacts</MudText>
|
||||
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
||||
<ChipTagInput @bind-Items=Model.RelatedArtifacts OnChanged="OnChanged"></ChipTagInput>
|
||||
|
||||
@* Files *@
|
||||
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Artifact Documents</MudText>
|
||||
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
||||
<UploadDropBox></UploadDropBox>
|
||||
|
||||
@* Submit Buttons *@
|
||||
<MudGrid Justify="Justify.FlexEnd" Class="pt-6">
|
||||
<MudItem>
|
||||
<MudCheckBox Label="Publicly Visible" T="bool" @bind-Value=Model.IsPublic></MudCheckBox>
|
||||
</MudItem>
|
||||
|
||||
<MudItem>
|
||||
<MudButton Color="Color.Primary" Variant="Variant.Filled" Class="ml-4" OnClick="CancelClicked">Cancel</MudButton>
|
||||
</MudItem>
|
||||
|
||||
<MudItem>
|
||||
<MudButton Color="Color.Primary" Variant="Variant.Filled" Class="ml-4" OnClick="PublishClicked">Publish</MudButton>
|
||||
</MudItem>
|
||||
|
||||
</MudGrid>
|
||||
</div>
|
||||
</MudPaper>
|
||||
|
||||
@using OpenArchival.Database
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@inject IDialogService DialogService
|
||||
@inject ICategoryProvider CategoryProvider
|
||||
@inject IArchiveStorageLocationProvider StorageLocationProvider
|
||||
@inject IArtifactTypesProvider ArtifactTypesProvider
|
||||
@inject ITagsProvider TagsProvider;
|
||||
@inject IArtifactAssociatedNamesProvider AssociatedNamesProvider;
|
||||
@inject IDefectsProvider DefectsProvider;
|
||||
@inject NavigationManager NavigationManager;
|
||||
|
||||
@code {
|
||||
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<Category> Categories { get; set; } = new();
|
||||
|
||||
private bool _categorySelected = false;
|
||||
|
||||
//public List<IdentifierFieldValidationModel> IdentifierFields { get; set; } = [new IdentifierFieldValidationModel() { Name = "Field One", Value = "" }, new IdentifierFieldValidationModel() { Name = "Field Two", Value = "" }, new IdentifierFieldValidationModel() { Name = "Field Three", Value = "" }];
|
||||
|
||||
public ArchiveItemValidationModel Model { get; set; } = new();
|
||||
|
||||
public bool IsValid { get; set; } = false;
|
||||
/// <summary>
|
||||
/// The URI to navigate to if cancel is pressed
|
||||
/// </summary>
|
||||
public string? BackLink { get; set; } = "/";
|
||||
|
||||
public string? ForwardLink { get; set; } = "/";
|
||||
|
||||
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 void PublishClicked(MouseEventArgs args)
|
||||
{
|
||||
var validationContext = new ValidationContext(Model);
|
||||
var validationResult = new List<ValidationResult>();
|
||||
|
||||
IsValid = Validator.TryValidateObject(Model, validationContext, validationResult);
|
||||
if (ForwardLink is not null)
|
||||
{
|
||||
if (IsValid)
|
||||
{
|
||||
NavigationManager.NavigateTo(ForwardLink);
|
||||
}
|
||||
else
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentNullException("No forward link provided for the add archive item page.");
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
void OnChanged()
|
||||
{
|
||||
var validationContext = new ValidationContext(Model);
|
||||
var validationResult = new List<ValidationResult>();
|
||||
|
||||
IsValid = Validator.TryValidateObject(Model, validationContext, validationResult);
|
||||
}
|
||||
|
||||
async Task OnCategoryChanged()
|
||||
{
|
||||
Category? newCategory = await CategoryProvider.GetCategoryAsync(Model.Category);
|
||||
if (newCategory is not null)
|
||||
{
|
||||
_identifierTextBox.VerifyFormatCategory = newCategory;
|
||||
_isFormDivVisible = true;
|
||||
}
|
||||
|
||||
if (!_categorySelected)
|
||||
{
|
||||
_categorySelected = true;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<string>> SearchDefects(string value, CancellationToken cancellationToken)
|
||||
{
|
||||
List<string> defects;
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
defects = new(await DefectsProvider.TopDefects(25));
|
||||
}
|
||||
else
|
||||
{
|
||||
defects = new(await DefectsProvider.SearchDefects(value));
|
||||
}
|
||||
|
||||
return defects;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<string>> SearchListedNames(string value, CancellationToken cancellationToken)
|
||||
{
|
||||
List<string> names;
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
names = new(await AssociatedNamesProvider.TopNames(25));
|
||||
}
|
||||
else
|
||||
{
|
||||
names = new(await AssociatedNamesProvider.SearchNames(value));
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<string>> SearchTags(string value, CancellationToken cancellationToken)
|
||||
{
|
||||
List<string> tags;
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
tags = new(await TagsProvider.TopTags(25));
|
||||
}
|
||||
else
|
||||
{
|
||||
tags = new(await TagsProvider.SearchTags(value));
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<string>> SearchItemTypes(string value, CancellationToken cancellationToken)
|
||||
{
|
||||
List<string> itemTypes;
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
itemTypes = new(await ArtifactTypesProvider.TopTypes(25));
|
||||
}
|
||||
else
|
||||
{
|
||||
itemTypes = new(await ArtifactTypesProvider.SearchTypes(value));
|
||||
}
|
||||
|
||||
return itemTypes;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<string>> SearchStorageLocation(string value, CancellationToken cancellationToken)
|
||||
{
|
||||
List<string> storageLocations;
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
storageLocations = new(await StorageLocationProvider.TopLocations(25));
|
||||
}
|
||||
else
|
||||
{
|
||||
storageLocations = new(await StorageLocationProvider.SearchLocations(value));
|
||||
}
|
||||
|
||||
return storageLocations;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<string>> SearchCategory(string value, CancellationToken cancellationToken)
|
||||
{
|
||||
List<Category> categories;
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
categories = new(await CategoryProvider.TopCategories(25));
|
||||
}
|
||||
else
|
||||
{
|
||||
categories = new(await CategoryProvider.SearchCategories(value));
|
||||
}
|
||||
|
||||
List<string> categoryStrings = [];
|
||||
foreach (var category in categories)
|
||||
{
|
||||
categoryStrings.Add(category.CategoryName);
|
||||
}
|
||||
|
||||
return categoryStrings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
@using OpenArchival.Blazor.Components.CustomComponents;
|
||||
|
||||
<MudPaper Class="pa-4 ma-2 rounded" Elevation="3">
|
||||
<h3>@FilePath.OriginalName</h3>
|
||||
|
||||
<MudText Typo="Typo.h6" Color="Color.Primary" Class="pt-4 pb-0">Archive Item Title</MudText>
|
||||
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
||||
<MudTextField 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">Item Description</MudText>
|
||||
<MudDivider DividerType="DividerType.Middle"></MudDivider>
|
||||
<MudTextField 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 T="string" Label="Storage Location" Class="pt-0 mt-0 pl-2 pr-2" @bind-Value=Model.StorageLocation @bind-Value:after=OnInputsChanged SearchFunc="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 T="string" Label="Artifact Type" Class="pt-0 mt-0 pl-2 pr-2" @bind-Value=Model.ArtifactType @bind-Value:after=OnInputsChanged SearchFunc="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="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>
|
||||
<InputContent>
|
||||
<MudAutocomplete
|
||||
T="string"
|
||||
SearchFunc="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" DisplayFunc="date=>date.ToShortDateString()">
|
||||
<InputContent>
|
||||
<MudDatePicker @bind-Date=_associatedDateInputValue>
|
||||
</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>
|
||||
<InputContent>
|
||||
<MudAutocomplete
|
||||
T="string"
|
||||
SearchFunc="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="ArtifactGrouping" @ref="_assocaitedArtifactsChipContainer" DisplayFunc="artifact => artifact.ArtifactGroupingIdentifier">
|
||||
<InputContent>
|
||||
<MudAutocomplete
|
||||
T="ArtifactGrouping"
|
||||
OnInternalInputChanged="OnInputsChanged"
|
||||
Value="_associatedArtifactValue"
|
||||
ValueChanged="OnAssociatedArtifactChanged"
|
||||
OnKeyDown="@(EventArgs=>HandleChipContainerEnter<ArtifactGrouping>(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=_artifactTextContent ValueChanged="OnArtifactTextContentChanged"></MudTextField>
|
||||
</MudPaper>
|
||||
|
||||
@inject IArtifactDefectProvider DefectsProvider;
|
||||
@inject IArtifactStorageLocationProvider StorageLocationProvider;
|
||||
@inject IArchiveEntryTagProvider TagsProvider;
|
||||
@inject IArtifactTypeProvider TypesProvider;
|
||||
@inject IListedNameProvider ListedNameProvider;
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public required FilePathListing FilePath { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback OnValueChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public required ArtifactEntryValidationModel Model { get; set; } = new(){StorageLocation="hello", Title="Hello"};
|
||||
|
||||
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<ArtifactGrouping> _assocaitedArtifactsChipContainer;
|
||||
|
||||
private ArtifactGrouping? _associatedArtifactValue = null;
|
||||
|
||||
private string _artifactTextContent = "";
|
||||
|
||||
public Task OnInputsChanged()
|
||||
{
|
||||
return OnValueChanged.InvokeAsync();
|
||||
}
|
||||
|
||||
private Task OnDefectsValueChanged(string text)
|
||||
{
|
||||
_defectsInputValue = text;
|
||||
return OnValueChanged.InvokeAsync();
|
||||
}
|
||||
|
||||
private Task OnTagsInputTextChanged(string text)
|
||||
{
|
||||
_tagsInputValue = text;
|
||||
return OnValueChanged.InvokeAsync();
|
||||
}
|
||||
|
||||
private Task OnListedNamesTextChanged(string text)
|
||||
{
|
||||
_listedNamesInputValue = text;
|
||||
return OnValueChanged.InvokeAsync();
|
||||
}
|
||||
|
||||
private Task OnAssociatedArtifactChanged(ArtifactGrouping grouping)
|
||||
{
|
||||
if (grouping is not null)
|
||||
{
|
||||
_associatedArtifactValue = grouping;
|
||||
return OnValueChanged.InvokeAsync();
|
||||
}
|
||||
|
||||
return OnValueChanged.InvokeAsync();
|
||||
}
|
||||
|
||||
private Task OnArtifactTextContentChanged(string value)
|
||||
{
|
||||
return OnValueChanged.InvokeAsync();
|
||||
}
|
||||
|
||||
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 OnValueChanged.InvokeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task HandleAssociatedDateChipContainerAdd(MouseEventArgs args)
|
||||
{
|
||||
if (_associatedDateInputValue is not null)
|
||||
{
|
||||
await _assocaitedDatesChipContainer.AddItem((DateTime)_associatedDateInputValue);
|
||||
_associatedDateInputValue = default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task<IEnumerable<string>> SearchDefects(string value, CancellationToken cancellationToken)
|
||||
{
|
||||
List<string> defects;
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
defects = new((await DefectsProvider.Top(25) ?? []).Select(prop => prop.Description));
|
||||
}
|
||||
else
|
||||
{
|
||||
defects = new((await DefectsProvider.Search(value) ?? []).Select(prop => prop.Description));
|
||||
}
|
||||
|
||||
return defects;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<string>> SearchStorageLocation(string value, CancellationToken cancellationToken)
|
||||
{
|
||||
List<string> storageLocations;
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
storageLocations = new((await StorageLocationProvider.Top(25) ?? []).Select(prop => prop.Location));
|
||||
}
|
||||
else
|
||||
{
|
||||
storageLocations = new((await StorageLocationProvider.Search(value) ?? []).Select(prop => prop.Location));
|
||||
}
|
||||
|
||||
return storageLocations;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<string>> SearchTags(string value, CancellationToken cancellationToken)
|
||||
{
|
||||
List<string> tags;
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
tags = new((await TagsProvider.Top(25) ?? []).Select(prop => prop.Name));
|
||||
}
|
||||
else
|
||||
{
|
||||
tags = new((await TagsProvider.Search(value) ?? []).Select(prop => prop.Name));
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<string>> SearchItemTypes(string value, CancellationToken cancellationToken)
|
||||
{
|
||||
List<string> itemTypes;
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
itemTypes = new((await TypesProvider.Top(25) ?? []).Select(prop => prop.Name));
|
||||
}
|
||||
else
|
||||
{
|
||||
itemTypes = new((await TypesProvider.Search(value) ?? []).Select(prop => prop.Name));
|
||||
}
|
||||
|
||||
return itemTypes;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<string>> SearchListedNames(string value, CancellationToken cancellationToken)
|
||||
{
|
||||
List<string> names;
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
names = new((await ListedNameProvider.Top(25) ?? []).Select(prop=>$"{prop.FirstName} {prop.LastName}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
names = new((await ListedNameProvider.Search(value) ?? []).Select(prop=>$"{prop.FirstName} {prop.LastName}"));
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<MudItem Class="pt-6">
|
||||
<MudTextField Label="@field.Name"
|
||||
@bind-Value="field.Value"
|
||||
@bind-Value="@field.Value"
|
||||
@bind-Value:after="OnInputChanged"
|
||||
DebounceInterval="100"
|
||||
Required=true/>
|
||||
@@ -32,8 +32,8 @@
|
||||
}
|
||||
</MudGrid>
|
||||
|
||||
@using OpenArchival.Database;
|
||||
@inject ICategoryProvider CategoryProvider;
|
||||
@using OpenArchival.DataAccess;
|
||||
@inject IArchiveCategoryProvider CategoryProvider;
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
@@ -50,8 +50,8 @@
|
||||
[Parameter]
|
||||
public EventCallback<string> ValueChanged { get; set; }
|
||||
|
||||
private Category _verifyFormatCategory;
|
||||
public Category? VerifyFormatCategory
|
||||
private ArchiveCategory _verifyFormatCategory;
|
||||
public ArchiveCategory? VerifyFormatCategory
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -63,9 +63,9 @@
|
||||
{
|
||||
_identifierFields.Clear();
|
||||
_verifyFormatCategory = value;
|
||||
foreach (var field in value.FieldsIterator)
|
||||
foreach (var field in value.FieldNames)
|
||||
{
|
||||
_identifierFields.Add(new IdentifierFieldValidationModel() {Name=field.Key, Value=""});
|
||||
_identifierFields.Add(new IdentifierFieldValidationModel() {Name=field, Value=""});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using OpenArchival.Core;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using OpenArchival.DataAccess;
|
||||
|
||||
namespace OpenArchival.Blazor;
|
||||
|
||||
@@ -34,19 +34,4 @@ public class ArchiveItemValidationModel
|
||||
|
||||
public bool IsPublic { get; set; } = true;
|
||||
|
||||
public ArchiveItem ToArchiveItem(Category category)
|
||||
{
|
||||
return new ArchiveItem() {
|
||||
ArtifactType = ArtifactType,
|
||||
Category = category,
|
||||
Defects = Defects,
|
||||
Description = Description,
|
||||
AssociatedDates = AssociatedDates,
|
||||
ItemTitle = Title,
|
||||
ListedNames = AssociatedNames,
|
||||
StorageLocation = StorageLocation,
|
||||
Tags = Tags,
|
||||
IsPublic = IsPublic
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
namespace OpenArchival.Blazor;
|
||||
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using OpenArchival.DataAccess;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
public class ArtifactEntryValidationModel : IValidatableObject
|
||||
{
|
||||
[Required(AllowEmptyStrings = false, ErrorMessage = "An artifact numbering must be supplied")]
|
||||
public string? ArtifactNumber { get; set; }
|
||||
|
||||
[Required(AllowEmptyStrings = false, ErrorMessage = "A title must be provided")]
|
||||
public required string Title { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public string? Type { get; set; }
|
||||
|
||||
public required string? StorageLocation { get; set; }
|
||||
|
||||
public List<string>? Tags { get; set; } = [];
|
||||
|
||||
public List<string>? ListedNames { get; set; } = [];
|
||||
|
||||
public List<DateTime>? AssociatedDates { get; set; } = [];
|
||||
|
||||
public List<string>? Defects { get; set; } = [];
|
||||
|
||||
public List<string>? Links { get; set; } = [];
|
||||
|
||||
public string? ArtifactType { get; set; }
|
||||
|
||||
public List<FilePathListing>? Files { get; set; } = [];
|
||||
|
||||
public Dictionary<string, string>? FileTextContent { get; set; } = [];
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext context)
|
||||
{
|
||||
if (Links.IsNullOrEmpty() && Files.IsNullOrEmpty())
|
||||
{
|
||||
yield return new ValidationResult(
|
||||
"Either uploaded files or add content links",
|
||||
new[] {nameof(Links), nameof(Files)}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPublicallyVisible { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using OpenArchival.DataAccess;
|
||||
|
||||
namespace OpenArchival.Blazor;
|
||||
|
||||
public class ArtifactGroupingValidationModel
|
||||
{
|
||||
public required ArchiveCategory Category { get; set; }
|
||||
|
||||
public List<string>? IdentifierFieldValues { get; set; }
|
||||
|
||||
public List<ArtifactEntryValidationModel>? ArtifactEntries { get; set; }
|
||||
|
||||
public bool IsPublicallyVisible { get; set; }
|
||||
}
|
||||
@@ -1,44 +1,61 @@
|
||||
@using OpenArchival.Database
|
||||
@using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@inject ICategoryProvider CategoryProvider;
|
||||
@page "/categorieslist"
|
||||
|
||||
<MudPaper Class="pa-4 ma-2 rounded" Elevation="3">
|
||||
<MudText Typo="Typo.h6">Categories</MudText>
|
||||
<MudDivider></MudDivider>
|
||||
<MudList T="string">
|
||||
@foreach (Category category in _categories)
|
||||
@foreach (ArchiveCategory category in _categories)
|
||||
{
|
||||
<MudListItem Text=@category.CategoryName OnClick="@(() => OnCategoryItemClicked(category.CategoryName))"></MudListItem>
|
||||
<MudListItem Text=@category.Name OnClick="@(() => OnCategoryItemClicked(category))"></MudListItem>
|
||||
}
|
||||
</MudList>
|
||||
@ChildContent
|
||||
</MudPaper>
|
||||
|
||||
@inject IArchiveCategoryProvider CategoryProvider;
|
||||
@inject ILogger<CategoriesListComponent> Logger;
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
public RenderFragment ChildContent { get; set; } = default!;
|
||||
|
||||
private List<ArchiveCategory> _categories = new();
|
||||
|
||||
public List<Category> _categories = new();
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<string> ListItemClickedCallback { get; set; }
|
||||
public EventCallback<ArchiveCategory> ListItemClickedCallback { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var categories = await CategoryProvider.AllCategories();
|
||||
|
||||
var categories = await CategoryProvider.GetAllArchiveCategories();
|
||||
if (categories is null)
|
||||
{
|
||||
Logger.LogError("There were no categories in the database when attempting to load the list of categories.");
|
||||
return;
|
||||
}
|
||||
|
||||
_categories.AddRange(categories);
|
||||
}
|
||||
|
||||
public async Task RefreshData()
|
||||
{
|
||||
_categories.Clear();
|
||||
var categories = await CategoryProvider.AllCategories();
|
||||
var categories = await CategoryProvider.GetAllArchiveCategories();
|
||||
|
||||
if (categories is null)
|
||||
{
|
||||
Logger.LogError("There were no categories in the database when attempting to load the list of categories.");
|
||||
return;
|
||||
}
|
||||
|
||||
_categories.AddRange(categories);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
protected async Task OnCategoryItemClicked(string categoryName)
|
||||
protected async Task OnCategoryItemClicked(ArchiveCategory category)
|
||||
{
|
||||
await ListItemClickedCallback.InvokeAsync(categoryName);
|
||||
await ListItemClickedCallback.InvokeAsync(category);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
@using System.ComponentModel.DataAnnotations;
|
||||
@using OpenArchival.Core;
|
||||
@using OpenArchival.Database;
|
||||
@using OpenArchival.DataAccess;
|
||||
|
||||
<MudDialog>
|
||||
<TitleContent>
|
||||
@@ -8,8 +7,8 @@
|
||||
</TitleContent>
|
||||
<DialogContent>
|
||||
<MudForm @ref="_form">
|
||||
<MudTextField @bind-Value="Model.Name"
|
||||
For="@(() => Model.Name)"
|
||||
<MudTextField @bind-Value="ValidationModel.Name"
|
||||
For="@(() => ValidationModel.Name)"
|
||||
Label="Category Name"
|
||||
Variant="Variant.Filled" />
|
||||
|
||||
@@ -23,9 +22,9 @@
|
||||
<MudText Type="Typo.body2" Color="Color.Primary">@FormatPreview</MudText>
|
||||
</MudStack>
|
||||
|
||||
<MudTextField @bind-Value="Model.FieldSeparator"
|
||||
<MudTextField @bind-Value="ValidationModel.FieldSeparator"
|
||||
@bind-Value:after="UpdateFormatPreview"
|
||||
For="@(() => Model.FieldSeparator)"
|
||||
For="@(() => ValidationModel.FieldSeparator)"
|
||||
Label="Field Separator"
|
||||
Variant="Variant.Filled"
|
||||
MaxLength="1" />
|
||||
@@ -33,7 +32,7 @@
|
||||
<MudDivider Class="pt-4" />
|
||||
|
||||
<MudNumericField
|
||||
Value="Model.NumFields"
|
||||
Value="ValidationModel.NumFields"
|
||||
ValueChanged="@((int newCount) => OnNumFieldsChanged(newCount))"
|
||||
Label="Number of fields in the item identifiers"
|
||||
Variant="Variant.Filled"
|
||||
@@ -42,14 +41,14 @@
|
||||
<MudDivider Class="pt-4" />
|
||||
|
||||
<MudGrid Class="pr-2 pt-2 pb-2 pl-8" Justify="Justify.FlexStart" Spacing="3">
|
||||
@for (int index = 0; index < Model.FieldNames.Count; ++index)
|
||||
@for (int index = 0; index < ValidationModel.FieldNames.Count; ++index)
|
||||
{
|
||||
var localIndex = index;
|
||||
|
||||
<MudItem xs="12" sm="6" md="6">
|
||||
<CategoryFieldCardComponent Index="localIndex"
|
||||
FieldName="@Model.FieldNames[localIndex]"
|
||||
FieldDescription="@Model.FieldDescriptions[localIndex]"
|
||||
FieldName="@ValidationModel.FieldNames[localIndex]"
|
||||
FieldDescription="@ValidationModel.FieldDescriptions[localIndex]"
|
||||
OnNameUpdate="HandleNameUpdate"
|
||||
OnDescriptionUpdate="HandleDescriptionUpdate"/>
|
||||
</MudItem>
|
||||
@@ -63,14 +62,14 @@
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
|
||||
@inject ICategoryProvider CategoryProvider;
|
||||
@inject IArchiveCategoryProvider CategoryProvider;
|
||||
|
||||
@code {
|
||||
[CascadingParameter]
|
||||
private IMudDialogInstance MudDialog { get; set; } = default!;
|
||||
|
||||
[Parameter]
|
||||
public CategoryValidationModel Model { get; set; } = default!;
|
||||
public CategoryValidationModel ValidationModel { get; set; } = default!;
|
||||
|
||||
[Parameter]
|
||||
public bool IsUpdate { get; set; }
|
||||
@@ -83,38 +82,43 @@
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
Model ??= new CategoryValidationModel { NumFields = 1 };
|
||||
UpdateStateFromModel();
|
||||
if (ValidationModel is null)
|
||||
{
|
||||
ValidationModel = new CategoryValidationModel { NumFields = 1 };
|
||||
} else
|
||||
{
|
||||
ValidationModel.NumFields = ValidationModel.FieldNames.Count;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnNumFieldsChanged(int newCount)
|
||||
{
|
||||
if (newCount < 1) return; // Prevent invalid counts
|
||||
Model.NumFields = newCount;
|
||||
if (newCount < 1) return;
|
||||
ValidationModel.NumFields = newCount;
|
||||
UpdateStateFromModel();
|
||||
}
|
||||
|
||||
private void UpdateStateFromModel()
|
||||
{
|
||||
Model.FieldNames ??= new List<string>();
|
||||
Model.FieldDescriptions ??= new List<string>();
|
||||
ValidationModel.FieldNames ??= new List<string>();
|
||||
ValidationModel.FieldDescriptions ??= new List<string>();
|
||||
|
||||
while (Model.FieldNames.Count < Model.NumFields)
|
||||
while (ValidationModel.FieldNames.Count < ValidationModel.NumFields)
|
||||
{
|
||||
Model.FieldNames.Add($"Field {Model.FieldNames.Count + 1}");
|
||||
ValidationModel.FieldNames.Add($"Field {ValidationModel.FieldNames.Count + 1}");
|
||||
}
|
||||
while (Model.FieldNames.Count > Model.NumFields)
|
||||
while (ValidationModel.FieldNames.Count > ValidationModel.NumFields)
|
||||
{
|
||||
Model.FieldNames.RemoveAt(Model.FieldNames.Count - 1);
|
||||
ValidationModel.FieldNames.RemoveAt(ValidationModel.FieldNames.Count - 1);
|
||||
}
|
||||
|
||||
while (Model.FieldDescriptions.Count < Model.NumFields)
|
||||
while (ValidationModel.FieldDescriptions.Count < ValidationModel.NumFields)
|
||||
{
|
||||
Model.FieldDescriptions.Add("");
|
||||
ValidationModel.FieldDescriptions.Add("");
|
||||
}
|
||||
while (Model.FieldDescriptions.Count > Model.NumFields)
|
||||
while (ValidationModel.FieldDescriptions.Count > ValidationModel.NumFields)
|
||||
{
|
||||
Model.FieldDescriptions.RemoveAt(Model.FieldDescriptions.Count - 1);
|
||||
ValidationModel.FieldDescriptions.RemoveAt(ValidationModel.FieldDescriptions.Count - 1);
|
||||
}
|
||||
|
||||
UpdateFormatPreview();
|
||||
@@ -123,8 +127,8 @@
|
||||
|
||||
private void UpdateFormatPreview()
|
||||
{
|
||||
var fieldNames = Model.FieldNames.Select(name => string.IsNullOrEmpty(name) ? "<...>" : $"<{name}>");
|
||||
FormatPreview = string.Join(Model.FieldSeparator, fieldNames);
|
||||
var fieldNames = ValidationModel.FieldNames.Select(name => string.IsNullOrEmpty(name) ? "<...>" : $"<{name}>");
|
||||
FormatPreview = string.Join(ValidationModel.FieldSeparator, fieldNames);
|
||||
}
|
||||
|
||||
private async Task Submit()
|
||||
@@ -132,17 +136,7 @@
|
||||
await _form.Validate();
|
||||
if (!_form.IsValid) return;
|
||||
|
||||
var categoryToSave = Model.ToCategory();
|
||||
if (IsUpdate)
|
||||
{
|
||||
int lines = await CategoryProvider.UpdateCategoryAsync(OriginalName, categoryToSave);
|
||||
Console.WriteLine($"{lines} effected");
|
||||
}
|
||||
else
|
||||
{
|
||||
await CategoryProvider.InsertCategoryAsync(categoryToSave);
|
||||
}
|
||||
MudDialog.Close(DialogResult.Ok(Model.ToCategory()));
|
||||
MudDialog.Close(DialogResult.Ok(ValidationModel));
|
||||
}
|
||||
|
||||
private void Cancel() => MudDialog.Cancel();
|
||||
@@ -151,18 +145,18 @@
|
||||
|
||||
private void HandleNameUpdate((int Index, string NewValue) data)
|
||||
{
|
||||
if (data.Index < Model.FieldNames.Count)
|
||||
if (data.Index < ValidationModel.FieldNames.Count)
|
||||
{
|
||||
Model.FieldNames[data.Index] = data.NewValue;
|
||||
ValidationModel.FieldNames[data.Index] = data.NewValue;
|
||||
UpdateFormatPreview(); // Update the preview in real-time
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDescriptionUpdate((int Index, string NewValue) data)
|
||||
{
|
||||
if (data.Index < Model.FieldDescriptions.Count)
|
||||
if (data.Index < ValidationModel.FieldDescriptions.Count)
|
||||
{
|
||||
Model.FieldDescriptions[data.Index] = data.NewValue;
|
||||
ValidationModel.FieldDescriptions[data.Index] = data.NewValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
@* CategoryFieldCardComponent.razor *@
|
||||
|
||||
<MudCard Outlined="true">
|
||||
<MudCard Outlined="true">
|
||||
<MudCardContent>
|
||||
<MudTextField @bind-Value="FieldName"
|
||||
@bind-Value:after="OnNameChanged"
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
public class CategoryFieldValidationModel
|
||||
{
|
||||
[Required(ErrorMessage = "A field name must be provided.")]
|
||||
public string FieldName { get; set; } = "";
|
||||
|
||||
public string Description { get; set; } = "";
|
||||
}
|
||||
@@ -1,10 +1,19 @@
|
||||
using OpenArchival.Core;
|
||||
namespace OpenArchival.Blazor;
|
||||
|
||||
using Microsoft.IdentityModel.Abstractions;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using OpenArchival.DataAccess;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
public class CategoryValidationModel
|
||||
{
|
||||
public int? DatabaseId { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "Category name is required.")]
|
||||
public string Name { get; set; }
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "Field separator is required.")]
|
||||
[StringLength(1, ErrorMessage = "Separator must be a single character.")]
|
||||
@@ -16,14 +25,50 @@ public class CategoryValidationModel
|
||||
|
||||
public List<string> FieldNames { get; set; } = [""];
|
||||
|
||||
public List<string> FieldDescriptions { get; set; } = [""];
|
||||
public List<string> FieldDescriptions { get; set; } = [""];
|
||||
|
||||
public Category ToCategory()
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext context)
|
||||
{
|
||||
return new Category() { CategoryName = Name, FieldSeparator = FieldSeparator, FieldNames = FieldNames.ToArray(), FieldDescriptions = FieldDescriptions.ToArray() };
|
||||
if (FieldNames.IsNullOrEmpty() || FieldDescriptions.IsNullOrEmpty())
|
||||
{
|
||||
yield return new ValidationResult(
|
||||
"Either the FieldNames or FieldDescriptions were null or empty. At least one is required",
|
||||
new[] { nameof(FieldNames), nameof(FieldDescriptions) }
|
||||
);
|
||||
}
|
||||
}
|
||||
public static CategoryValidationModel FromCategory(Category category)
|
||||
|
||||
public static CategoryValidationModel FromArchiveCategory(ArchiveCategory category)
|
||||
{
|
||||
return new CategoryValidationModel() { Name = category.CategoryName, FieldSeparator=category.FieldSeparator, NumFields=category.FieldNames.Length, FieldNames = new(category.FieldNames), FieldDescriptions = new(category.FieldDescriptions)};
|
||||
return new CategoryValidationModel()
|
||||
{
|
||||
Name = category.Name,
|
||||
Description = category.Description,
|
||||
DatabaseId = category.Id,
|
||||
FieldSeparator = category.FieldSeparator,
|
||||
FieldNames = category.FieldNames,
|
||||
FieldDescriptions = category.FieldDescriptions,
|
||||
};
|
||||
}
|
||||
|
||||
public static ArchiveCategory ToArchiveCategory(CategoryValidationModel model)
|
||||
{
|
||||
return new ArchiveCategory()
|
||||
{
|
||||
Name = model.Name,
|
||||
FieldSeparator = model.FieldSeparator,
|
||||
Description = model.Description,
|
||||
FieldNames = model.FieldNames,
|
||||
FieldDescriptions = model.FieldDescriptions
|
||||
};
|
||||
}
|
||||
|
||||
public static void UpdateArchiveValidationModel(CategoryValidationModel model, ArchiveCategory category)
|
||||
{
|
||||
category.Name = model.Name ?? throw new ArgumentNullException(nameof(model.Name), "The model name was null.");
|
||||
category.Description = model.Description;
|
||||
category.FieldSeparator = model.FieldSeparator;
|
||||
category.FieldNames = model.FieldNames;
|
||||
category.FieldDescriptions = model.FieldDescriptions;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
@page "/categories"
|
||||
|
||||
@using OpenArchival.Core;
|
||||
@using OpenArchival.Database;
|
||||
@using OpenArchival.DataAccess;
|
||||
|
||||
@inject IDialogService DialogService
|
||||
@inject ICategoryProvider CategoryProvider;
|
||||
@inject IArchiveCategoryProvider CategoryProvider;
|
||||
@inject ArchiveDbContext Context;
|
||||
@inject ILogger<ViewAddCategoriesComponent> Logger;
|
||||
|
||||
<CategoriesListComponent @ref=_categoriesListComponent ListItemClickedCallback="ShowFilledDialog">
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="OnAddClick">Add Category</MudButton>
|
||||
@@ -14,18 +15,11 @@
|
||||
@code {
|
||||
CategoriesListComponent _categoriesListComponent = default!;
|
||||
|
||||
private async Task ShowFilledDialog(string categoryName)
|
||||
private async Task ShowFilledDialog(ArchiveCategory category)
|
||||
{
|
||||
Category? category = await CategoryProvider.GetCategoryAsync(categoryName);
|
||||
CategoryValidationModel validationModel = CategoryValidationModel.FromArchiveCategory(category);
|
||||
|
||||
if (category is null)
|
||||
{
|
||||
throw new ArgumentNullException($"The passed in categoryName={categoryName} resulted in no category in the database");
|
||||
}
|
||||
|
||||
CategoryValidationModel validationModel = CategoryValidationModel.FromCategory(category);
|
||||
|
||||
var parameters = new DialogParameters { ["Model"] = validationModel, ["IsUpdate"] = true, ["OriginalName"] = category.CategoryName};
|
||||
var parameters = new DialogParameters { ["ValidationModel"] = validationModel, ["IsUpdate"] = true, ["OriginalName"] = category.Name};
|
||||
|
||||
var options = new DialogOptions { CloseOnEscapeKey = true, BackdropClick=false};
|
||||
|
||||
@@ -34,6 +28,16 @@
|
||||
|
||||
if (result is not null && !result.Canceled && _categoriesListComponent is not null)
|
||||
{
|
||||
if (result.Data is null)
|
||||
{
|
||||
Logger.LogError($"The new category received by the result had a null data result member.");
|
||||
throw new NullReferenceException($"The new category received by the result had a null data result member.");
|
||||
}
|
||||
|
||||
CategoryValidationModel model = (CategoryValidationModel)result.Data;
|
||||
CategoryValidationModel.UpdateArchiveValidationModel(model, category);
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
StateHasChanged();
|
||||
await _categoriesListComponent.RefreshData();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user