diff --git a/songrequests_blazor.sln b/songrequests_blazor.sln index 1a29aaa..6df0166 100644 --- a/songrequests_blazor.sln +++ b/songrequests_blazor.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.9.34728.123 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "songrequests_blazor", "songrequests_blazor\songrequests_blazor.csproj", "{9036E36D-4C0B-45FD-9DE5-2C171277B6B1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "songrequests_blazor", "songrequests_blazor\songrequests_blazor.csproj", "{9036E36D-4C0B-45FD-9DE5-2C171277B6B1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/songrequests_blazor/Components/Pages/ModalComponent.razor b/songrequests_blazor/Components/Pages/ModalComponent.razor new file mode 100644 index 0000000..258a377 --- /dev/null +++ b/songrequests_blazor/Components/Pages/ModalComponent.razor @@ -0,0 +1,51 @@ + + +@if (showBackdrop) +{ + +} + +@code { + [Parameter] + public RenderFragment? Title { get; set; } + + [Parameter] + public RenderFragment? Body { get; set; } + + [Parameter] + public RenderFragment? Footer { get; set; } + + private string modalDisplay = "none;"; + private string modalClass = ""; + private bool showBackdrop = false; + + public async Task ShowAsync() + { + modalDisplay = "block;"; + await Task.Delay(100);//Delay allows bootstrap to perform nice fade animation + modalClass = "show"; + showBackdrop = true; + } + + public async Task CloseAsync() + { + modalDisplay = "none"; + await Task.Delay(250);//Delay allows bootstrap to perform nice fade animation + modalClass = ""; + showBackdrop = false; + } +} \ No newline at end of file diff --git a/songrequests_blazor/Components/Pages/Settings/Libraries.razor b/songrequests_blazor/Components/Pages/Settings/Libraries.razor new file mode 100644 index 0000000..5fd89d5 --- /dev/null +++ b/songrequests_blazor/Components/Pages/Settings/Libraries.razor @@ -0,0 +1,75 @@ +@page "/options/libraries" +@using Microsoft.EntityFrameworkCore +@using songrequests_blazor.Data +@inject Microsoft.EntityFrameworkCore.IDbContextFactory DataContextFactory +

Bibliotheken

+ +Bibliothek anlegen + + + + + Löschen bestätigen + + Willst du die Bibliothek wirklich löschen? + +
+ + +
+
+ +@code { + + List? Items { get; set; } + + private ModalComponent modal = null!; + Library? ModalItem { get; set; } + + protected override async Task OnInitializedAsync() + { + await ShowList(); + } + + private async Task ShowList() + { + using (LocalDbContext db = await DataContextFactory.CreateDbContextAsync()) + { + Items = await db.Libraries.OrderBy(l => l.Name).ToListAsync(); + } + } + + private async Task ShowDialog(Library itm) + { + ModalItem = itm; + await modal.ShowAsync(); + } + private async Task CloseDialog() + { + ModalItem = null; + await modal.CloseAsync(); + } + + private async Task RemoveItem() + { + if (ModalItem == null) return; + using (LocalDbContext db = await DataContextFactory.CreateDbContextAsync()) + { + db.Libraries.Remove(ModalItem); + await db.SaveChangesAsync(); + await ShowList(); + } + await CloseDialog(); + } +} \ No newline at end of file diff --git a/songrequests_blazor/Components/Pages/Settings/LibraryEdit.razor b/songrequests_blazor/Components/Pages/Settings/LibraryEdit.razor new file mode 100644 index 0000000..41aace0 --- /dev/null +++ b/songrequests_blazor/Components/Pages/Settings/LibraryEdit.razor @@ -0,0 +1,150 @@ +@page "/options/libraries/edit" +@page "/options/libraries/edit/{id}" +@using songrequests_blazor.Data +@inject Microsoft.EntityFrameworkCore.IDbContextFactory DataContextFactory +@inject NavigationManager nav; +

Bibliothek bearbeiten

+ + + + + +
+
+
+ + +
+
+ + +
+ @if (libItem.Type == Library.LibraryType.Local) + { +
+ +
+ + +
+ @if (AddPathMessage != null) + { + + } +
    + @if (libItem.ScanPaths != null && libItem.ScanPaths.Count > 0) + { + @foreach (var lp in libItem.ScanPaths ?? []) + { +
  • + @lp + +
  • + } + } + else + { +
  • Keine Einträge
  • + } +
+
+ } + + +
+
+
+ + +@code { + [Parameter] + public string? id { get; set; } + + const string LISTURL = "/options/libraries"; + + Library libItem { get; set; } = null!; + string? InputAddPath { get; set; } + + string? AddPathMessage { get; set; } + + private CustomValidation? customValidation; + + protected override async Task OnInitializedAsync() + { + if (id != null) + { + using (LocalDbContext db = await DataContextFactory.CreateDbContextAsync()) + { + libItem = db.Libraries.Single(lib => lib.Id == Convert.ToInt32(id)); + } + } + libItem ??= new(); + } + + private void AddPathToLibray(string path) + { + AddPathMessage = null; + if (!System.IO.Path.Exists(path)) + { + AddPathMessage = "Der angegebene Pfad existiert nicht."; + return; + } + + libItem.ScanPaths ??= new List(); + if (libItem.ScanPaths.Contains(path)) + { + AddPathMessage = "Der Pfad ist bereits in der Liste enthalten."; + return; + } + libItem.ScanPaths.Add(path); + InputAddPath = null; + } + + private void RemovePathFromLibrary(string path) + { + AddPathMessage = null; + libItem.ScanPaths?.Remove(path); + } + + private bool CustomValidate() + { + customValidation?.ClearErrors(); + var errors = new Dictionary>(); + + if (libItem.Type == Library.LibraryType.Local && (libItem.ScanPaths == null || libItem.ScanPaths?.Count == 0)) + { + errors.Add(nameof(libItem.Type), new() { "Type 'Local' is selected but no paths are specified." }); + } + if (errors.Any()) + { + customValidation?.DisplayErrors(errors); + return false; + } + else + { + return true; + } + } + + private void Cancel() + { + nav.NavigateTo(LISTURL); + } + private async Task AddLibrary() + { + if (!CustomValidate()) return; + if (libItem.Type != Library.LibraryType.Local) libItem.ScanPaths = null; + + using (LocalDbContext db = await DataContextFactory.CreateDbContextAsync()) + { + db.Libraries.Add(libItem); + await db.SaveChangesAsync(); + } + nav.NavigateTo(LISTURL); + } +} diff --git a/songrequests_blazor/Components/Pages/Settings/Sessions.razor b/songrequests_blazor/Components/Pages/Settings/Sessions.razor new file mode 100644 index 0000000..3bc25e1 --- /dev/null +++ b/songrequests_blazor/Components/Pages/Settings/Sessions.razor @@ -0,0 +1,75 @@ +@page "/options/sessions" +@using Microsoft.EntityFrameworkCore +@using songrequests_blazor.Data +@inject Microsoft.EntityFrameworkCore.IDbContextFactory DataContextFactory +

Sessions

+ +Session anlegen + +
    + @foreach (var itm in Items ?? []) + { +
  • + Erstellt am @itm.Created +
    + Bearbeiten + +
    +
  • + } +
+ + + Löschen bestätigen + + Willst du die Session wirklich löschen? + +
+ + +
+
+ +@code { + + List? Items { get; set; } + + private ModalComponent modal = null!; + Session? ModalItem { get; set; } + + protected override async Task OnInitializedAsync() + { + await ShowList(); + } + + private async Task ShowList() + { + using (LocalDbContext db = await DataContextFactory.CreateDbContextAsync()) + { + Items = await db.Sessions.OrderBy(l => l.Created).ToListAsync(); + } + } + + private async Task ShowDialog(Session itm) + { + ModalItem = itm; + await modal.ShowAsync(); + } + private async Task CloseDialog() + { + ModalItem = null; + await modal.CloseAsync(); + } + + private async Task RemoveItem() + { + if (ModalItem == null) return; + using (LocalDbContext db = await DataContextFactory.CreateDbContextAsync()) + { + db.Sessions.Remove(ModalItem); + await db.SaveChangesAsync(); + await ShowList(); + } + await CloseDialog(); + } +} \ No newline at end of file diff --git a/songrequests_blazor/CustomValidation.cs b/songrequests_blazor/CustomValidation.cs new file mode 100644 index 0000000..9327e32 --- /dev/null +++ b/songrequests_blazor/CustomValidation.cs @@ -0,0 +1,52 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Forms; + +namespace songrequests_blazor +{ + public class CustomValidation : ComponentBase + { + private ValidationMessageStore? messageStore; + + [CascadingParameter] + private EditContext? CurrentEditContext { get; set; } + + protected override void OnInitialized() + { + if (CurrentEditContext is null) + { + throw new InvalidOperationException( + $"{nameof(CustomValidation)} requires a cascading " + + $"parameter of type {nameof(EditContext)}. " + + $"For example, you can use {nameof(CustomValidation)} " + + $"inside an {nameof(EditForm)}."); + } + + messageStore = new(CurrentEditContext); + + CurrentEditContext.OnValidationRequested += (s, e) => + messageStore?.Clear(); + CurrentEditContext.OnFieldChanged += (s, e) => + messageStore?.Clear(e.FieldIdentifier); + } + + public void DisplayErrors(Dictionary> errors) + { + if (CurrentEditContext is not null) + { + foreach (var err in errors) + { + messageStore?.Add(CurrentEditContext.Field(err.Key), err.Value); + } + + CurrentEditContext.NotifyValidationStateChanged(); + } + } + + public void ClearErrors() + { + messageStore?.Clear(); + CurrentEditContext?.NotifyValidationStateChanged(); + } + + } +} diff --git a/songrequests_blazor/Data/Library.cs b/songrequests_blazor/Data/Library.cs index 8595830..2a9cb13 100644 --- a/songrequests_blazor/Data/Library.cs +++ b/songrequests_blazor/Data/Library.cs @@ -1,13 +1,15 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace songrequests_blazor.Data { public class Library { public int Id { get; set; } + [Required(ErrorMessage = "Der Name fehlt")] public string Name { get; set; } = null!; public List? Songs { get; set; } - public List? ScanPaths { get; set; } + public List? ScanPaths { get; set; } public LibraryType Type { get; set; } [Column("Sessions_ID")] diff --git a/songrequests_blazor/Program.cs b/songrequests_blazor/Program.cs index c05b6be..2dd8ced 100644 --- a/songrequests_blazor/Program.cs +++ b/songrequests_blazor/Program.cs @@ -20,6 +20,7 @@ builder.Services.Configure(builder.Configuration.GetSection(nameof(M // Database var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."); +builder.Services.AddDbContextFactory(options => options.UseSqlite(connectionString)); builder.Services.AddDbContext(options => options.UseSqlite(connectionString)); // Authentication