Added all fucking things except Versioning
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 3m53s
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 3m53s
This commit is contained in:
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v0.1.0-beta
|
||||
|
||||
jobs:
|
||||
build-docker-linux-arm64:
|
||||
@@ -22,6 +23,14 @@ jobs:
|
||||
id: get_repo
|
||||
run: |
|
||||
echo "REPO_LC=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV
|
||||
|
||||
- name: Get version
|
||||
run: |
|
||||
VERSION=${GITHUB_REF#refs/tags/v}
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
echo "ASSEMBLY_VERSION=${VERSION}.0" >> $GITHUB_ENV
|
||||
echo "FILE_VERSION=${VERSION}.0" >> $GITHUB_ENV
|
||||
echo "REPO_LC=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build Linux ARM64 Docker Image
|
||||
run: |
|
||||
@@ -30,7 +39,7 @@ jobs:
|
||||
.
|
||||
|
||||
- name: Save Docker Image to Tar
|
||||
run: docker save -o CouchLog-Linux-ARM64-Docker-Image.tar gitea.penry.de/${{ env.REPO_LC }}:latest
|
||||
run: docker save -o CouchLog-Linux-ARM64-Docker-Image.tar gitea.penry.de/${{ env.REPO_LC }}:nightly
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -1,51 +1,38 @@
|
||||
@page "/AdminSettings/CouchLogSettings"
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@using System.Reflection
|
||||
@using CouchLog.Data
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
|
||||
@inject ApplicationDbContext CouchLogDB
|
||||
@inject ApplicationDbContext CouchLogDb
|
||||
|
||||
<h3>CouchLog Settings</h3>
|
||||
<MudText Typo="Typo.h3">CouchLog Settings</MudText>
|
||||
|
||||
@if (accountsSettings is null)
|
||||
{
|
||||
<p>Lade Einstellungen…</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<EditForm Model="accountsSettings">
|
||||
<div class="form-check form-switch">
|
||||
<InputCheckbox class="form-check-input"
|
||||
role="switch"
|
||||
id="IsRegistrationallowedInput"
|
||||
Value="accountsSettings.IsRegistrationAllowed"
|
||||
ValueChanged="OnRegistrationChanged"
|
||||
ValueExpression="() => accountsSettings.IsRegistrationAllowed" />
|
||||
<EditForm Model="_accountsSettings">
|
||||
<MudSwitch T="bool"
|
||||
Label="Is Registration allowed"
|
||||
Value="_accountsSettings.IsRegistrationAllowed"
|
||||
ValueChanged="OnRegistrationChanged"
|
||||
Color="Color.Primary" />
|
||||
</EditForm>
|
||||
|
||||
|
||||
|
||||
<label class="form-check-label" for="IsRegistrationallowedInput">
|
||||
Is Registration allowed
|
||||
</label>
|
||||
</div>
|
||||
</EditForm>
|
||||
}
|
||||
<MudText>Version: @_version</MudText>
|
||||
|
||||
@code {
|
||||
private AccountsSettings? accountsSettings;
|
||||
private AccountsSettings? _accountsSettings;
|
||||
private readonly string _version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion?.Split('+')[0];
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
accountsSettings = await CouchLogDB.AccountsSettings.FirstAsync();
|
||||
_accountsSettings = await CouchLogDb.AccountsSettings.FirstAsync();
|
||||
}
|
||||
|
||||
private async Task OnRegistrationChanged(bool value)
|
||||
{
|
||||
accountsSettings!.IsRegistrationAllowed = value;
|
||||
_accountsSettings!.IsRegistrationAllowed = value;
|
||||
|
||||
CouchLogDB.AccountsSettings.Update(accountsSettings);
|
||||
await CouchLogDB.SaveChangesAsync();
|
||||
CouchLogDb.AccountsSettings.Update(_accountsSettings);
|
||||
await CouchLogDb.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
@using CouchLog.Data
|
||||
@using Microsoft.AspNetCore.Identity
|
||||
|
||||
@inject UserManager<ApplicationUser> UserManager
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<MudDialog>
|
||||
<TitleContent>
|
||||
<MudText Typo="Typo.h6">Create New User</MudText>
|
||||
</TitleContent>
|
||||
|
||||
<DialogContent>
|
||||
<MudInput @bind-Value="@_newUsername"></MudInput>
|
||||
<MudSelect @bind-Value="_newUserRole">
|
||||
@foreach (var role in Roles)
|
||||
{
|
||||
<MudSelectItem Value="@role.Name">@role.Name</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<MudButton OnClick="CreateNewUser" Variant="Variant.Filled" Color="Color.Success">Create user</MudButton>
|
||||
<MudButton OnClick="Cancel" Variant="Variant.Filled" Color="Color.Error">Cancel</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter] public required List<IdentityRole> Roles { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
public IMudDialogInstance MudDialog { get; set; } = null!;
|
||||
|
||||
private string _newUsername = string.Empty;
|
||||
private string _newUserRole = string.Empty;
|
||||
|
||||
private async Task CreateNewUser()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_newUsername) || string.IsNullOrWhiteSpace(_newUserRole))
|
||||
{
|
||||
Snackbar.Add("Add Username and Role" , Severity.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
ApplicationUser newUser = new ApplicationUser
|
||||
{
|
||||
UserName = _newUsername,
|
||||
EmailConfirmed = true
|
||||
};
|
||||
|
||||
var result = await UserManager.CreateAsync(newUser, "NewPassword123!");
|
||||
|
||||
if(result.Succeeded)
|
||||
{
|
||||
await UserManager.AddToRoleAsync(newUser, _newUserRole);
|
||||
MudDialog.Close(DialogResult.Ok(newUser));
|
||||
return;
|
||||
}
|
||||
|
||||
//Snackbar.Add("User could not be created", Severity.Error);
|
||||
}
|
||||
|
||||
private async Task Cancel()
|
||||
{
|
||||
MudDialog.Cancel();
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,15 @@
|
||||
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
@attribute [Authorize(Roles = "Admin")]
|
||||
|
||||
<h3>Index</h3>
|
||||
|
||||
<MudLink Href="AdminSettings/CouchLogSettings">CouchLog Settings</MudLink>
|
||||
<MudLink Href="AdminSettings/UserManagement">User Management</MudLink>
|
||||
|
||||
@code {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,106 +1,84 @@
|
||||
@page "/AdminSettings/UserManagement"
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@using CouchLog.Components.AdminSettings.Pages.Dialogs
|
||||
@using CouchLog.Data
|
||||
@using Microsoft.AspNetCore.Identity
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Microsoft.AspNetCore.Components.QuickGrid
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
|
||||
@inject ApplicationDbContext CouchLogDB
|
||||
@inject ApplicationDbContext CouchLogDb
|
||||
@inject UserManager<ApplicationUser> UserManager
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IDialogService DialogService
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
@attribute [Authorize(Roles = "Admin")]
|
||||
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<h3 class="mb-0">UserManagement</h3>
|
||||
<MudStack Row="true">
|
||||
<MudText Typo="Typo.h3">UserManagement</MudText>
|
||||
<MudSpacer/>
|
||||
<MudFab StartIcon="@Icons.Material.Filled.Add" Color="Color.Primary" OnClick="OpenCreateUserDialog"/>
|
||||
</MudStack>
|
||||
|
||||
<button type="button"
|
||||
class="btn btn-primary"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#exampleModal">
|
||||
Add User
|
||||
</button>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<!-- <div>
|
||||
<QuickGrid Items="@gridUsers.AsQueryable()">
|
||||
<PropertyColumn Title="#" Property="@(u => u.Index)" />
|
||||
<PropertyColumn Title="Username" Property="@(u => u.UserName)" />
|
||||
<PropertyColumn Title="Role" Property="@(u => u.Role)" />
|
||||
|
||||
<TemplateColumn Title="">
|
||||
@if(context.UserId != currentUserId)
|
||||
{
|
||||
<button class="btn btn-danger btn-sm" @onclick="() => DeleteUser(context)">Delete</button>
|
||||
}
|
||||
<MudDataGrid Items="_gridUsers" Filterable="false" SortMode="@SortMode.Single" Groupable="false">
|
||||
<Columns>
|
||||
<PropertyColumn Property="arg => arg.Index " Title="Index"/>
|
||||
<PropertyColumn Property="arg => arg.UserId" Title="User Id"/>
|
||||
<PropertyColumn Property="arg => arg.UserName" Title="User Name"/>
|
||||
<PropertyColumn Property="arg => arg.Role" Title="Role"/>
|
||||
<TemplateColumn>
|
||||
<CellTemplate>
|
||||
@if (context.Item.UserId != _currentUserId)
|
||||
{
|
||||
<MudFab Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" OnClick="@(() => DeleteUser(context.Item))"/>
|
||||
}
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
</QuickGrid>
|
||||
</div> -->
|
||||
<!-- #region UserCreate Modal -->
|
||||
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true" style="color: black">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" style="color: black" id="exampleModalLabel">Create User</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- 1. Username Input mit Binding und Bootstrap Klasse -->
|
||||
<div class="mb-3">
|
||||
<label for="newUsernameInput" class="form-label">Username:</label>
|
||||
<input type="text" class="form-control" id="newUsernameInput" @bind="newUsername" />
|
||||
</div>
|
||||
|
||||
<!-- 2. Select mit Binding -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Role:</label>
|
||||
<select class="form-select" @bind="newUserRoleId">
|
||||
<!-- WICHTIG: Eine leere Option als Standard, damit man weiß, ob etwas gewählt wurde -->
|
||||
<option value="" selected disabled>Bitte Rolle wählen...</option>
|
||||
|
||||
@foreach (var role in roles)
|
||||
{
|
||||
<option value="@role.Id">@role.Name</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary" @onclick="CreateUserAsync">Create User</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- #endregion -->
|
||||
</Columns>
|
||||
</MudDataGrid>
|
||||
|
||||
@code {
|
||||
|
||||
string newUsername = "";
|
||||
string newUserRoleId = "";
|
||||
List<UserGridItem> gridUsers = new();
|
||||
List<IdentityRole> roles = new();
|
||||
string? currentUserId;
|
||||
private string? _currentUserId;
|
||||
private List<ApplicationUser> _users = new();
|
||||
private List<IdentityRole> _roles = new();
|
||||
private List<UserGridItem> _gridUsers = new();
|
||||
|
||||
public class UserGridItem
|
||||
{
|
||||
public int Index { get; set; }
|
||||
public string UserId { get; set; } = "";
|
||||
public string UserName { get; set; } = "";
|
||||
public string Role { get; set; } = "";
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
currentUserId = authState.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
|
||||
_currentUserId = authState.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
|
||||
|
||||
var users = await CouchLogDB.Users.ToListAsync();
|
||||
roles = await CouchLogDB.Roles.ToListAsync();
|
||||
var users = await CouchLogDb.Users.ToListAsync();
|
||||
_roles = await CouchLogDb.Roles.ToListAsync();
|
||||
|
||||
await CreateGridUserObjects();
|
||||
}
|
||||
|
||||
private async Task CreateGridUserObjects()
|
||||
{
|
||||
_gridUsers.Clear();
|
||||
|
||||
var users = await CouchLogDb.Users.ToListAsync();
|
||||
int index = 1;
|
||||
|
||||
gridUsers.Clear();
|
||||
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
|
||||
var role = await UserManager.GetRolesAsync(user);
|
||||
|
||||
gridUsers.Add(new UserGridItem
|
||||
_gridUsers.Add(new UserGridItem
|
||||
{
|
||||
Index = index++,
|
||||
UserId = user.Id,
|
||||
@@ -110,17 +88,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
public class UserGridItem
|
||||
private async Task OpenCreateUserDialog()
|
||||
{
|
||||
public int Index { get; set; }
|
||||
public string UserId { get; set; } = "";
|
||||
public string UserName { get; set; } = "";
|
||||
public string Role { get; set; } = "";
|
||||
var parameters = new DialogParameters<CreateUserDialog>
|
||||
{
|
||||
{ x => x.Roles, _roles }
|
||||
};
|
||||
|
||||
var dialog = await DialogService.ShowAsync<CreateUserDialog>("Create User", parameters);
|
||||
var result = await dialog.Result;
|
||||
if (!result!.Canceled && result.Data is ApplicationUser newUser)
|
||||
{
|
||||
Snackbar.Add("User created", Severity.Success);
|
||||
await CreateGridUserObjects();
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
async Task DeleteUser(UserGridItem user)
|
||||
{
|
||||
if (user.UserId == currentUserId)
|
||||
if (user.UserId == _currentUserId)
|
||||
return;
|
||||
|
||||
var identityUser = await UserManager.FindByIdAsync(user.UserId);
|
||||
@@ -132,48 +119,12 @@
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
gridUsers.Remove(user);
|
||||
_gridUsers.Remove(user);
|
||||
StateHasChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Optional: Fehler anzeigen
|
||||
Snackbar.Add(result.Errors.ToString()!, Severity.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CreateUserAsync()
|
||||
{
|
||||
// Validierung
|
||||
if (string.IsNullOrWhiteSpace(newUsername) || string.IsNullOrWhiteSpace(newUserRoleId))
|
||||
{
|
||||
// Fehlermeldung anzeigen oder abbrechen
|
||||
Console.WriteLine("Bitte Benutzername und Rolle angeben.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Hier deine Logik zum Erstellen des Users
|
||||
var roleIdToUse = newUserRoleId;
|
||||
var usernameToUse = newUsername;
|
||||
|
||||
ApplicationUser newUser = new ApplicationUser
|
||||
{
|
||||
UserName = newUsername,
|
||||
EmailConfirmed = true
|
||||
};
|
||||
|
||||
var result = await UserManager.CreateAsync(newUser, "NewPassword123!");
|
||||
|
||||
IdentityRole roleName = roles.FirstOrDefault(r => r.Id == newUserRoleId);
|
||||
|
||||
if(result.Succeeded)
|
||||
{
|
||||
await UserManager.AddToRoleAsync(newUser, roleName.Name);
|
||||
}
|
||||
|
||||
// Modal schließen oder Formular zurücksetzen
|
||||
newUsername = "";
|
||||
newUserRoleId = "";
|
||||
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,17 +5,27 @@
|
||||
|
||||
@attribute [Authorize(Roles = "Admin")]
|
||||
|
||||
<h1>Manage CouchLog</h1>
|
||||
<MudText Typo="Typo.h1">Manage CouchLog</MudText>
|
||||
|
||||
<div>
|
||||
<h2></h2>
|
||||
<hr />
|
||||
<div class="row">
|
||||
<div class="col-lg-3">
|
||||
<AdminSettingsNavMenu />
|
||||
</div>
|
||||
<div class="col-lg-9">
|
||||
<MudContainer MaxWidth="MaxWidth.False" Class="pa-4">
|
||||
<MudGrid>
|
||||
<MudItem xs="12" md="3" lg="2">
|
||||
<MudNavMenu>
|
||||
<MudNavLink Href="/AdminSettings/CouchLogSettings"
|
||||
Icon="@Icons.Material.Filled.Settings">
|
||||
CouchLog Settings
|
||||
</MudNavLink>
|
||||
|
||||
<MudNavLink Href="/AdminSettings/UserManagement"
|
||||
Match="NavLinkMatch.All"
|
||||
Icon="@Icons.Material.Filled.People">
|
||||
User Management
|
||||
</MudNavLink>
|
||||
</MudNavMenu>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" md="9" lg="10">
|
||||
@Body
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</MudContainer>
|
||||
@@ -1,18 +0,0 @@
|
||||
@using Microsoft.AspNetCore.Identity
|
||||
@using CouchLog.Data
|
||||
|
||||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
|
||||
<ul class="nav nav-pills flex-column">
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href="/AdminSettings/CouchLogSettings">CouchLog Settings</NavLink>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href="/AdminSettings/UserManagement" Match="NavLinkMatch.All">User Management</NavLink>
|
||||
</li>
|
||||
<!--
|
||||
<li class="nav-item">
|
||||
<NavLink class="nav-link" href=""></NavLink>
|
||||
</li>
|
||||
-->
|
||||
</ul>
|
||||
@@ -11,19 +11,9 @@
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<style>
|
||||
.drop-zone-hover-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: opacity .2s ease;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.drop-zone-wrapper:hover .drop-zone-hover-overlay {
|
||||
opacity: 1;
|
||||
.mud-file-upload-dragarea {
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -36,57 +26,31 @@
|
||||
<EditForm EditContext="_editContext" OnSubmit="HandleSubmit">
|
||||
<DataAnnotationsValidator />
|
||||
|
||||
<MudGrid Style="min-height: 400px;">
|
||||
<MudItem xs="5" Style="display: flex;">
|
||||
<MudFileUpload T="IBrowserFile" @ref="_fileUpload" FilesChanged="OnPictureUploaded" Accept="image/*" Style="width: 100%; display: flex;">
|
||||
<ActivatorContent>
|
||||
<div @onclick="OpenFilePicker"
|
||||
@ondragenter="@(() => _isDragOver = true)"
|
||||
@ondragleave="@(() => _isDragOver = false)"
|
||||
@ondragover:preventDefault="true"
|
||||
@ondrop:preventDefault="true"
|
||||
@ondrop="@(() => _isDragOver = false)"
|
||||
style="@GetDropZoneStyle()"
|
||||
class="d-flex align-center justify-center cursor-pointer drop-zone-wrapper">
|
||||
|
||||
@if (_imagePreviewUrl is not null)
|
||||
{
|
||||
<div style="position: relative; width: 100%; height: 100%;">
|
||||
<img src="@_imagePreviewUrl"
|
||||
alt="Preview"
|
||||
style="width: 100%; height: 100%; object-fit: cover; border-radius: 6px; display: block;" />
|
||||
<div class="drop-zone-hover-overlay">
|
||||
<MudStack AlignItems="AlignItems.Center" Spacing="1">
|
||||
<MudIcon Icon="@Icons.Material.Filled.Edit"
|
||||
Color="Color.Surface"
|
||||
Size="Size.Large" />
|
||||
<MudText Typo="Typo.caption" Style="color: white;">
|
||||
Replace Image
|
||||
</MudText>
|
||||
</MudStack>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudStack AlignItems="AlignItems.Center" Spacing="2">
|
||||
<MudIcon Icon="@Icons.Material.Filled.CloudUpload"
|
||||
Size="Size.Large"
|
||||
Color="@(_isDragOver ? Color.Primary : Color.Secondary)" />
|
||||
<MudText Typo="Typo.body2"
|
||||
Align="Align.Center"
|
||||
Color="@(_isDragOver ? Color.Primary : Color.Secondary)">
|
||||
Bild hierher ziehen<br />oder klicken zum Auswählen
|
||||
</MudText>
|
||||
</MudStack>
|
||||
}
|
||||
|
||||
</div>
|
||||
</ActivatorContent>
|
||||
</MudFileUpload>
|
||||
</MudItem>
|
||||
<MudStack Row="true" Spacing="3" AlignItems="AlignItems.Start" Style="min-height: 400px;">
|
||||
<!--<editor-fold desc="Picture-Upload">-->
|
||||
<MudPaper Class="pa-0" Elevation="0" Width="280px" MinWidth="280px">
|
||||
@if (!string.IsNullOrWhiteSpace(_imagePreview))
|
||||
{
|
||||
<MudImage Src="@_imagePreview"
|
||||
ObjectFit="ObjectFit.Contain"
|
||||
Class="rounded"
|
||||
Style="width: 280px; height: 400px;"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudFileUpload @bind-Files="_pictureFile"
|
||||
DragAndDrop="true"
|
||||
OnFilesChanged="OnPictureUploaded"
|
||||
Accept="image/*"
|
||||
InputClass="null"
|
||||
Class="mud-file-upload-dragarea">
|
||||
<SelectedTemplate Context="SelectedTemplateContext"/>
|
||||
</MudFileUpload>
|
||||
}
|
||||
</MudPaper>
|
||||
<!--</editor-fold>-->
|
||||
|
||||
<MudItem xs="7">
|
||||
<MudPaper Class="flex-grow-1" Elevation="0">
|
||||
<MudStack Spacing="3">
|
||||
|
||||
<MudTextField @bind-Value="_form.Title"
|
||||
@@ -127,8 +91,8 @@
|
||||
}
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</MudPaper>
|
||||
</MudStack>
|
||||
</EditForm>
|
||||
</DialogContent>
|
||||
|
||||
@@ -148,14 +112,14 @@
|
||||
private List<Genre> _genres = new();
|
||||
|
||||
private GlobalEntityFormModel _form = new();
|
||||
private MudFileUpload<IBrowserFile>? _fileUpload;
|
||||
private IBrowserFile? _picture;
|
||||
private string? _imagePreviewUrl;
|
||||
private bool _isDragOver;
|
||||
private string? _imagePreview;
|
||||
private int _selectedMediaTypeId;
|
||||
private bool _isPrivate;
|
||||
private bool _isSaving;
|
||||
private IEnumerable<int> _selectedGenreIds = new HashSet<int>();
|
||||
private IReadOnlyCollection<int> _selectedGenreIds = Array.Empty<int>();
|
||||
|
||||
//Picture
|
||||
private IBrowserFile? _pictureFile;
|
||||
|
||||
private class GlobalEntityFormModel
|
||||
{
|
||||
@@ -164,15 +128,6 @@
|
||||
public string Title { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
private string GetDropZoneStyle() =>
|
||||
"width: 100%; height: 100%; min-height: 380px; border-radius: 6px; " +
|
||||
$"border: 2px dashed {(_isDragOver ? "var(--mud-palette-primary)" : "var(--mud-palette-lines-default)")}; " +
|
||||
$"background-color: {(_isDragOver ? "var(--mud-palette-primary-hover)" : "transparent")}; " +
|
||||
"transition: border-color .2s ease, background-color .2s ease;";
|
||||
|
||||
private Task OpenFilePicker() =>
|
||||
_fileUpload?.OpenFilePickerAsync() ?? Task.CompletedTask;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_editContext = new EditContext(_form);
|
||||
@@ -194,17 +149,24 @@
|
||||
_selectedMediaTypeId = _mediaTypes.First().Id;
|
||||
}
|
||||
|
||||
private async Task OnPictureUploaded(IBrowserFile file)
|
||||
private async Task OnPictureUploaded()
|
||||
{
|
||||
_picture = file;
|
||||
|
||||
const long maxPreviewSize = 5 * 1024 * 1024;
|
||||
await using var stream = file.OpenReadStream(maxPreviewSize);
|
||||
if (_pictureFile == null)
|
||||
{
|
||||
Snackbar.Add("No file selected!", Severity.Warning);
|
||||
return;
|
||||
}
|
||||
if (_pictureFile.Size > 5242880)
|
||||
{
|
||||
Snackbar.Add("File size exceeds 5MB!", Severity.Error);
|
||||
return;
|
||||
}
|
||||
await using var stream = _pictureFile.OpenReadStream(5242880);
|
||||
using var ms = new MemoryStream();
|
||||
await stream.CopyToAsync(ms);
|
||||
|
||||
var base64 = Convert.ToBase64String(ms.ToArray());
|
||||
_imagePreviewUrl = $"data:{file.ContentType};base64,{base64}";
|
||||
_imagePreview = $"data:{_pictureFile.ContentType};base64,{base64}";
|
||||
}
|
||||
|
||||
private void Cancel() => MudDialog.Cancel();
|
||||
@@ -213,7 +175,7 @@
|
||||
{
|
||||
if (!ctx.Validate()) return;
|
||||
|
||||
if (_picture is null)
|
||||
if (_pictureFile is null)
|
||||
{
|
||||
Snackbar.Add("Please upload a picture.", Severity.Warning);
|
||||
return;
|
||||
@@ -229,13 +191,12 @@
|
||||
|
||||
try
|
||||
{
|
||||
string safeTitle = _form.Title.Replace(" ", "-");
|
||||
string fileName = $"{safeTitle}-{Guid.NewGuid()}{Path.GetExtension(_picture.Name)}";
|
||||
string fileName = $"{Guid.NewGuid()}{Path.GetExtension(_pictureFile.Name)}";
|
||||
string picturePath = Path.Combine("wwwroot", "Pictures", fileName);
|
||||
|
||||
await using (var fs = File.Create(picturePath))
|
||||
{
|
||||
await _picture.OpenReadStream(maxAllowedSize: 10_000_000).CopyToAsync(fs);
|
||||
await _pictureFile.OpenReadStream(maxAllowedSize: 10_000_000).CopyToAsync(fs);
|
||||
}
|
||||
|
||||
var entity = new GlobalEntity
|
||||
|
||||
@@ -178,6 +178,8 @@
|
||||
CreationTime = DateTime.Now,
|
||||
GlobalEntityId = globalEntity.Id,
|
||||
UserWatchStatusId = 1,
|
||||
//Season = 0,
|
||||
//Episode = 0
|
||||
};
|
||||
|
||||
CouchLogDb.PrivateEntities.Add(privateEntity);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
@using CouchLog.Data
|
||||
|
||||
<MudCard Style="max-width: 500px; background-color: #2a2a2a; color: white;" Elevation="4">
|
||||
<MudCard Elevation="4">
|
||||
<MudCardContent Class="pa-0">
|
||||
<MudGrid Spacing="0">
|
||||
<!-- Poster Image -->
|
||||
<!--<editor-fold desc="Poster-Image">-->
|
||||
<MudItem xs="4">
|
||||
<MudImage
|
||||
Src="@PrivateEntity.GlobalEntity.PicturePath"
|
||||
@@ -11,73 +11,84 @@
|
||||
ObjectFit="ObjectFit.Cover"
|
||||
Style="width: 100%; height: 100%; min-height: 180px; border-radius: 4px 0 0 4px;" />
|
||||
</MudItem>
|
||||
|
||||
<!-- Content -->
|
||||
<!--</editor-fold>-->
|
||||
|
||||
<MudItem xs="8">
|
||||
<MudStack Class="pa-3" Spacing="2">
|
||||
<!-- Title Row with Menu -->
|
||||
<!--<editor-fold desc="Options">-->
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween">
|
||||
<MudText Typo="Typo.h6" Style="color: white; font-weight: 600;">@PrivateEntity.GlobalEntity.Title</MudText>
|
||||
<MudText Typo="Typo.h6">@PrivateEntity.GlobalEntity.Title</MudText>
|
||||
<MudMenu Icon="@Icons.Material.Filled.MoreVert" Color="Color.Default" Dense="true">
|
||||
<MudMenuItem>Bearbeiten</MudMenuItem>
|
||||
<MudMenuItem>Entfernen</MudMenuItem>
|
||||
<MudMenuItem>Edit</MudMenuItem>
|
||||
<MudMenuItem OnClick="@(() => OnRemove.InvokeAsync())">Remove</MudMenuItem>
|
||||
</MudMenu>
|
||||
</MudStack>
|
||||
<!--</editor-fold>-->
|
||||
|
||||
<!-- Date -->
|
||||
<!-- <MudText Typo="Typo.caption" Style="color: #aaaaaa;">@Date.ToString("MM/dd/yyyy")</MudText> -->
|
||||
|
||||
<!-- Status Dropdown -->
|
||||
<MudSelect
|
||||
T="string"
|
||||
@bind-Value="@PrivateEntity.UserWatchStatus!.Name"
|
||||
<!--<editor-fold desc="Dropdown-States">-->
|
||||
<MudSelect
|
||||
T="int"
|
||||
Value="PrivateEntity.UserWatchStatus!.Id"
|
||||
ValueChanged="@(newStatusId => OnWatchStatusChange.InvokeAsync(newStatusId))"
|
||||
Dense="true"
|
||||
Variant="Variant.Outlined"
|
||||
Style="background-color: #3a3a3a; color: white;"
|
||||
Label="">
|
||||
<!--@foreach (var status in )
|
||||
@foreach (var status in UserWatchStatuses)
|
||||
{
|
||||
<MudSelectItem Value="@status">@status</MudSelectItem>
|
||||
}-->
|
||||
<MudSelectItem Value="status.Id">@status.Name</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
<!--</editor-fold>-->
|
||||
|
||||
<!-- Season Dropdown -->
|
||||
<!--<editor-fold desc="Dropdown-Season">-->
|
||||
<MudSelect
|
||||
T="int?"
|
||||
@bind-Value="@PrivateEntity.Season"
|
||||
Value="PrivateEntity.Season"
|
||||
ValueChanged="@(newSeason => OnSeasonChange.InvokeAsync(newSeason))"
|
||||
Dense="true"
|
||||
Variant="Variant.Outlined"
|
||||
Style="background-color: #3a3a3a; color: white;"
|
||||
Label="">
|
||||
<!-- @foreach (var season in SeasonOptions)
|
||||
@{
|
||||
int? currentSeason = PrivateEntity.Season ?? 1;
|
||||
int? startOffsetSeason = currentSeason > 1 ? -1 : 0;
|
||||
}
|
||||
@for (int? i = startOffsetSeason; i <= 5; i++)
|
||||
{
|
||||
<MudSelectItem Value="@season">@season</MudSelectItem>
|
||||
}-->
|
||||
int? season = currentSeason + i;
|
||||
<MudSelectItem Value="@season">Staffel @season</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
<!--</editor-fold>-->
|
||||
|
||||
<!-- Episode Dropdown -->
|
||||
<!--<editor-fold desc="Dropdown-Episode">-->
|
||||
<MudSelect
|
||||
T="int?"
|
||||
@bind-Value="@PrivateEntity.Episode"
|
||||
Value="PrivateEntity.Episode"
|
||||
ValueChanged="@(newEpisode => OnEpisodeChange.InvokeAsync(newEpisode))"
|
||||
Dense="true"
|
||||
Variant="Variant.Outlined"
|
||||
Style="background-color: #3a3a3a; color: white;"
|
||||
Label="">
|
||||
<!--@foreach (var episode in EpisodeOptions)
|
||||
@{
|
||||
int? currentEpisode = PrivateEntity.Episode ?? 1;
|
||||
int? startOffsetEpisode = currentEpisode > 1 ? -1 : 0;
|
||||
}
|
||||
@for (int? i = startOffsetEpisode; i <= 5; i++)
|
||||
{
|
||||
<MudSelectItem Value="@episode">@episode</MudSelectItem>
|
||||
}-->
|
||||
int? episode = currentEpisode + i;
|
||||
<MudSelectItem Value="@episode">Episode @episode</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
<!--</editor-fold>-->
|
||||
|
||||
<!-- Details Button -->
|
||||
<!--<editor-fold desc="Details Button">-->
|
||||
<MudStack Row="true" Justify="Justify.FlexEnd">
|
||||
<MudButton
|
||||
Variant="Variant.Outlined"
|
||||
Size="Size.Small"
|
||||
Style="color: #aaaaaa; border-color: #555555;">
|
||||
Size="Size.Small">
|
||||
Details
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
<!--</editor-fold>-->
|
||||
</MudStack>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
@@ -88,4 +99,10 @@
|
||||
{
|
||||
[Parameter] public PrivateEntity PrivateEntity { get; set; } = null!;
|
||||
[Parameter] public List<UserWatchStatus> UserWatchStatuses { get; set; } = null!;
|
||||
|
||||
[Parameter] public EventCallback<int> OnWatchStatusChange { get; set; }
|
||||
[Parameter] public EventCallback<int?> OnSeasonChange { get; set; }
|
||||
[Parameter] public EventCallback<int?> OnEpisodeChange { get; set; }
|
||||
|
||||
[Parameter] public EventCallback OnRemove { get; set; }
|
||||
}
|
||||
|
||||
@@ -53,10 +53,11 @@
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
<MudCollapse Expanded="_showFilters">
|
||||
@*Entity allready added*@
|
||||
No Filters avalible yet!
|
||||
<!--@*Entity allready added*@
|
||||
<MudSwitch @bind-Value="_showAlreadyAddedEntities" Color="Color.Primary">
|
||||
Show already added Entities
|
||||
</MudSwitch>
|
||||
</MudSwitch>-->
|
||||
@*To be exstendet*@
|
||||
</MudCollapse>
|
||||
</MudPaper>
|
||||
@@ -72,7 +73,16 @@
|
||||
<MudGrid Spacing="6" Justify="Justify.Center">
|
||||
@foreach (var privateEntity in VisibleActiveWatchingEntities)
|
||||
{
|
||||
<PrivateEntityCard PrivateEntity="privateEntity"/>
|
||||
<MudItem xs="12" sm="8" md="6" lg="4">
|
||||
<PrivateEntityCard
|
||||
PrivateEntity="privateEntity"
|
||||
UserWatchStatuses="_userWatchStatuses"
|
||||
OnWatchStatusChange="@(newStatusId => ChangeWatchStatus(privateEntity, newStatusId))"
|
||||
OnSeasonChange="@(newSeason => ChangeSeason(privateEntity, newSeason))"
|
||||
OnEpisodeChange="@(newEpisode => ChangeEpisode(privateEntity, newEpisode))"
|
||||
OnRemove="() => RemovePrivateEntity(privateEntity)"
|
||||
/>
|
||||
</MudItem>
|
||||
}
|
||||
</MudGrid>
|
||||
</MudContainer>
|
||||
@@ -85,14 +95,24 @@
|
||||
</MudContainer>
|
||||
<!--</editor-fold>-->
|
||||
|
||||
<br/>
|
||||
|
||||
<!--<editor-fold desc="Library">-->
|
||||
<MudContainer MaxWidth="MaxWidth.False">
|
||||
<MudText Typo="Typo.h4">Library</MudText>
|
||||
<br/>
|
||||
<MudGrid Spacing="6" Justify="Justify.Center">
|
||||
@foreach (var privateEntity in VisibleLibraryEntities)
|
||||
{
|
||||
<PrivateEntityCard PrivateEntity="privateEntity"/>
|
||||
<MudItem xs="12" sm="8" md="6" lg="4">
|
||||
<PrivateEntityCard
|
||||
PrivateEntity="privateEntity"
|
||||
UserWatchStatuses="_userWatchStatuses"
|
||||
OnWatchStatusChange="@(newStatusId => ChangeWatchStatus(privateEntity, newStatusId))"
|
||||
OnSeasonChange="@(newSeason => ChangeSeason(privateEntity, newSeason))"
|
||||
OnEpisodeChange="@(newEpisode => ChangeEpisode(privateEntity, newEpisode))"
|
||||
OnRemove="() => RemovePrivateEntity(privateEntity)"
|
||||
/>
|
||||
</MudItem>
|
||||
}
|
||||
</MudGrid>
|
||||
</MudContainer>
|
||||
@@ -119,13 +139,13 @@
|
||||
|
||||
//Paging Active Watching
|
||||
private int _currentActiveWatchingPage = 1;
|
||||
private int _pageSizeActiveWatching = 12;
|
||||
private readonly int _pageSizeActiveWatching = 12;
|
||||
private int TotalPagesActiveWatching => (int)Math.Ceiling(ActiveWatchingEntities.Count() / (double)_pageSizeActiveWatching);
|
||||
private IEnumerable<PrivateEntity> VisibleActiveWatchingEntities => ActiveWatchingEntities.Skip((_currentActiveWatchingPage - 1) * _pageSizeActiveWatching).Take(_pageSizeActiveWatching);
|
||||
|
||||
//Paging Library
|
||||
private int _currentLibraryPage = 1;
|
||||
private int _pageSizeLibrary = 12;
|
||||
private readonly int _pageSizeLibrary = 12;
|
||||
private int TotalPagesLibrary => (int)Math.Ceiling(LibraryEntities.Count() / (double)_pageSizeLibrary);
|
||||
private IEnumerable<PrivateEntity> VisibleLibraryEntities => LibraryEntities.Skip((_currentLibraryPage - 1) * _pageSizeLibrary).Take(_pageSizeLibrary);
|
||||
|
||||
@@ -140,6 +160,13 @@
|
||||
private IEnumerable<PrivateEntity> LibraryEntities => FiltertGlobalEntities.Where(x => x.UserWatchStatus?.Name != "Watching").ToList();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await GetPrivateEntities();
|
||||
|
||||
_userWatchStatuses = await CouchLogDb.UserWatchStatuses.OrderBy(entity => entity.Id).ToListAsync();
|
||||
}
|
||||
|
||||
private async Task GetPrivateEntities()
|
||||
{
|
||||
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
_appUser = await UserManager.GetUserAsync(authState.User);
|
||||
@@ -153,8 +180,38 @@
|
||||
.Include(x => x.GlobalEntity.MediaType)
|
||||
.OrderByDescending(entity => entity.LastChange ?? entity.CreationTime)
|
||||
.ToListAsync();
|
||||
|
||||
_userWatchStatuses = await CouchLogDb.UserWatchStatuses.OrderByDescending(entity => entity.Id).ToListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshPages()
|
||||
{
|
||||
_currentLibraryPage = 1;
|
||||
_currentActiveWatchingPage = 1;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task ChangeWatchStatus(PrivateEntity privateEntity, int newUserWatchStatusId)
|
||||
{
|
||||
privateEntity.UserWatchStatus = _userWatchStatuses.FirstOrDefault(s => s.Id == newUserWatchStatusId);
|
||||
await CouchLogDb.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task ChangeSeason(PrivateEntity privateEntity, int? newSeason)
|
||||
{
|
||||
privateEntity.Season = newSeason;
|
||||
await CouchLogDb.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task ChangeEpisode(PrivateEntity privateEntity, int? newEpisode)
|
||||
{
|
||||
privateEntity.Episode = newEpisode;
|
||||
await CouchLogDb.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task RemovePrivateEntity(PrivateEntity privateEntity)
|
||||
{
|
||||
CouchLogDb.PrivateEntities.Remove(privateEntity);
|
||||
await CouchLogDb.SaveChangesAsync();
|
||||
await GetPrivateEntities();
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@
|
||||
<OutputType>Exe</OutputType>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<PackAsTool>True</PackAsTool>
|
||||
<Version>0.1.0-nightly.10</Version>
|
||||
<Authors>Penry</Authors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -77,7 +79,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MudBlazor" Version="8.15.0" />
|
||||
<PackageReference Include="MudBlazor" Version="9.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace CouchLog
|
||||
new() { Name = "Not watched", CreationTime = DateTime.Now },
|
||||
new() { Name = "Watching", CreationTime = DateTime.Now },
|
||||
new() { Name = "Finished", CreationTime = DateTime.Now },
|
||||
new() { Name = "Paused", CreationTime = DateTime.Now },
|
||||
new() { Name = "Waiting for next Season", CreationTime = DateTime.Now },
|
||||
new() { Name = "Aborted", CreationTime= DateTime.Now },
|
||||
];
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
# Watchlog
|
||||
## Work in Progress
|
||||
### Information
|
||||
- This Repo has a Mirror on Codeberg, but the Releases a only available on [Gitea](https://gitea.penry.de/Penry/CouchLog) for now
|
||||
## Information
|
||||
- This Repo has a Mirror on Codeberg, but the Releases a only available on [Gitea](https://gitea.penry.de/Penry/CouchLog) for now
|
||||
|
||||
## About docker image Releases
|
||||
There are two options:
|
||||
- latest: Is the latest release Version
|
||||
- nightly: Is the latest commit on the main branch
|
||||
Reference in New Issue
Block a user