Compare commits
53 Commits
94e3104773
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c5e24cd4c3 | |||
| 5fcc42a99f | |||
| 9204fed54c | |||
| 406d0f5652 | |||
| 9cd651bdfe | |||
| 96336fd637 | |||
| e7d1b4b524 | |||
| a4d7269f8f | |||
| c7d741d15b | |||
| 6d2a6c5925 | |||
| 9ccb929553 | |||
| 5e086c0598 | |||
| a7c86289b3 | |||
| b2e851d765 | |||
| 1a1d7dec7c | |||
| 103da6abe8 | |||
| 5f2fdbac87 | |||
| e0ceb7d314 | |||
| 8df80eba3b | |||
| 77875110f0 | |||
| ddbc6affd1 | |||
| cd41eea0d9 | |||
| d15ecd926c | |||
| 78774abffd | |||
| a8cc182265 | |||
| 65e03c2586 | |||
| 45c4379d53 | |||
| 55d65f4841 | |||
| 3f4feb9dc9 | |||
| 45479d9df7 | |||
| 1d077fe1cc | |||
| 2046939d8a | |||
| 02d2a26a82 | |||
| c00fb3234c | |||
| 6922303e52 | |||
| 3ffad3abbe | |||
| 68afe22526 | |||
| 28fe48c3f0 | |||
| 8cd0dc6c81 | |||
| 8c4a3def23 | |||
| ef41d46e50 | |||
| f8eba6b8dd | |||
| 99581df6f7 | |||
| 6991af3daa | |||
| 04ca70dc67 | |||
| cd159509a0 | |||
| ea7604a05f | |||
| 622a751c40 | |||
| 0f3da6952b | |||
| 59442d5203 | |||
| 594541a103 | |||
| ee7bcc1eae | |||
| 4fe354add0 |
@@ -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 }}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
private HttpContext HttpContext { get; set; } = default!;
|
private HttpContext HttpContext { get; set; } = default!;
|
||||||
|
|
||||||
[SupplyParameterFromForm]
|
[SupplyParameterFromForm]
|
||||||
private InputModel Input { get; set; } = new();
|
private InputModel Input { get; set; } = null!;
|
||||||
|
|
||||||
[SupplyParameterFromQuery]
|
[SupplyParameterFromQuery]
|
||||||
private string? RemoteError { get; set; }
|
private string? RemoteError { get; set; }
|
||||||
@@ -94,6 +94,8 @@
|
|||||||
// the login page if we get here some other way.
|
// the login page if we get here some other way.
|
||||||
RedirectManager.RedirectTo("Account/Login");
|
RedirectManager.RedirectTo("Account/Login");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Input ??= new InputModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnLoginCallbackAsync()
|
private async Task OnLoginCallbackAsync()
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
@code {
|
@code {
|
||||||
[SupplyParameterFromForm]
|
[SupplyParameterFromForm]
|
||||||
private InputModel Input { get; set; } = new();
|
private InputModel Input { get; set; } = null!;
|
||||||
|
|
||||||
private async Task OnValidSubmitAsync()
|
private async Task OnValidSubmitAsync()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
@* MudTextFields zwingend mit Name-Attribut für SSR *@
|
@* MudTextFields zwingend mit Name-Attribut für SSR *@
|
||||||
<MudTextField @bind-Value="Input.Username"
|
<MudTextField @bind-Value="Input.Username"
|
||||||
For="@(() => Input.Username)"
|
For="@(() => Input.Username)"
|
||||||
Name="Input.Username"
|
name="Input.Username"
|
||||||
Label="Username"
|
Label="Username"
|
||||||
Variant="Variant.Outlined"
|
Variant="Variant.Outlined"
|
||||||
Margin="Margin.Dense"
|
Margin="Margin.Dense"
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
<MudTextField @bind-Value="Input.Password"
|
<MudTextField @bind-Value="Input.Password"
|
||||||
For="@(() => Input.Password)"
|
For="@(() => Input.Password)"
|
||||||
Name="Input.Password"
|
name="Input.Password"
|
||||||
Label="Password"
|
Label="Password"
|
||||||
InputType="InputType.Password"
|
InputType="InputType.Password"
|
||||||
Variant="Variant.Outlined"
|
Variant="Variant.Outlined"
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
private HttpContext HttpContext { get; set; } = default!;
|
private HttpContext HttpContext { get; set; } = default!;
|
||||||
|
|
||||||
[SupplyParameterFromForm]
|
[SupplyParameterFromForm]
|
||||||
private InputModel Input { get; set; } = new();
|
private InputModel Input { get; set; } = null!;
|
||||||
|
|
||||||
[SupplyParameterFromQuery]
|
[SupplyParameterFromQuery]
|
||||||
private string? ReturnUrl { get; set; }
|
private string? ReturnUrl { get; set; }
|
||||||
@@ -87,6 +87,8 @@
|
|||||||
// Clear the existing external cookie to ensure a clean login process
|
// Clear the existing external cookie to ensure a clean login process
|
||||||
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
|
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Input ??= new InputModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoginUser()
|
public async Task LoginUser()
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
private ApplicationUser user = default!;
|
private ApplicationUser user = default!;
|
||||||
|
|
||||||
[SupplyParameterFromForm]
|
[SupplyParameterFromForm]
|
||||||
private InputModel Input { get; set; } = new();
|
private InputModel Input { get; set; } = null!;
|
||||||
|
|
||||||
[SupplyParameterFromQuery]
|
[SupplyParameterFromQuery]
|
||||||
private string? ReturnUrl { get; set; }
|
private string? ReturnUrl { get; set; }
|
||||||
@@ -62,6 +62,8 @@
|
|||||||
// Ensure the user has gone through the username & password screen first
|
// Ensure the user has gone through the username & password screen first
|
||||||
user = await SignInManager.GetTwoFactorAuthenticationUserAsync() ??
|
user = await SignInManager.GetTwoFactorAuthenticationUserAsync() ??
|
||||||
throw new InvalidOperationException("Unable to load two-factor authentication user.");
|
throw new InvalidOperationException("Unable to load two-factor authentication user.");
|
||||||
|
|
||||||
|
Input ??= new InputModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnValidSubmitAsync()
|
private async Task OnValidSubmitAsync()
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
private ApplicationUser user = default!;
|
private ApplicationUser user = default!;
|
||||||
|
|
||||||
[SupplyParameterFromForm]
|
[SupplyParameterFromForm]
|
||||||
private InputModel Input { get; set; } = new();
|
private InputModel Input { get; set; } = null!;
|
||||||
|
|
||||||
[SupplyParameterFromQuery]
|
[SupplyParameterFromQuery]
|
||||||
private string? ReturnUrl { get; set; }
|
private string? ReturnUrl { get; set; }
|
||||||
@@ -48,6 +48,8 @@
|
|||||||
// Ensure the user has gone through the username & password screen first
|
// Ensure the user has gone through the username & password screen first
|
||||||
user = await SignInManager.GetTwoFactorAuthenticationUserAsync() ??
|
user = await SignInManager.GetTwoFactorAuthenticationUserAsync() ??
|
||||||
throw new InvalidOperationException("Unable to load two-factor authentication user.");
|
throw new InvalidOperationException("Unable to load two-factor authentication user.");
|
||||||
|
|
||||||
|
Input ??= new InputModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnValidSubmitAsync()
|
private async Task OnValidSubmitAsync()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@page "/Account/Manage/ChangePassword"
|
@page "/Account/ChangePassword"
|
||||||
|
|
||||||
@using System.ComponentModel.DataAnnotations
|
@using System.ComponentModel.DataAnnotations
|
||||||
@using Microsoft.AspNetCore.Identity
|
@using Microsoft.AspNetCore.Identity
|
||||||
@@ -9,67 +9,81 @@
|
|||||||
@inject IdentityUserAccessor UserAccessor
|
@inject IdentityUserAccessor UserAccessor
|
||||||
@inject IdentityRedirectManager RedirectManager
|
@inject IdentityRedirectManager RedirectManager
|
||||||
@inject ILogger<ChangePassword> Logger
|
@inject ILogger<ChangePassword> Logger
|
||||||
|
@inject ISnackbar Snackbar
|
||||||
|
|
||||||
<PageTitle>Change password</PageTitle>
|
<PageTitle>Change password</PageTitle>
|
||||||
|
|
||||||
<h3>Change password</h3>
|
<MudText Typo="Typo.h6">Change password</MudText>
|
||||||
<StatusMessage Message="@message" />
|
|
||||||
<div class="row">
|
<EditForm Model="Input" FormName="change-password" OnValidSubmit="OnValidSubmitAsync" method="post">
|
||||||
<div class="col-xl-6">
|
|
||||||
<EditForm Model="Input" FormName="change-password" OnValidSubmit="OnValidSubmitAsync" method="post">
|
<MudTextField
|
||||||
<DataAnnotationsValidator />
|
T="string"
|
||||||
<ValidationSummary class="text-danger" role="alert" />
|
name="Input.OldPassword"
|
||||||
<div class="form-floating mb-3">
|
Label="OldPassword"
|
||||||
<InputText type="password" @bind-Value="Input.OldPassword" id="Input.OldPassword" class="form-control" autocomplete="current-password" aria-required="true" placeholder="Enter the old password" />
|
@bind-Value="Input.OldPassword"
|
||||||
<label for="Input.OldPassword" class="form-label">Old password</label>
|
For="@(() => Input.OldPassword)"
|
||||||
<ValidationMessage For="() => Input.OldPassword" class="text-danger" />
|
InputType="InputType.Password"
|
||||||
</div>
|
Required="true"
|
||||||
<div class="form-floating mb-3">
|
Variant="Variant.Outlined"
|
||||||
<InputText type="password" @bind-Value="Input.NewPassword" id="Input.NewPassword" class="form-control" autocomplete="new-password" aria-required="true" placeholder="Enter the new password" />
|
ShrinkLabel="true"/>
|
||||||
<label for="Input.NewPassword" class="form-label">New password</label>
|
|
||||||
<ValidationMessage For="() => Input.NewPassword" class="text-danger" />
|
<MudTextField
|
||||||
</div>
|
T="string"
|
||||||
<div class="form-floating mb-3">
|
name="Input.NewPassword"
|
||||||
<InputText type="password" @bind-Value="Input.ConfirmPassword" id="Input.ConfirmPassword" class="form-control" autocomplete="new-password" aria-required="true" placeholder="Enter the new password" />
|
Label="New password"
|
||||||
<label for="Input.ConfirmPassword" class="form-label">Confirm password</label>
|
@bind-Value="Input.NewPassword"
|
||||||
<ValidationMessage For="() => Input.ConfirmPassword" class="text-danger" />
|
For="@(() => Input.NewPassword)"
|
||||||
</div>
|
InputType="InputType.Password"
|
||||||
<button type="submit" class="w-100 btn btn-lg btn-primary">Update password</button>
|
Required="true"
|
||||||
</EditForm>
|
Variant="Variant.Outlined"
|
||||||
</div>
|
ShrinkLabel="true"/>
|
||||||
</div>
|
|
||||||
|
<MudTextField
|
||||||
|
T="string"
|
||||||
|
name="Input.ConfirmPassword"
|
||||||
|
Label="Confirm password"
|
||||||
|
@bind-Value="Input.ConfirmPassword"
|
||||||
|
For="@(() => Input.ConfirmPassword)"
|
||||||
|
InputType="InputType.Password"
|
||||||
|
Required="true"
|
||||||
|
Variant="Variant.Outlined"
|
||||||
|
ShrinkLabel="true"/>
|
||||||
|
|
||||||
|
<MudButton ButtonType="ButtonType.Submit" Variant="Variant.Filled" Color="Color.Primary">Change password</MudButton>
|
||||||
|
</EditForm>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private string? message;
|
private ApplicationUser _user = null!;
|
||||||
private ApplicationUser user = default!;
|
private bool _hasPassword;
|
||||||
private bool hasPassword;
|
|
||||||
|
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
private HttpContext HttpContext { get; set; } = default!;
|
private HttpContext HttpContext { get; set; } = null!;
|
||||||
|
|
||||||
[SupplyParameterFromForm]
|
[SupplyParameterFromForm]
|
||||||
private InputModel Input { get; set; } = new();
|
private InputModel Input { get; set; } = null!;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
user = await UserAccessor.GetRequiredUserAsync(HttpContext);
|
_user = await UserAccessor.GetRequiredUserAsync(HttpContext);
|
||||||
hasPassword = await UserManager.HasPasswordAsync(user);
|
_hasPassword = await UserManager.HasPasswordAsync(_user);
|
||||||
if (!hasPassword)
|
if (!_hasPassword)
|
||||||
{
|
{
|
||||||
RedirectManager.RedirectTo("Account/Manage/SetPassword");
|
RedirectManager.RedirectTo("Account/Manage/SetPassword");
|
||||||
}
|
}
|
||||||
|
Input ??= new InputModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnValidSubmitAsync()
|
private async Task OnValidSubmitAsync()
|
||||||
{
|
{
|
||||||
var changePasswordResult = await UserManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword);
|
var changePasswordResult = await UserManager.ChangePasswordAsync(_user, Input.OldPassword, Input.NewPassword);
|
||||||
if (!changePasswordResult.Succeeded)
|
if (!changePasswordResult.Succeeded)
|
||||||
{
|
{
|
||||||
message = $"Error: {string.Join(",", changePasswordResult.Errors.Select(error => error.Description))}";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await SignInManager.RefreshSignInAsync(user);
|
await SignInManager.RefreshSignInAsync(_user);
|
||||||
Logger.LogInformation("User changed their password successfully.");
|
Logger.LogInformation("User changed their password successfully.");
|
||||||
|
|
||||||
RedirectManager.RedirectToCurrentPageWithStatus("Your password has been changed", HttpContext);
|
RedirectManager.RedirectToCurrentPageWithStatus("Your password has been changed", HttpContext);
|
||||||
|
|||||||
@@ -47,13 +47,15 @@
|
|||||||
private HttpContext HttpContext { get; set; } = default!;
|
private HttpContext HttpContext { get; set; } = default!;
|
||||||
|
|
||||||
[SupplyParameterFromForm]
|
[SupplyParameterFromForm]
|
||||||
private InputModel Input { get; set; } = new();
|
private InputModel Input { get; set; } = null!;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
Input ??= new();
|
Input ??= new();
|
||||||
user = await UserAccessor.GetRequiredUserAsync(HttpContext);
|
user = await UserAccessor.GetRequiredUserAsync(HttpContext);
|
||||||
requirePassword = await UserManager.HasPasswordAsync(user);
|
requirePassword = await UserManager.HasPasswordAsync(user);
|
||||||
|
|
||||||
|
Input ??= new InputModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnValidSubmitAsync()
|
private async Task OnValidSubmitAsync()
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
private HttpContext HttpContext { get; set; } = default!;
|
private HttpContext HttpContext { get; set; } = default!;
|
||||||
|
|
||||||
[SupplyParameterFromForm(FormName = "change-email")]
|
[SupplyParameterFromForm(FormName = "change-email")]
|
||||||
private InputModel Input { get; set; } = new();
|
private InputModel Input { get; set; } = null!;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
@@ -72,6 +72,7 @@
|
|||||||
isEmailConfirmed = await UserManager.IsEmailConfirmedAsync(user);
|
isEmailConfirmed = await UserManager.IsEmailConfirmedAsync(user);
|
||||||
|
|
||||||
Input.NewEmail ??= email;
|
Input.NewEmail ??= email;
|
||||||
|
Input ??= new InputModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnValidSubmitAsync()
|
private async Task OnValidSubmitAsync()
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ else
|
|||||||
private HttpContext HttpContext { get; set; } = default!;
|
private HttpContext HttpContext { get; set; } = default!;
|
||||||
|
|
||||||
[SupplyParameterFromForm]
|
[SupplyParameterFromForm]
|
||||||
private InputModel Input { get; set; } = new();
|
private InputModel Input { get; set; } = null!;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,27 +11,14 @@
|
|||||||
|
|
||||||
<PageTitle>Profile</PageTitle>
|
<PageTitle>Profile</PageTitle>
|
||||||
|
|
||||||
<h3>Profile</h3>
|
<MudText Typo="Typo.h6">Profile</MudText>
|
||||||
<StatusMessage />
|
<StatusMessage />
|
||||||
|
|
||||||
<div class="row">
|
<MudContainer Class="pa-0 ma-0">
|
||||||
<div class="col-xl-6">
|
<MudTextField Value="@username" Variant="Variant.Outlined" Label="Username"/>
|
||||||
<EditForm Model="Input" FormName="profile" OnValidSubmit="OnValidSubmitAsync" method="post">
|
</MudContainer>
|
||||||
<DataAnnotationsValidator />
|
|
||||||
<ValidationSummary class="text-danger" role="alert" />
|
|
||||||
<div class="form-floating mb-3">
|
|
||||||
<input type="text" value="@username" id="username" class="form-control" placeholder="Choose your username." disabled />
|
|
||||||
<label for="username" class="form-label">Username</label>
|
|
||||||
</div>
|
|
||||||
<!--<div class="form-floating mb-3">
|
|
||||||
<InputText @bind-Value="Input.PhoneNumber" id="Input.PhoneNumber" class="form-control" placeholder="Enter your phone number" />
|
|
||||||
<label for="Input.PhoneNumber" class="form-label">Phone number</label>
|
|
||||||
<ValidationMessage For="() => Input.PhoneNumber" class="text-danger" />
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="w-100 btn btn-lg btn-primary">Save</button>-->
|
|
||||||
</EditForm>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private ApplicationUser user = default!;
|
private ApplicationUser user = default!;
|
||||||
@@ -42,7 +29,7 @@
|
|||||||
private HttpContext HttpContext { get; set; } = default!;
|
private HttpContext HttpContext { get; set; } = default!;
|
||||||
|
|
||||||
[SupplyParameterFromForm]
|
[SupplyParameterFromForm]
|
||||||
private InputModel Input { get; set; } = new();
|
private InputModel Input { get; set; } = null!;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
private HttpContext HttpContext { get; set; } = default!;
|
private HttpContext HttpContext { get; set; } = default!;
|
||||||
|
|
||||||
[SupplyParameterFromForm]
|
[SupplyParameterFromForm]
|
||||||
private InputModel Input { get; set; } = new();
|
private InputModel Input { get; set; } = null!;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
private IEnumerable<IdentityError>? identityErrors;
|
private IEnumerable<IdentityError>? identityErrors;
|
||||||
|
|
||||||
[SupplyParameterFromForm]
|
[SupplyParameterFromForm]
|
||||||
private InputModel Input { get; set; } = new();
|
private InputModel Input { get; set; } = null!;
|
||||||
|
|
||||||
[SupplyParameterFromQuery]
|
[SupplyParameterFromQuery]
|
||||||
private string? ReturnUrl { get; set; }
|
private string? ReturnUrl { get; set; }
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
private string? message;
|
private string? message;
|
||||||
|
|
||||||
[SupplyParameterFromForm]
|
[SupplyParameterFromForm]
|
||||||
private InputModel Input { get; set; } = new();
|
private InputModel Input { get; set; } = null!;
|
||||||
|
|
||||||
private async Task OnValidSubmitAsync()
|
private async Task OnValidSubmitAsync()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
private IEnumerable<IdentityError>? identityErrors;
|
private IEnumerable<IdentityError>? identityErrors;
|
||||||
|
|
||||||
[SupplyParameterFromForm]
|
[SupplyParameterFromForm]
|
||||||
private InputModel Input { get; set; } = new();
|
private InputModel Input { get; set; } = null!;
|
||||||
|
|
||||||
[SupplyParameterFromQuery]
|
[SupplyParameterFromQuery]
|
||||||
private string? Code { get; set; }
|
private string? Code { get; set; }
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
@inherits LayoutComponentBase
|
@inherits LayoutComponentBase
|
||||||
@layout CouchLog.Components.Layout.MainLayout
|
@layout CouchLog.Components.Layout.MainLayout
|
||||||
|
|
||||||
<h1>Manage your account</h1>
|
<MudText Typo="Typo.h2">Manage your account</MudText>
|
||||||
|
|
||||||
<div>
|
<MudContainer MaxWidth="MaxWidth.False" Class="pa-4">
|
||||||
<h2>Change your account settings</h2>
|
<MudText Typo="Typo.h4">Change your account settings</MudText>
|
||||||
<hr />
|
<hr/>
|
||||||
<div class="row">
|
<MudGrid>
|
||||||
<div class="col-lg-3">
|
<MudItem xs="12" md="3" lg="2">
|
||||||
<ManageNavMenu />
|
<ManageNavMenu/>
|
||||||
</div>
|
</MudItem>
|
||||||
<div class="col-lg-9">
|
|
||||||
|
<MudItem xs="12" md="9" lg="10">
|
||||||
@Body
|
@Body
|
||||||
</div>
|
</MudItem>
|
||||||
</div>
|
</MudGrid>
|
||||||
</div>
|
</MudContainer>
|
||||||
@@ -3,33 +3,41 @@
|
|||||||
|
|
||||||
@inject SignInManager<ApplicationUser> SignInManager
|
@inject SignInManager<ApplicationUser> SignInManager
|
||||||
|
|
||||||
<ul class="nav nav-pills flex-column">
|
<MudNavMenu>
|
||||||
<li class="nav-item">
|
<MudNavLink Href="Account/Manage"
|
||||||
<NavLink class="nav-link" href="Account/Manage" Match="NavLinkMatch.All">Profile</NavLink>
|
Icon="@Icons.Material.Filled.ManageAccounts">
|
||||||
</li>
|
Manage
|
||||||
|
</MudNavLink>
|
||||||
<!--
|
<!--
|
||||||
<li class="nav-item">
|
<MudNavLink Href="Account/Manage/Email"
|
||||||
<NavLink class="nav-link" href="Account/Manage/Email">Email</NavLink>
|
Icon="@Icons.Material.Filled.Email">
|
||||||
</li>
|
Email
|
||||||
|
</MudNavLink>
|
||||||
-->
|
-->
|
||||||
<li class="nav-item">
|
<MudNavLink Href="Account/ChangePassword"
|
||||||
<NavLink class="nav-link" href="Account/Manage/ChangePassword">Password</NavLink>
|
Icon="@Icons.Material.Filled.Password">
|
||||||
</li>
|
Password
|
||||||
|
</MudNavLink>
|
||||||
@if (hasExternalLogins)
|
@if (hasExternalLogins)
|
||||||
{
|
{
|
||||||
<li class="nav-item">
|
<MudNavLink Href="Account/Manage/ExternalLogins"
|
||||||
<NavLink class="nav-link" href="Account/Manage/ExternalLogins">External logins</NavLink>
|
Icon="@Icons.Material.Filled.Login">
|
||||||
</li>
|
External logins
|
||||||
|
</MudNavLink>
|
||||||
}
|
}
|
||||||
<!--
|
<!--
|
||||||
<li class="nav-item">
|
<MudNavLink Href="Account/Manage/TwoFactorAuthentication"
|
||||||
<NavLink class="nav-link" href="Account/Manage/TwoFactorAuthentication">Two-factor authentication</NavLink>
|
Icon="@Icons.Material.Filled.FactCheck">
|
||||||
</li>
|
Two-factor authentication
|
||||||
|
</MudNavLink>
|
||||||
-->
|
-->
|
||||||
<li class="nav-item">
|
<!--
|
||||||
<NavLink class="nav-link" href="Account/Manage/PersonalData">Personal data</NavLink>
|
<MudNavLink Href="Account/Manage/PersonalData"
|
||||||
</li>
|
Icon="@Icons.Material.Filled.DataObject">
|
||||||
</ul>
|
Personal data
|
||||||
|
</MudNavLink>
|
||||||
|
-->
|
||||||
|
</MudNavMenu>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private bool hasExternalLogins;
|
private bool hasExternalLogins;
|
||||||
|
|||||||
@@ -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
|
ValueChanged="OnRegistrationChanged"
|
||||||
{
|
Color="Color.Primary" />
|
||||||
<EditForm Model="accountsSettings">
|
</EditForm>
|
||||||
<div class="form-check form-switch">
|
|
||||||
<InputCheckbox class="form-check-input"
|
|
||||||
role="switch"
|
|
||||||
id="IsRegistrationallowedInput"
|
|
||||||
Value="accountsSettings.IsRegistrationAllowed"
|
|
||||||
ValueChanged="OnRegistrationChanged"
|
|
||||||
ValueExpression="() => accountsSettings.IsRegistrationAllowed" />
|
|
||||||
|
|
||||||
|
@*<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 = null!;
|
||||||
|
//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>
|
|
||||||
@@ -1,11 +1,16 @@
|
|||||||
@using CouchLog.Data
|
@using CouchLog.Data
|
||||||
|
|
||||||
<MudCard>
|
<MudCard>
|
||||||
<MudPaper Elevation="0">
|
<MudPaper Elevation="0" Style="position: relative;" @onmouseenter="@(() => _showEditOverlay = true)" @onmouseleave="@(() => _showEditOverlay = false)">
|
||||||
<MudCardMedia Image="@GlobalEntity.PicturePath" Height="400" Style="object-fit: contain;"/>
|
<MudCardMedia Image="@($"{GlobalEntity.PicturePath}?v={DateTime.Now.Ticks}")" Height="400" Style="object-fit: contain;"/>
|
||||||
|
<MudOverlay Visible="@_showEditOverlay" DarkBackground="true" Absolute="true">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Rounded.Edit" Color="Color.Primary" OnClick="@(()=> OnEdit.InvokeAsync(GlobalEntity))" />
|
||||||
|
</MudOverlay>
|
||||||
</MudPaper>
|
</MudPaper>
|
||||||
<MudCardContent>
|
<MudCardContent>
|
||||||
<MudText Typo="Typo.h5">@GlobalEntity.Title</MudText>
|
<MudTooltip Text="@GlobalEntity.Title">
|
||||||
|
<MudText Typo="Typo.h5" Style="display: block; max-width: 250px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">@GlobalEntity.Title</MudText>
|
||||||
|
</MudTooltip>
|
||||||
</MudCardContent>
|
</MudCardContent>
|
||||||
<MudCardActions>
|
<MudCardActions>
|
||||||
<MudTooltip Text="Add to shared List">
|
<MudTooltip Text="Add to shared List">
|
||||||
@@ -34,4 +39,7 @@
|
|||||||
|
|
||||||
[Parameter] public EventCallback<GlobalEntity> OnAddToPrivate { get; set; }
|
[Parameter] public EventCallback<GlobalEntity> OnAddToPrivate { get; set; }
|
||||||
[Parameter] public EventCallback<GlobalEntity> OnAddToShared { get; set; }
|
[Parameter] public EventCallback<GlobalEntity> OnAddToShared { get; set; }
|
||||||
|
[Parameter] public EventCallback<GlobalEntity> OnEdit { get; set; }
|
||||||
|
|
||||||
|
private bool _showEditOverlay;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,18 @@
|
|||||||
@using CouchLog.Data
|
@using CouchLog.Data
|
||||||
@using Microsoft.EntityFrameworkCore
|
@using Microsoft.EntityFrameworkCore
|
||||||
@using Microsoft.AspNetCore.Authorization
|
|
||||||
@using Microsoft.AspNetCore.Identity
|
@using Microsoft.AspNetCore.Identity
|
||||||
@using System.ComponentModel.DataAnnotations
|
@using System.ComponentModel.DataAnnotations
|
||||||
|
|
||||||
@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 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>
|
||||||
|
|
||||||
@@ -33,60 +22,33 @@
|
|||||||
</TitleContent>
|
</TitleContent>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<EditForm EditContext="_editContext" OnSubmit="HandleSubmit">
|
<EditForm EditContext="_editContext" OnSubmit="HandleSubmit" id="New-Global-Entity-Form">
|
||||||
<DataAnnotationsValidator />
|
<DataAnnotationsValidator />
|
||||||
|
|
||||||
<MudGrid Style="min-height: 400px;">
|
<MudStack Row="true" Spacing="3" AlignItems="AlignItems.Start" Style="min-height: 400px;" Breakpoint="Breakpoint.SmAndDown">
|
||||||
<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)"
|
<MudImage Src="@_imagePreview"
|
||||||
@ondragleave="@(() => _isDragOver = false)"
|
ObjectFit="ObjectFit.Contain"
|
||||||
@ondragover:preventDefault="true"
|
Class="rounded"
|
||||||
@ondrop:preventDefault="true"
|
Style="width: 280px; height: 400px;"/>
|
||||||
@ondrop="@(() => _isDragOver = false)"
|
}
|
||||||
style="@GetDropZoneStyle()"
|
else
|
||||||
class="d-flex align-center justify-center cursor-pointer drop-zone-wrapper">
|
{
|
||||||
|
<MudFileUpload @bind-Files="_pictureFile"
|
||||||
@if (_imagePreviewUrl is not null)
|
DragAndDrop="true"
|
||||||
{
|
OnFilesChanged="OnPictureUploaded"
|
||||||
<div style="position: relative; width: 100%; height: 100%;">
|
Accept="image/*"
|
||||||
<img src="@_imagePreviewUrl"
|
InputClass="null"
|
||||||
alt="Preview"
|
Class="mud-file-upload-dragarea">
|
||||||
style="width: 100%; height: 100%; object-fit: cover; border-radius: 6px; display: block;" />
|
<SelectedTemplate Context="selectedTemplateContext"/>
|
||||||
<div class="drop-zone-hover-overlay">
|
</MudFileUpload>
|
||||||
<MudStack AlignItems="AlignItems.Center" Spacing="1">
|
}
|
||||||
<MudIcon Icon="@Icons.Material.Filled.Edit"
|
</MudPaper>
|
||||||
Color="Color.Surface"
|
<!--</editor-fold>-->
|
||||||
Size="Size.Large" />
|
<MudPaper Class="flex-grow-1" Elevation="0" MinWidth="200px">
|
||||||
<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>
|
|
||||||
|
|
||||||
<MudItem xs="7">
|
|
||||||
<MudStack Spacing="3">
|
<MudStack Spacing="3">
|
||||||
|
|
||||||
<MudTextField @bind-Value="_form.Title"
|
<MudTextField @bind-Value="_form.Title"
|
||||||
@@ -115,25 +77,25 @@
|
|||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
<MudCheckBox @bind-Value="_isPrivate" Label="Is Private" Color="Color.Primary" />
|
<MudCheckBox @bind-Value="_isPrivate" Label="Is Private" Color="Color.Primary" />
|
||||||
<MudButton ButtonType="ButtonType.Submit" Variant="Variant.Filled" Color="Color.Success" Disabled="_isSaving" Class="mt-3">
|
|
||||||
@if (_isSaving)
|
|
||||||
{
|
|
||||||
<MudProgressCircular Indeterminate="true" Size="Size.Small" Class="mr-2" />
|
|
||||||
<span>Saving...</span>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<span>Create Global Entity</span>
|
|
||||||
}
|
|
||||||
</MudButton>
|
|
||||||
</MudStack>
|
</MudStack>
|
||||||
</MudItem>
|
</MudPaper>
|
||||||
</MudGrid>
|
</MudStack>
|
||||||
</EditForm>
|
</EditForm>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="Cancel" Variant="Variant.Text" Disabled="_isSaving">Cancel</MudButton>
|
<MudButton OnClick="Cancel" Variant="Variant.Filled" Color="Color.Error" Disabled="_isSaving" Class="mt-3">Cancel</MudButton>
|
||||||
|
<MudButton ButtonType="ButtonType.Submit" Variant="Variant.Filled" Color="Color.Success" Disabled="_isSaving" Class="mt-3" form="New-Global-Entity-Form">
|
||||||
|
@if (_isSaving)
|
||||||
|
{
|
||||||
|
<MudProgressCircular Indeterminate="true" Size="Size.Small" Class="mr-2"/>
|
||||||
|
<span>Saving...</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>Create Global Entity</span>
|
||||||
|
}
|
||||||
|
</MudButton>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</MudDialog>
|
</MudDialog>
|
||||||
|
|
||||||
@@ -148,14 +110,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 = null!;
|
||||||
|
private string? _pictureContentType;
|
||||||
|
private string? _pictureExtension;
|
||||||
|
|
||||||
private class GlobalEntityFormModel
|
private class GlobalEntityFormModel
|
||||||
{
|
{
|
||||||
@@ -164,15 +129,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);
|
||||||
@@ -187,24 +143,34 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_mediaTypes = await CouchLogDB.MediaType.OrderBy(t => t.Id).ToListAsync();
|
_mediaTypes = await CouchLogDb.MediaType.OrderBy(t => t.Id).ToListAsync();
|
||||||
_genres = await CouchLogDB.Genres.OrderBy(t => t.Id).ToListAsync();
|
_genres = await CouchLogDb.Genres.OrderBy(t => t.Id).ToListAsync();
|
||||||
|
|
||||||
if (_mediaTypes.Count > 0)
|
if (_mediaTypes.Count > 0)
|
||||||
_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 +179,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 +195,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
|
||||||
{
|
{
|
||||||
@@ -248,19 +210,19 @@
|
|||||||
IsPrivate = _isPrivate
|
IsPrivate = _isPrivate
|
||||||
};
|
};
|
||||||
|
|
||||||
CouchLogDB.Add(entity);
|
CouchLogDb.Add(entity);
|
||||||
await CouchLogDB.SaveChangesAsync();
|
await CouchLogDb.SaveChangesAsync();
|
||||||
|
|
||||||
foreach (int genreId in _selectedGenreIds)
|
foreach (int genreId in _selectedGenreIds)
|
||||||
{
|
{
|
||||||
CouchLogDB.Add(new LinkTableGlobalGenre
|
CouchLogDb.Add(new LinkTableGlobalGenre
|
||||||
{
|
{
|
||||||
GenreId = genreId,
|
GenreId = genreId,
|
||||||
GlobalEntityId = entity.Id
|
GlobalEntityId = entity.Id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await CouchLogDB.SaveChangesAsync();
|
await CouchLogDb.SaveChangesAsync();
|
||||||
|
|
||||||
Snackbar.Add("Global Entity created!", Severity.Success);
|
Snackbar.Add("Global Entity created!", Severity.Success);
|
||||||
MudDialog.Close(DialogResult.Ok(entity));
|
MudDialog.Close(DialogResult.Ok(entity));
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -78,11 +78,12 @@
|
|||||||
<MudGrid Spacing="6" Justify="Justify.Center">
|
<MudGrid Spacing="6" Justify="Justify.Center">
|
||||||
@foreach (var globalEntity in VisibleGlobalEntities)
|
@foreach (var globalEntity in VisibleGlobalEntities)
|
||||||
{
|
{
|
||||||
<MudItem xs="12" sm="6" md="4" lg="3" xl="2">
|
<MudItem xs="12" sm="6" md="4" lg="3" xl="2" Style="min-width: 0;">
|
||||||
<GlobalEntityCard GlobalEntity="globalEntity"
|
<GlobalEntityCard GlobalEntity="globalEntity"
|
||||||
UserPrivateEntityIds="_userPrivateEntityIds"
|
UserPrivateEntityIds="_userPrivateEntityIds"
|
||||||
OnAddToPrivate="AddGlobalEntityToPrivateList"
|
OnAddToPrivate="AddGlobalEntityToPrivateList"
|
||||||
OnAddToShared="AddGlobalEntityToSharedList"/>
|
OnAddToShared="AddGlobalEntityToSharedList"
|
||||||
|
OnEdit="OpenEditGlobalEntityDialog"/>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
}
|
}
|
||||||
</MudGrid>
|
</MudGrid>
|
||||||
@@ -151,7 +152,7 @@
|
|||||||
|
|
||||||
private async Task OnNewEntity(GlobalEntity entity)
|
private async Task OnNewEntity(GlobalEntity entity)
|
||||||
{
|
{
|
||||||
_globalEntities.Add(entity);
|
_globalEntities.Insert(0, entity);
|
||||||
await RefreshPage();
|
await RefreshPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,6 +167,44 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task OpenEditGlobalEntityDialog(GlobalEntity globalEntity)
|
||||||
|
{
|
||||||
|
var parameters = new DialogParameters<EditGlobalEntityDialog>
|
||||||
|
{
|
||||||
|
{ x => x.GlobalEntity, globalEntity },
|
||||||
|
{ x => x.AppUser, _appUser}
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialog = await DialogService.ShowAsync<EditGlobalEntityDialog>("Edit Global Entity", parameters);
|
||||||
|
var result = await dialog.Result;
|
||||||
|
|
||||||
|
if (!result!.Canceled && result.Data is ValueTuple<GlobalEntity, bool> data)
|
||||||
|
{
|
||||||
|
if (data.Item2)
|
||||||
|
{
|
||||||
|
CouchLogDb.GlobalEntities.Remove(data.Item1);
|
||||||
|
await CouchLogDb.SaveChangesAsync();
|
||||||
|
|
||||||
|
_globalEntities.RemoveAll(x => x.Id == data.Item1.Id);
|
||||||
|
|
||||||
|
if (_currentPage > TotalPages)
|
||||||
|
_currentPage = Math.Max(1, TotalPages);
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.Item2)
|
||||||
|
{
|
||||||
|
int index = _globalEntities.FindIndex(x => x.Id == data.Item1.Id);
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
_globalEntities[index] = data.Item1;
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task AddGlobalEntityToPrivateList(GlobalEntity globalEntity)
|
public async Task AddGlobalEntityToPrivateList(GlobalEntity globalEntity)
|
||||||
{
|
{
|
||||||
if(_appUser is null) { Snackbar.Add("Not logged in!", Severity.Error); return;}
|
if(_appUser is null) { Snackbar.Add("Not logged in!", Severity.Error); return;}
|
||||||
@@ -178,6 +217,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,83 +1,101 @@
|
|||||||
@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}?v={DateTime.Now.Ticks}")"
|
||||||
Alt=""
|
Alt=""
|
||||||
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>
|
<MudTooltip Text="@PrivateEntity.GlobalEntity.Title">
|
||||||
|
<MudText Typo="Typo.h6" Style="display: block; max-width: 250px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">@PrivateEntity.GlobalEntity.Title</MudText>
|
||||||
|
</MudTooltip>
|
||||||
<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") -->
|
||||||
<MudSelect
|
<!--{-->
|
||||||
T="int?"
|
<!--<editor-fold desc="Dropdown-Season">-->
|
||||||
@bind-Value="@PrivateEntity.Season"
|
<MudSelect
|
||||||
Dense="true"
|
T="int?"
|
||||||
Variant="Variant.Outlined"
|
Value="PrivateEntity.Season"
|
||||||
Style="background-color: #3a3a3a; color: white;"
|
ValueChanged="@(newSeason => OnSeasonChange.InvokeAsync(newSeason))"
|
||||||
Label="">
|
Dense="true"
|
||||||
<!-- @foreach (var season in SeasonOptions)
|
Variant="Variant.Outlined"
|
||||||
{
|
Label=""
|
||||||
<MudSelectItem Value="@season">@season</MudSelectItem>
|
Class="@(PrivateEntity.GlobalEntity.MediaType.Name == "Series" ? "" : "invisible")">
|
||||||
}-->
|
@{
|
||||||
</MudSelect>
|
int? currentSeason = PrivateEntity.Season ?? 1;
|
||||||
|
int? startOffsetSeason = currentSeason > 1 ? -1 : 0;
|
||||||
|
}
|
||||||
|
@for (int? i = startOffsetSeason; i <= 5; i++)
|
||||||
|
{
|
||||||
|
int? season = currentSeason + i;
|
||||||
|
<MudSelectItem Value="@season">Staffel @season</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
<!--</editor-fold>-->
|
||||||
|
|
||||||
<!-- Episode Dropdown -->
|
<!--<editor-fold desc="Dropdown-Episode">-->
|
||||||
<MudSelect
|
<MudSelect
|
||||||
T="int?"
|
T="int?"
|
||||||
@bind-Value="@PrivateEntity.Episode"
|
Value="PrivateEntity.Episode"
|
||||||
Dense="true"
|
ValueChanged="@(newEpisode => OnEpisodeChange.InvokeAsync(newEpisode))"
|
||||||
Variant="Variant.Outlined"
|
Dense="true"
|
||||||
Style="background-color: #3a3a3a; color: white;"
|
Variant="Variant.Outlined"
|
||||||
Label="">
|
Label=""
|
||||||
<!--@foreach (var episode in EpisodeOptions)
|
Class="@(PrivateEntity.GlobalEntity.MediaType.Name == "Series" ? "" : "invisible")">
|
||||||
{
|
@{
|
||||||
<MudSelectItem Value="@episode">@episode</MudSelectItem>
|
int? currentEpisode = PrivateEntity.Episode ?? 1;
|
||||||
}-->
|
int? startOffsetEpisode = currentEpisode > 1 ? -1 : 0;
|
||||||
</MudSelect>
|
}
|
||||||
|
@for (int? i = startOffsetEpisode; i <= 5; i++)
|
||||||
|
{
|
||||||
|
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">
|
<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 +106,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>
|
||||||
@@ -112,20 +132,20 @@
|
|||||||
//Filter
|
//Filter
|
||||||
private string? _searchString;
|
private string? _searchString;
|
||||||
private bool _showFilters;
|
private bool _showFilters;
|
||||||
private bool _showAlreadyAddedEntities = false;
|
//private bool _showAlreadyAddedEntities = false;
|
||||||
private IEnumerable<PrivateEntity> FiltertGlobalEntities => _privateEntities.Where(entity =>
|
private IEnumerable<PrivateEntity> FiltertGlobalEntities => _privateEntities.Where(entity =>
|
||||||
(string.IsNullOrWhiteSpace(_searchString) || entity.GlobalEntity.Title.Contains(_searchString, StringComparison.OrdinalIgnoreCase)) //Search bar
|
(string.IsNullOrWhiteSpace(_searchString) || entity.GlobalEntity.Title.Contains(_searchString, StringComparison.OrdinalIgnoreCase)) //Search bar
|
||||||
);
|
);
|
||||||
|
|
||||||
//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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+15
-14
@@ -7,6 +7,10 @@
|
|||||||
<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>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<WarnOnPackingNonPackableProject>false</WarnOnPackingNonPackableProject>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -58,26 +62,23 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.QuickGrid" Version="10.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.QuickGrid" Version="10.0.9" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="10.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="10.0.9" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.9" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="10.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="10.0.9" />
|
||||||
<PackageReference Include="Microsoft.Build" Version="18.0.2" />
|
<PackageReference Include="Microsoft.Build" Version="18.7.1" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="5.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.9" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="5.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.9">
|
||||||
<PackageReference Include="Microsoft.DotNet.Scaffolding.Shared" Version="10.0.0" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.1" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.1">
|
|
||||||
<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="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.9" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.9" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.1">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.9">
|
||||||
<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.6.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
|
|
||||||
namespace CouchLog.Data
|
namespace CouchLog.Data
|
||||||
{
|
{
|
||||||
[method: SetsRequiredMembers]
|
public class GlobalEntity
|
||||||
public class GlobalEntity()
|
|
||||||
{
|
{
|
||||||
[Key]
|
[Key]
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|||||||
@@ -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 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 410 KiB |
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user