51 Commits

Author SHA1 Message Date
Penry 9204fed54c fix: #72 fixed that the height of the PrivateEntityCard.razor changes when don't having all dropdown menues (#74)
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 1m25s
Build Docker Linux ARM64 Release / build-docker-linux-arm64-release (push) Successful in 25s
Reviewed-on: #74
2026-06-12 20:48:42 +02:00
Penry 406d0f5652 fix: #60 fixed that the Global or Private Entity Card Title chnages height of the Card (#73)
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 2m54s
Reviewed-on: #73
2026-06-12 20:05:05 +02:00
Penry 9cd651bdfe Fixed GlobalEntity Problems and removed Season and Episode on Movie Entitys (#70)
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 1m22s
Reviewed-on: #70
Fixed Issue #63
Fixed Issue #68
Fixed Issue #69
2026-06-07 22:02:10 +02:00
Penry 96336fd637 Fixed Workflow Syntax (#67)
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 38s
Reviewed-on: #67
2026-06-07 21:21:30 +02:00
Penry e7d1b4b524 Merge pull request 'Adapted Nightly and Release Workflow' (#66) from Test-Branch into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 8s
Reviewed-on: #66
2026-06-07 21:16:31 +02:00
Penry a4d7269f8f Adapted Release Workflow 2026-06-07 21:15:46 +02:00
Penry c7d741d15b Adpated Workflow for Nightly Build 2026-06-07 21:14:02 +02:00
Penry 6d2a6c5925 Added fetch depth
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 8s
2026-06-07 20:41:31 +02:00
Penry 9ccb929553 Added Tags fetching
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 8s
2026-06-07 20:34:28 +02:00
Penry 5e086c0598 Fixed Syntax
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 8s
2026-06-07 20:32:46 +02:00
Penry a7c86289b3 Adpated Workflow for working only on those Branches
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 7s
2026-06-07 20:31:29 +02:00
Penry b2e851d765 Adpated Workflow for Running every Time 2026-06-07 20:30:32 +02:00
Penry 1a1d7dec7c Test 2026-06-07 20:29:44 +02:00
Penry 103da6abe8 Test Commit 2026-06-07 20:27:03 +02:00
Penry 5f2fdbac87 Merge pull request 'Test Commit 2' (#65) from penry-patch-2 into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 38s
Reviewed-on: #65
2026-06-07 20:16:52 +02:00
Penry e0ceb7d314 Test Commit 2 2026-06-07 20:16:46 +02:00
Penry 8df80eba3b Merge pull request 'Test Commit' (#64) from penry-patch-1 into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 38s
Reviewed-on: #64
2026-06-07 20:16:25 +02:00
Penry 77875110f0 Test Commit 2026-06-07 20:16:18 +02:00
Penry ddbc6affd1 Merge pull request '.gitea/workflows/linux_arm64_docker.yaml aktualisiert' (#58) from penry-patch-1 into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 40s
Build Docker Linux ARM64 Release / build-docker-linux-arm64-release (push) Successful in 41s
Reviewed-on: #58
2026-06-07 18:44:33 +02:00
Penry cd41eea0d9 .gitea/workflows/linux_arm64_docker.yaml aktualisiert
Now?
2026-06-07 18:44:25 +02:00
Penry d15ecd926c Merge pull request '.gitea/workflows/linux_arm64_docker.yaml aktualisiert' (#57) from penry-patch-1 into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 8s
Reviewed-on: #57
2026-06-07 18:42:44 +02:00
Penry 78774abffd .gitea/workflows/linux_arm64_docker.yaml aktualisiert
Hopefully Final fix
2026-06-07 18:42:36 +02:00
Penry a8cc182265 Merge pull request '.gitea/workflows/linux_arm64_docker.yaml aktualisiert' (#56) from penry-patch-1 into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 40s
Reviewed-on: #56
2026-06-07 18:22:44 +02:00
Penry 65e03c2586 .gitea/workflows/linux_arm64_docker.yaml aktualisiert
Try to fix it again
2026-06-07 18:22:36 +02:00
Penry 45c4379d53 Merge pull request '.gitea/workflows/linux_arm64_docker.yaml aktualisiert' (#55) from penry-patch-1 into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 8s
Reviewed-on: #55
2026-06-07 18:20:00 +02:00
Penry 55d65f4841 .gitea/workflows/linux_arm64_docker.yaml aktualisiert
Fixes again...:(
2026-06-07 18:19:53 +02:00
Penry 3f4feb9dc9 Merge pull request '.gitea/workflows/linux_arm64_docker.yaml aktualisiert' (#54) from penry-patch-1 into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 39s
Reviewed-on: #54
2026-06-07 18:14:58 +02:00
Penry 45479d9df7 .gitea/workflows/linux_arm64_docker.yaml aktualisiert
Adapted Versioning and Workflow
2026-06-07 18:14:47 +02:00
Penry 1d077fe1cc Merge pull request '.gitea/workflows/linux_arm64_docker.yaml aktualisiert' (#53) from penry-patch-1 into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 38s
Reviewed-on: #53
2026-06-07 18:04:38 +02:00
Penry 2046939d8a .gitea/workflows/linux_arm64_docker.yaml aktualisiert
Fix Workflow Syntax
2026-06-07 18:04:31 +02:00
Penry 02d2a26a82 Merge pull request 'Add Changes' (#52) from v0.1.0-beta into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 8s
Reviewed-on: #52
2026-06-07 18:01:39 +02:00
Penry c00fb3234c Test Nightly Version if works 2026-06-07 17:53:01 +02:00
Penry 6922303e52 Testing
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 39s
2026-06-07 17:31:05 +02:00
Penry 3ffad3abbe Testing
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 39s
2026-06-07 17:25:20 +02:00
Penry 68afe22526 Merge pull request 'Most Changes for v0.1.0-beta' (#51) from v0.1.0-beta into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 17s
Reviewed-on: #51
2026-06-07 16:46:14 +02:00
Penry 28fe48c3f0 Testing
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 8s
2026-06-07 16:30:57 +02:00
Penry 8cd0dc6c81 Test
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 8s
2026-06-07 16:28:05 +02:00
Penry 8c4a3def23 Added Version
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 38s
2026-06-07 16:23:23 +02:00
Penry ef41d46e50 Looking at the new Output
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 38s
2026-06-07 15:45:47 +02:00
Penry f8eba6b8dd Fix Syntax
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 41s
2026-06-07 15:44:16 +02:00
Penry 99581df6f7 Test Echo Output
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 8s
2026-06-07 15:43:34 +02:00
Penry 6991af3daa Added Authentication
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 1m10s
2026-06-07 15:35:33 +02:00
Penry 04ca70dc67 Fixed Syntax
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 7s
2026-06-07 15:34:26 +02:00
Penry cd159509a0 Remove Version for Tests
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 7s
2026-06-07 15:33:03 +02:00
Penry ea7604a05f Added Nightly Push to Workflow
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 8s
2026-06-07 15:31:35 +02:00
Penry 622a751c40 Adpated Nightly Build
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 37s
2026-06-07 15:27:53 +02:00
Penry 0f3da6952b Added all fucking things except Versioning
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 3m53s
2026-06-07 08:11:02 +02:00
Penry 59442d5203 Merge branch 'main' into v0.1.0-beta 2026-06-07 01:10:17 +02:00
Penry 594541a103 Merge pull request 'penry-patch-1' (#47) from penry-patch-1 into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 45s
Reviewed-on: #47
2026-02-21 22:39:18 +01:00
Penry ee7bcc1eae README.md aktualisiert 2026-02-21 22:38:01 +01:00
Penry 4fe354add0 README.md aktualisiert 2026-02-21 22:36:12 +01:00
16 changed files with 449 additions and 347 deletions
@@ -24,13 +24,6 @@ jobs:
echo "VERSION=$VERSION" >> $GITHUB_ENV echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "REPO_LC=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV echo "REPO_LC=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV
- name: Login to own image registry
uses: docker/login-action@v3
with:
registry: gitea.penry.de
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Setup .NET10 - name: Setup .NET10
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v3
with: with:
@@ -42,8 +35,16 @@ jobs:
-t gitea.penry.de/${{ env.REPO_LC }}:${{ env.VERSION }} \ -t gitea.penry.de/${{ env.REPO_LC }}:${{ env.VERSION }} \
-t gitea.penry.de/${{ env.REPO_LC }}:latest \ -t gitea.penry.de/${{ env.REPO_LC }}:latest \
--label "org.opencontainers.image.source=https://gitea.penry.de/${{ github.repository }}" \ --label "org.opencontainers.image.source=https://gitea.penry.de/${{ github.repository }}" \
--build-arg VERSION=${{ env.VERSION }} \
. .
- name: Login to own image registry
uses: docker/login-action@v3
with:
registry: gitea.penry.de
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Push Images - name: Push Images
run: | run: |
docker push gitea.penry.de/${{ env.REPO_LC }}:${{ env.VERSION }} docker push gitea.penry.de/${{ env.REPO_LC }}:${{ env.VERSION }}
+46 -3
View File
@@ -14,6 +14,8 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Docker Buildx - name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v3
@@ -21,16 +23,57 @@ jobs:
- name: Get Repository Name - name: Get Repository Name
id: get_repo id: get_repo
run: | run: |
echo "REPO_LC=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV REPO_LC="${GITHUB_REPOSITORY,,}"
echo "REPO_LC=$REPO_LC" >> $GITHUB_ENV
echo "Repo: $REPO_LC"
- name: Build version
shell: bash
run: |
LAST_TAG=$(git describe --tags --abbrev=0)
if [[ -z "${LAST_TAG:-}" ]]; then
echo "No tag found"
exit 1
fi
COMMIT_COUNT=$(git rev-list $LAST_TAG..HEAD --count)
if [[ "$LAST_TAG" == *-* ]]; then
TEMP_LAST_TAG="${LAST_TAG%-*}"
VERSION="${TEMP_LAST_TAG}-nightly.${COMMIT_COUNT}"
else
VERSION="${LAST_TAG}-nightly.${COMMIT_COUNT}"
fi
echo "LAST_TAG=$LAST_TAG" >> "$GITHUB_ENV"
echo "COMMIT_COUNT=$COMMIT_COUNT" >> "$GITHUB_ENV"
echo "VERSION=$VERSION" >> "$GITHUB_ENV"
echo "Last tag: $LAST_TAG"
echo "Commit count: $COMMIT_COUNT"
echo "Final version: $VERSION"
- name: Build Linux ARM64 Docker Image - name: Build Linux ARM64 Docker Image
run: | run: |
docker build \ docker build \
-t gitea.penry.de/${{ env.REPO_LC }}:latest \ -t gitea.penry.de/${{ env.REPO_LC }}:nightly \
--build-arg VERSION=${{ env.VERSION }} \
. .
- name: Login to own image registry
uses: docker/login-action@v3
with:
registry: gitea.penry.de
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Push Images
run: |
docker push gitea.penry.de/${{ env.REPO_LC }}:nightly
- name: Save Docker Image to Tar - 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 - name: Upload Artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
@@ -1,51 +1,38 @@
@page "/AdminSettings/CouchLogSettings" @page "/AdminSettings/CouchLogSettings"
@rendermode InteractiveServer @rendermode InteractiveServer
@using System.Reflection
@using CouchLog.Data @using CouchLog.Data
@using Microsoft.EntityFrameworkCore @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) <EditForm Model="_accountsSettings">
{ <MudSwitch T="bool"
<p>Lade Einstellungen…</p> Label="Is Registration allowed"
} Value="_accountsSettings.IsRegistrationAllowed"
else
{
<EditForm Model="accountsSettings">
<div class="form-check form-switch">
<InputCheckbox class="form-check-input"
role="switch"
id="IsRegistrationallowedInput"
Value="accountsSettings.IsRegistrationAllowed"
ValueChanged="OnRegistrationChanged" ValueChanged="OnRegistrationChanged"
ValueExpression="() => accountsSettings.IsRegistrationAllowed" /> Color="Color.Primary" />
</EditForm>
<MudText>Version: @_version</MudText>
<label class="form-check-label" for="IsRegistrationallowedInput">
Is Registration allowed
</label>
</div>
</EditForm>
}
@code { @code {
private AccountsSettings? accountsSettings; private AccountsSettings? _accountsSettings;
private readonly string _version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion?.Split('+')[0];
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
accountsSettings = await CouchLogDB.AccountsSettings.FirstAsync(); _accountsSettings = await CouchLogDb.AccountsSettings.FirstAsync();
} }
private async Task OnRegistrationChanged(bool value) private async Task OnRegistrationChanged(bool value)
{ {
accountsSettings!.IsRegistrationAllowed = value; _accountsSettings!.IsRegistrationAllowed = value;
CouchLogDB.AccountsSettings.Update(accountsSettings); CouchLogDb.AccountsSettings.Update(_accountsSettings);
await CouchLogDB.SaveChangesAsync(); 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 @using Microsoft.AspNetCore.Authorization
@inject NavigationManager NavigationManager
@attribute [Authorize(Roles = "Admin")] @attribute [Authorize(Roles = "Admin")]
<h3>Index</h3> <h3>Index</h3>
<MudLink Href="AdminSettings/CouchLogSettings">CouchLog Settings</MudLink>
<MudLink Href="AdminSettings/UserManagement">User Management</MudLink>
@code { @code {
} }
@@ -1,114 +1,51 @@
@page "/AdminSettings/UserManagement" @page "/AdminSettings/UserManagement"
@rendermode InteractiveServer @rendermode InteractiveServer
@using CouchLog.Components.AdminSettings.Pages.Dialogs
@using CouchLog.Data @using CouchLog.Data
@using Microsoft.AspNetCore.Identity @using Microsoft.AspNetCore.Identity
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@using Microsoft.AspNetCore.Components.QuickGrid
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@inject ApplicationDbContext CouchLogDB @inject ApplicationDbContext CouchLogDb
@inject UserManager<ApplicationUser> UserManager @inject UserManager<ApplicationUser> UserManager
@inject AuthenticationStateProvider AuthenticationStateProvider @inject AuthenticationStateProvider AuthenticationStateProvider
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IDialogService DialogService
@inject ISnackbar Snackbar
@attribute [Authorize(Roles = "Admin")] @attribute [Authorize(Roles = "Admin")]
<div class="d-flex align-items-center justify-content-between"> <MudStack Row="true">
<h3 class="mb-0">UserManagement</h3> <MudText Typo="Typo.h3">UserManagement</MudText>
<MudSpacer/>
<MudFab StartIcon="@Icons.Material.Filled.Add" Color="Color.Primary" OnClick="OpenCreateUserDialog"/>
</MudStack>
<button type="button" <br/>
class="btn btn-primary"
data-bs-toggle="modal"
data-bs-target="#exampleModal">
Add User
</button>
</div>
<!-- <div> <MudDataGrid Items="_gridUsers" Filterable="false" SortMode="@SortMode.Single" Groupable="false">
<QuickGrid Items="@gridUsers.AsQueryable()"> <Columns>
<PropertyColumn Title="#" Property="@(u => u.Index)" /> <PropertyColumn Property="arg => arg.Index " Title="Index"/>
<PropertyColumn Title="Username" Property="@(u => u.UserName)" /> <PropertyColumn Property="arg => arg.UserId" Title="User Id"/>
<PropertyColumn Title="Role" Property="@(u => u.Role)" /> <PropertyColumn Property="arg => arg.UserName" Title="User Name"/>
<PropertyColumn Property="arg => arg.Role" Title="Role"/>
<TemplateColumn Title=""> <TemplateColumn>
@if(context.UserId != currentUserId) <CellTemplate>
@if (context.Item.UserId != _currentUserId)
{ {
<button class="btn btn-danger btn-sm" @onclick="() => DeleteUser(context)">Delete</button> <MudFab Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" OnClick="@(() => DeleteUser(context.Item))"/>
} }
</CellTemplate>
</TemplateColumn> </TemplateColumn>
</QuickGrid> </Columns>
</div> --> </MudDataGrid>
<!-- #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 -->
@code { @code {
private string? _currentUserId;
string newUsername = ""; private List<ApplicationUser> _users = new();
string newUserRoleId = ""; private List<IdentityRole> _roles = new();
List<UserGridItem> gridUsers = new(); private List<UserGridItem> _gridUsers = new();
List<IdentityRole> roles = new();
string? currentUserId;
protected override async Task OnInitializedAsync()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
currentUserId = authState.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value;
var users = await CouchLogDB.Users.ToListAsync();
roles = await CouchLogDB.Roles.ToListAsync();
int index = 1;
gridUsers.Clear();
foreach (var user in users)
{
var role = await UserManager.GetRolesAsync(user);
gridUsers.Add(new UserGridItem
{
Index = index++,
UserId = user.Id,
UserName = user.UserName!,
Role = role.FirstOrDefault() ?? "-"
});
}
}
public class UserGridItem public class UserGridItem
{ {
@@ -118,9 +55,59 @@
public string Role { 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;
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;
foreach (var user in users)
{
var role = await UserManager.GetRolesAsync(user);
_gridUsers.Add(new UserGridItem
{
Index = index++,
UserId = user.Id,
UserName = user.UserName!,
Role = role.FirstOrDefault() ?? "-"
});
}
}
private async Task OpenCreateUserDialog()
{
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) async Task DeleteUser(UserGridItem user)
{ {
if (user.UserId == currentUserId) if (user.UserId == _currentUserId)
return; return;
var identityUser = await UserManager.FindByIdAsync(user.UserId); var identityUser = await UserManager.FindByIdAsync(user.UserId);
@@ -132,48 +119,12 @@
if (result.Succeeded) if (result.Succeeded)
{ {
gridUsers.Remove(user); _gridUsers.Remove(user);
StateHasChanged(); StateHasChanged();
} }
else 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")] @attribute [Authorize(Roles = "Admin")]
<h1>Manage CouchLog</h1> <MudText Typo="Typo.h1">Manage CouchLog</MudText>
<div> <MudContainer MaxWidth="MaxWidth.False" Class="pa-4">
<h2></h2> <MudGrid>
<hr /> <MudItem xs="12" md="3" lg="2">
<div class="row"> <MudNavMenu>
<div class="col-lg-3"> <MudNavLink Href="/AdminSettings/CouchLogSettings"
<AdminSettingsNavMenu /> Icon="@Icons.Material.Filled.Settings">
</div> CouchLog Settings
<div class="col-lg-9"> </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 @Body
</div> </MudItem>
</div> </MudGrid>
</div> </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>
@@ -5,7 +5,7 @@
<MudCardMedia Image="@GlobalEntity.PicturePath" Height="400" Style="object-fit: contain;"/> <MudCardMedia Image="@GlobalEntity.PicturePath" Height="400" Style="object-fit: contain;"/>
</MudPaper> </MudPaper>
<MudCardContent> <MudCardContent>
<MudText Typo="Typo.h5">@GlobalEntity.Title</MudText> <MudText Typo="Typo.h5" Style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">@GlobalEntity.Title</MudText>
</MudCardContent> </MudCardContent>
<MudCardActions> <MudCardActions>
<MudTooltip Text="Add to shared List"> <MudTooltip Text="Add to shared List">
@@ -11,19 +11,9 @@
@inject ISnackbar Snackbar @inject ISnackbar Snackbar
<style> <style>
.drop-zone-hover-overlay { .mud-file-upload-dragarea {
position: absolute; height: 400px;
inset: 0; width: 100%;
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;
} }
</style> </style>
@@ -36,57 +26,31 @@
<EditForm EditContext="_editContext" OnSubmit="HandleSubmit"> <EditForm EditContext="_editContext" OnSubmit="HandleSubmit">
<DataAnnotationsValidator /> <DataAnnotationsValidator />
<MudGrid Style="min-height: 400px;"> <MudStack Row="true" Spacing="3" AlignItems="AlignItems.Start" Style="min-height: 400px;">
<MudItem xs="5" Style="display: flex;"> <!--<editor-fold desc="Picture-Upload">-->
<MudFileUpload T="IBrowserFile" @ref="_fileUpload" FilesChanged="OnPictureUploaded" Accept="image/*" Style="width: 100%; display: flex;"> <MudPaper Class="pa-0" Elevation="0" Width="280px" MinWidth="280px">
<ActivatorContent> @if (!string.IsNullOrWhiteSpace(_imagePreview))
<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%;"> <MudImage Src="@_imagePreview"
<img src="@_imagePreviewUrl" ObjectFit="ObjectFit.Contain"
alt="Preview" Class="rounded"
style="width: 100%; height: 100%; object-fit: cover; border-radius: 6px; display: block;" /> Style="width: 280px; height: 400px;"/>
<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 else
{ {
<MudStack AlignItems="AlignItems.Center" Spacing="2"> <MudFileUpload @bind-Files="_pictureFile"
<MudIcon Icon="@Icons.Material.Filled.CloudUpload" DragAndDrop="true"
Size="Size.Large" OnFilesChanged="OnPictureUploaded"
Color="@(_isDragOver ? Color.Primary : Color.Secondary)" /> Accept="image/*"
<MudText Typo="Typo.body2" InputClass="null"
Align="Align.Center" Class="mud-file-upload-dragarea">
Color="@(_isDragOver ? Color.Primary : Color.Secondary)"> <SelectedTemplate Context="SelectedTemplateContext"/>
Bild hierher ziehen<br />oder klicken zum Auswählen
</MudText>
</MudStack>
}
</div>
</ActivatorContent>
</MudFileUpload> </MudFileUpload>
</MudItem> }
</MudPaper>
<!--</editor-fold>-->
<MudItem xs="7"> <MudPaper Class="flex-grow-1" Elevation="0">
<MudStack Spacing="3"> <MudStack Spacing="3">
<MudTextField @bind-Value="_form.Title" <MudTextField @bind-Value="_form.Title"
@@ -127,8 +91,8 @@
} }
</MudButton> </MudButton>
</MudStack> </MudStack>
</MudItem> </MudPaper>
</MudGrid> </MudStack>
</EditForm> </EditForm>
</DialogContent> </DialogContent>
@@ -148,14 +112,17 @@
private List<Genre> _genres = new(); private List<Genre> _genres = new();
private GlobalEntityFormModel _form = new(); private GlobalEntityFormModel _form = new();
private MudFileUpload<IBrowserFile>? _fileUpload; private string? _imagePreview;
private IBrowserFile? _picture;
private string? _imagePreviewUrl;
private bool _isDragOver;
private int _selectedMediaTypeId; private int _selectedMediaTypeId;
private bool _isPrivate; private bool _isPrivate;
private bool _isSaving; private bool _isSaving;
private IEnumerable<int> _selectedGenreIds = new HashSet<int>(); private IReadOnlyCollection<int> _selectedGenreIds = Array.Empty<int>();
//Picture
private IBrowserFile? _pictureFile;
private string _pictureBase64;
private string? _pictureContentType;
private string? _pictureExtension;
private class GlobalEntityFormModel private class GlobalEntityFormModel
{ {
@@ -164,15 +131,6 @@
public string Title { get; set; } = string.Empty; 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() protected override async Task OnInitializedAsync()
{ {
_editContext = new EditContext(_form); _editContext = new EditContext(_form);
@@ -194,17 +152,27 @@
_selectedMediaTypeId = _mediaTypes.First().Id; _selectedMediaTypeId = _mediaTypes.First().Id;
} }
private async Task OnPictureUploaded(IBrowserFile file) private async Task OnPictureUploaded()
{ {
_picture = file; if (_pictureFile == null)
{
const long maxPreviewSize = 5 * 1024 * 1024; Snackbar.Add("No file selected!", Severity.Warning);
await using var stream = file.OpenReadStream(maxPreviewSize); 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(); using var ms = new MemoryStream();
await stream.CopyToAsync(ms); await stream.CopyToAsync(ms);
var base64 = Convert.ToBase64String(ms.ToArray()); _pictureContentType = _pictureFile.ContentType;
_imagePreviewUrl = $"data:{file.ContentType};base64,{base64}"; _pictureExtension = Path.GetExtension(_pictureFile.Name);
_pictureBase64 = Convert.ToBase64String(ms.ToArray());
_imagePreview = $"data:{_pictureFile.ContentType};base64,{_pictureBase64}";
} }
private void Cancel() => MudDialog.Cancel(); private void Cancel() => MudDialog.Cancel();
@@ -213,7 +181,7 @@
{ {
if (!ctx.Validate()) return; if (!ctx.Validate()) return;
if (_picture is null) if (_pictureFile is null)
{ {
Snackbar.Add("Please upload a picture.", Severity.Warning); Snackbar.Add("Please upload a picture.", Severity.Warning);
return; return;
@@ -229,14 +197,10 @@
try try
{ {
string safeTitle = _form.Title.Replace(" ", "-"); string fileName = $"{Guid.NewGuid()}{_pictureExtension}";
string fileName = $"{safeTitle}-{Guid.NewGuid()}{Path.GetExtension(_picture.Name)}";
string picturePath = Path.Combine("wwwroot", "Pictures", fileName); string picturePath = Path.Combine("wwwroot", "Pictures", fileName);
await using (var fs = File.Create(picturePath)) await File.WriteAllBytesAsync(picturePath, Convert.FromBase64String(_pictureBase64));
{
await _picture.OpenReadStream(maxAllowedSize: 10_000_000).CopyToAsync(fs);
}
var entity = new GlobalEntity var entity = new GlobalEntity
{ {
@@ -151,7 +151,7 @@
private async Task OnNewEntity(GlobalEntity entity) private async Task OnNewEntity(GlobalEntity entity)
{ {
_globalEntities.Add(entity); _globalEntities.Insert(0, entity);
await RefreshPage(); await RefreshPage();
} }
@@ -178,6 +178,8 @@
CreationTime = DateTime.Now, CreationTime = DateTime.Now,
GlobalEntityId = globalEntity.Id, GlobalEntityId = globalEntity.Id,
UserWatchStatusId = 1, UserWatchStatusId = 1,
//Season = 0,
//Episode = 0
}; };
CouchLogDb.PrivateEntities.Add(privateEntity); CouchLogDb.PrivateEntities.Add(privateEntity);
@@ -1,9 +1,9 @@
@using CouchLog.Data @using CouchLog.Data
<MudCard Style="max-width: 500px; background-color: #2a2a2a; color: white;" Elevation="4"> <MudCard Elevation="4">
<MudCardContent Class="pa-0"> <MudCardContent Class="pa-0">
<MudGrid Spacing="0"> <MudGrid Spacing="0">
<!-- Poster Image --> <!--<editor-fold desc="Poster-Image">-->
<MudItem xs="4"> <MudItem xs="4">
<MudImage <MudImage
Src="@PrivateEntity.GlobalEntity.PicturePath" Src="@PrivateEntity.GlobalEntity.PicturePath"
@@ -11,73 +11,89 @@
ObjectFit="ObjectFit.Cover" ObjectFit="ObjectFit.Cover"
Style="width: 100%; height: 100%; min-height: 180px; border-radius: 4px 0 0 4px;" /> Style="width: 100%; height: 100%; min-height: 180px; border-radius: 4px 0 0 4px;" />
</MudItem> </MudItem>
<!--</editor-fold>-->
<!-- Content -->
<MudItem xs="8"> <MudItem xs="8">
<MudStack Class="pa-3" Spacing="2"> <MudStack Class="pa-3" Spacing="2">
<!-- Title Row with Menu --> <!--<editor-fold desc="Options">-->
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween"> <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" Style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap">@PrivateEntity.GlobalEntity.Title</MudText>
<MudMenu Icon="@Icons.Material.Filled.MoreVert" Color="Color.Default" Dense="true"> <MudMenu Icon="@Icons.Material.Filled.MoreVert" Color="Color.Default" Dense="true">
<MudMenuItem>Bearbeiten</MudMenuItem> <MudMenuItem>Edit</MudMenuItem>
<MudMenuItem>Entfernen</MudMenuItem> <MudMenuItem OnClick="@(() => OnRemove.InvokeAsync())">Remove</MudMenuItem>
</MudMenu> </MudMenu>
</MudStack> </MudStack>
<!--</editor-fold>-->
<!-- Date --> <!--<editor-fold desc="Dropdown-States">-->
<!-- <MudText Typo="Typo.caption" Style="color: #aaaaaa;">@Date.ToString("MM/dd/yyyy")</MudText> -->
<!-- Status Dropdown -->
<MudSelect <MudSelect
T="string" T="int"
@bind-Value="@PrivateEntity.UserWatchStatus!.Name" Value="PrivateEntity.UserWatchStatus!.Id"
ValueChanged="@(newStatusId => OnWatchStatusChange.InvokeAsync(newStatusId))"
Dense="true" Dense="true"
Variant="Variant.Outlined" Variant="Variant.Outlined"
Style="background-color: #3a3a3a; color: white;"
Label=""> Label="">
<!--@foreach (var status in ) @foreach (var status in UserWatchStatuses)
{ {
<MudSelectItem Value="@status">@status</MudSelectItem> <MudSelectItem Value="status.Id">@status.Name</MudSelectItem>
}--> }
</MudSelect> </MudSelect>
<!--</editor-fold>-->
<!-- Season Dropdown --> <!-- @if (PrivateEntity.GlobalEntity.MediaType.Name == "Series" || PrivateEntity.GlobalEntity.MediaType.Name == "Anime") -->
<!--{-->
<!--<editor-fold desc="Dropdown-Season">-->
<MudSelect <MudSelect
T="int?" T="int?"
@bind-Value="@PrivateEntity.Season" Value="PrivateEntity.Season"
ValueChanged="@(newSeason => OnSeasonChange.InvokeAsync(newSeason))"
Dense="true" Dense="true"
Variant="Variant.Outlined" Variant="Variant.Outlined"
Style="background-color: #3a3a3a; color: white;" Label=""
Label=""> Class="@(PrivateEntity.GlobalEntity.MediaType.Name == "Series" ? "" : "invisible")">
<!-- @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> </MudSelect>
<!--</editor-fold>-->
<!-- Episode Dropdown --> <!--<editor-fold desc="Dropdown-Episode">-->
<MudSelect <MudSelect
T="int?" T="int?"
@bind-Value="@PrivateEntity.Episode" Value="PrivateEntity.Episode"
ValueChanged="@(newEpisode => OnEpisodeChange.InvokeAsync(newEpisode))"
Dense="true" Dense="true"
Variant="Variant.Outlined" Variant="Variant.Outlined"
Style="background-color: #3a3a3a; color: white;" Label=""
Label=""> Class="@(PrivateEntity.GlobalEntity.MediaType.Name == "Series" ? "" : "invisible")">
<!--@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> </MudSelect>
<!--</editor-fold>-->
<!--}-->
<!-- Details Button --> <!--<editor-fold desc="Details Button">-->
<MudStack Row="true" Justify="Justify.FlexEnd"> <MudStack Row="true" Justify="Justify.FlexEnd">
<MudButton <MudButton
Variant="Variant.Outlined" Variant="Variant.Outlined"
Size="Size.Small" Size="Size.Small">
Style="color: #aaaaaa; border-color: #555555;">
Details Details
</MudButton> </MudButton>
</MudStack> </MudStack>
<!--</editor-fold>-->
</MudStack> </MudStack>
</MudItem> </MudItem>
</MudGrid> </MudGrid>
@@ -88,4 +104,10 @@
{ {
[Parameter] public PrivateEntity PrivateEntity { get; set; } = null!; [Parameter] public PrivateEntity PrivateEntity { get; set; } = null!;
[Parameter] public List<UserWatchStatus> UserWatchStatuses { 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> </MudTooltip>
</MudStack> </MudStack>
<MudCollapse Expanded="_showFilters"> <MudCollapse Expanded="_showFilters">
@*Entity allready added*@ No Filters avalible yet!
<!--@*Entity allready added*@
<MudSwitch @bind-Value="_showAlreadyAddedEntities" Color="Color.Primary"> <MudSwitch @bind-Value="_showAlreadyAddedEntities" Color="Color.Primary">
Show already added Entities Show already added Entities
</MudSwitch> </MudSwitch>-->
@*To be exstendet*@ @*To be exstendet*@
</MudCollapse> </MudCollapse>
</MudPaper> </MudPaper>
@@ -72,7 +73,16 @@
<MudGrid Spacing="6" Justify="Justify.Center"> <MudGrid Spacing="6" Justify="Justify.Center">
@foreach (var privateEntity in VisibleActiveWatchingEntities) @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> </MudGrid>
</MudContainer> </MudContainer>
@@ -85,14 +95,24 @@
</MudContainer> </MudContainer>
<!--</editor-fold>--> <!--</editor-fold>-->
<br/>
<!--<editor-fold desc="Library">--> <!--<editor-fold desc="Library">-->
<MudContainer MaxWidth="MaxWidth.False"> <MudContainer MaxWidth="MaxWidth.False">
<MudText Typo="Typo.h4">Library</MudText> <MudText Typo="Typo.h4">Library</MudText>
<br/>
<MudGrid Spacing="6" Justify="Justify.Center"> <MudGrid Spacing="6" Justify="Justify.Center">
@foreach (var privateEntity in VisibleLibraryEntities) @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> </MudGrid>
</MudContainer> </MudContainer>
@@ -119,13 +139,13 @@
//Paging Active Watching //Paging Active Watching
private int _currentActiveWatchingPage = 1; private int _currentActiveWatchingPage = 1;
private int _pageSizeActiveWatching = 12; private readonly int _pageSizeActiveWatching = 12;
private int TotalPagesActiveWatching => (int)Math.Ceiling(ActiveWatchingEntities.Count() / (double)_pageSizeActiveWatching); private int TotalPagesActiveWatching => (int)Math.Ceiling(ActiveWatchingEntities.Count() / (double)_pageSizeActiveWatching);
private IEnumerable<PrivateEntity> VisibleActiveWatchingEntities => ActiveWatchingEntities.Skip((_currentActiveWatchingPage - 1) * _pageSizeActiveWatching).Take(_pageSizeActiveWatching); private IEnumerable<PrivateEntity> VisibleActiveWatchingEntities => ActiveWatchingEntities.Skip((_currentActiveWatchingPage - 1) * _pageSizeActiveWatching).Take(_pageSizeActiveWatching);
//Paging Library //Paging Library
private int _currentLibraryPage = 1; private int _currentLibraryPage = 1;
private int _pageSizeLibrary = 12; private readonly int _pageSizeLibrary = 12;
private int TotalPagesLibrary => (int)Math.Ceiling(LibraryEntities.Count() / (double)_pageSizeLibrary); private int TotalPagesLibrary => (int)Math.Ceiling(LibraryEntities.Count() / (double)_pageSizeLibrary);
private IEnumerable<PrivateEntity> VisibleLibraryEntities => LibraryEntities.Skip((_currentLibraryPage - 1) * _pageSizeLibrary).Take(_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(); private IEnumerable<PrivateEntity> LibraryEntities => FiltertGlobalEntities.Where(x => x.UserWatchStatus?.Name != "Watching").ToList();
protected override async Task OnInitializedAsync() 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(); var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
_appUser = await UserManager.GetUserAsync(authState.User); _appUser = await UserManager.GetUserAsync(authState.User);
@@ -153,8 +180,38 @@
.Include(x => x.GlobalEntity.MediaType) .Include(x => x.GlobalEntity.MediaType)
.OrderByDescending(entity => entity.LastChange ?? entity.CreationTime) .OrderByDescending(entity => entity.LastChange ?? entity.CreationTime)
.ToListAsync(); .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();
}
} }
+3 -1
View File
@@ -7,6 +7,8 @@
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackAsTool>True</PackAsTool> <PackAsTool>True</PackAsTool>
<Version>0.1.0-nightly.10</Version>
<Authors>Penry</Authors>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -77,7 +79,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="MudBlazor" Version="8.15.0" /> <PackageReference Include="MudBlazor" Version="9.5.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
+1 -1
View File
@@ -140,7 +140,7 @@ namespace CouchLog
new() { Name = "Not watched", CreationTime = DateTime.Now }, new() { Name = "Not watched", CreationTime = DateTime.Now },
new() { Name = "Watching", CreationTime = DateTime.Now }, new() { Name = "Watching", CreationTime = DateTime.Now },
new() { Name = "Finished", 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 }, new() { Name = "Aborted", CreationTime= DateTime.Now },
]; ];
+9 -1
View File
@@ -1,2 +1,10 @@
# Watchlog # 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
## About docker image Releases
There are two options:
- latest: Is the latest release Version
- nightly: Is the latest commit on the main branch
Test-Commit