35 Commits

Author SHA1 Message Date
Penry 94e3104773 Added Paging and Filtering to PrivateList 2026-06-04 23:19:28 +02:00
Penry dcbff4e30d Added Paging, Page only reload and Search bar to GlobalList.razor 2026-06-01 01:01:48 +02:00
Penry 7af23518e5 Added MockUp Code for the PrivateEntity Cards 2026-05-28 23:48:03 +02:00
Penry 226cc38bf7 Started with Private List for 0.1.0-beta version 2026-05-27 19:31:22 +02:00
Penry 8e615c15f0 Save Changes 2026-05-23 13:08:12 +02:00
Penry 352a7dbfde Made Create GlobalEntity to function and added MudCard for GlobalEntitys 2026-03-24 00:00:55 +01:00
Penry 2e9ea45bd9 Save Progress 2026-03-22 22:12:04 +01:00
Penry e7f8e8c331 SaveProgress 2026-03-03 22:17:38 +01:00
Penry 9fab942181 Made Navbar working and began to adapted Global Entitys List Page 2026-02-26 22:27:55 +01:00
Penry b316a882c5 Delete Bootstrap complitly and adapted some Sites 2026-02-25 22:55:26 +01:00
Penry 44cbfc9c55 feat: Added MudBlazor and reworked MainLayout 2026-02-16 22:34:53 +01:00
Penry c6355d47df Merge pull request 'feat: added Remove Button for removing Privte Entity from PrivateList' (#46) from AddRemovePrivateEntityFromList into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 1m35s
Reviewed-on: #46
2026-02-15 23:39:01 +01:00
Penry 97a92269ad feat: added Remove Button for removing Privte Entity from PrivateList 2026-02-15 23:38:02 +01:00
Penry 66468a1f8b Merge pull request 'feat: changed the GlobalEntity Sort Attribute to CreationTime' (#45) from ChangeGlobalEntitySortAttribute into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 45s
Reviewed-on: #45
2026-02-13 19:49:30 +01:00
Penry e48e8b6394 feat: changed the GlobalEntity Sort Attribute to CreationTime 2026-02-13 19:42:25 +01:00
Penry 5f39417109 Merge pull request 'feat: added Currently Watching to PrivateList' (#40) from AddCurrentlyWatchingToPrivateList into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 4m34s
Reviewed-on: #40
2026-01-04 20:30:57 +01:00
Penry 76e8e8daa6 feat: added Currently Watching to PrivateList 2026-01-04 20:27:17 +01:00
Penry 6db2bca50b Merge pull request 'fix: edited UserWatchStatus' (#39) from EditedUserWatchStatusName into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 9m11s
Reviewed-on: #39
2026-01-04 20:00:45 +01:00
Penry 46491dd987 fix: edited UserWatchStatus 2026-01-04 19:56:11 +01:00
Penry 8edf749e23 Merge pull request 'feat: added that Episode gets set to 1 when changing the Season' (#38) from AddedEpisodeChangeOnSeasonChange into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 9m13s
Reviewed-on: #38
2026-01-04 19:26:59 +01:00
Penry 3cc4fd8240 feat: added that Episode gets set to 1 when changing the Season 2026-01-04 19:16:32 +01:00
Penry d0f91fd0e1 Merge pull request 'feat: Rework first account to be register is admin and no Standard users get created' (#37) from ReworkedRegisterAndStandardAdminUser into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 9m15s
Reviewed-on: #37
2026-01-04 18:00:21 +01:00
Penry f1a68296ec Merge branch 'main' into ReworkedRegisterAndStandardAdminUser 2026-01-04 18:00:05 +01:00
Penry c5a3ba2b40 feat: Rework first account to be register is admin and no Standard users get created 2026-01-04 17:58:26 +01:00
Penry 67b559e6d7 Merge pull request '.gitea/workflows/linux_arm64_docker.yaml aktualisiert' (#34) from WrongDockerImageNameForWorkflow into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 3m59s
Reviewed-on: #34
2026-01-03 20:13:00 +01:00
Penry 3c10721c4e .gitea/workflows/linux_arm64_docker.yaml aktualisiert 2026-01-03 20:12:48 +01:00
Penry eec7f40e72 Merge pull request 'fix: fixed the Issue with long Names and adapted the count of Entity in a row' (#33) from SomeCSSReworks into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Failing after 5m55s
Reviewed-on: #33
2025-12-27 23:00:00 +01:00
Penry 899a36a49c Merge branch 'main' into SomeCSSReworks 2025-12-27 22:59:47 +01:00
Penry 8e607ee180 Merge pull request '.gitea/workflows/linux_arm64_docker.yaml aktualisiert' (#32) from RemovedUnnecessaryImageCopy into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Has been cancelled
Reviewed-on: #32
2025-12-27 22:57:56 +01:00
Penry 87e6132f34 .gitea/workflows/linux_arm64_docker.yaml aktualisiert 2025-12-27 22:57:34 +01:00
Penry dd845f4be6 fix: fixed the Issue with long Names and adapted the count of Entity in a row 2025-12-27 22:52:34 +01:00
Penry 6ca8fe7bdc Merge pull request '.gitea/workflows/linux_arm64_docker.yaml aktualisiert' (#30) from UpdateMainPushWorkflowForARM64Docker into main
Build Docker Linux ARM64 / build-docker-linux-arm64 (push) Successful in 9m4s
Reviewed-on: #30
2025-12-27 22:45:14 +01:00
Penry 56bcd712f1 .gitea/workflows/linux_arm64_docker.yaml aktualisiert 2025-12-27 22:44:59 +01:00
Penry 4e3eca0650 Merge pull request 'Added Adventure Genre to StartUp Actions' (#28) from AddAdventureGenre into main
Reviewed-on: #28
2025-12-27 00:37:30 +01:00
Penry 2160c9abbc Added Adventure Genre to StartUp Actions 2025-12-27 00:36:36 +01:00
90 changed files with 4268 additions and 60857 deletions
+12 -4
View File
@@ -3,7 +3,7 @@ name: Build Docker Linux ARM64
on:
push:
branches:
#- main
- main
jobs:
build-docker-linux-arm64:
@@ -18,11 +18,19 @@ jobs:
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Get Repository Name
id: get_repo
run: |
echo "REPO_LC=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV
- name: Build Linux ARM64 Docker Image
run: docker build -t couchlog-linux-arm64 .
run: |
docker build \
-t gitea.penry.de/${{ env.REPO_LC }}:latest \
.
- name: Save Docker Image to Tar
run: docker save -o CouchLog-Linux-ARM64-Docker-Image.tar couchlog-linux-arm64
run: docker save -o CouchLog-Linux-ARM64-Docker-Image.tar gitea.penry.de/${{ env.REPO_LC }}:latest
- name: Upload Artifact
uses: actions/upload-artifact@v3
@@ -30,4 +38,4 @@ jobs:
name: CouchLog-Linux-ARM64-Docker-Image
path: CouchLog-Linux-ARM64-Docker-Image.tar
if-no-files-found: error
retention-days: 1
retention-days: 5
+2
View File
@@ -2,5 +2,7 @@
bin
Debug
/CouchLog/Data/CouchLog.db
/CouchLog/Data/CouchLog.db-shm
/CouchLog/Data/CouchLog.db-wal
#Migrations
obj
+3
View File
@@ -0,0 +1,3 @@
{
"terminal.explorerKind": "integrated"
}
+4
View File
@@ -0,0 +1,4 @@
[*.cs]
# IDE0130: Namespace does not match folder structure
dotnet_diagnostic.IDE0130.severity = none
+48 -44
View File
@@ -1,9 +1,11 @@
@page "/Account/Login"
@layout Layout.IdentityLayout
@using System.ComponentModel.DataAnnotations
@using Microsoft.AspNetCore.Authentication
@using Microsoft.AspNetCore.Identity
@using CouchLog.Data
@using MudBlazor
@inject SignInManager<ApplicationUser> SignInManager
@inject ILogger<Login> Logger
@@ -12,57 +14,59 @@
<PageTitle>Log in</PageTitle>
<h1>Log in</h1>
<div class="row">
<div class="col-lg-6">
<section>
<MudGrid Justify="Justify.Center" Class="align-center">
@* Linke Spalte: Login-Formular *@
<MudItem xs="12" md="7">
<MudPaper Elevation="3" Class="pa-6">
<StatusMessage Message="@errorMessage" />
<EditForm Model="Input" method="post" OnValidSubmit="LoginUser" FormName="login">
<DataAnnotationsValidator />
<h2>Use a local account to log in.</h2>
<hr />
<MudText Typo="Typo.h5" GutterBottom="true" Align="Align.Center">Login</MudText>
<MudDivider Class="mb-6" />
<ValidationSummary class="text-danger" role="alert" />
<div class="form-floating mb-3">
<InputText @bind-Value="Input.Username" id="Input.Username" class="form-control" autocomplete="username" aria-required="true" placeholder="Username" />
<label for="Input.Username" class="form-label">Username</label>
<ValidationMessage For="() => Input.Username" class="text-danger" />
@* MudTextFields zwingend mit Name-Attribut für SSR *@
<MudTextField @bind-Value="Input.Username"
For="@(() => Input.Username)"
Name="Input.Username"
Label="Username"
Variant="Variant.Outlined"
Margin="Margin.Dense"
ShrinkLabel="true"
Class="mb-4" />
<MudTextField @bind-Value="Input.Password"
For="@(() => Input.Password)"
Name="Input.Password"
Label="Password"
InputType="InputType.Password"
Variant="Variant.Outlined"
Margin="Margin.Dense"
ShrinkLabel="true"
Class="mb-4" />
@* Standard-Checkbox mit MudBlazor-Typografie *@
<div class="d-flex align-center mb-6">
<InputCheckbox @bind-Value="Input.RememberMe" id="Input.RememberMe" style="width: 1.2rem; height: 1.2rem; accent-color: var(--mud-palette-primary);" />
<label for="Input.RememberMe" class="ml-2 mud-typography mud-typography-body1">Remember me</label>
</div>
<div class="form-floating mb-3">
<InputText type="password" @bind-Value="Input.Password" id="Input.Password" class="form-control" autocomplete="current-password" aria-required="true" placeholder="Password" />
<label for="Input.Password" class="form-label">Password</label>
<ValidationMessage For="() => Input.Password" class="text-danger" />
</div>
<div class="checkbox mb-3">
<label class="form-label">
<InputCheckbox @bind-Value="Input.RememberMe" class="darker-border-checkbox form-check-input" />
Remember me
</label>
</div>
<div>
<button type="submit" class="w-100 btn btn-lg btn-primary">Log in</button>
</div>
<div>
<p>
<a href="Account/ForgotPassword">Forgot your password?</a>
</p>
<p>
<a href="@(NavigationManager.GetUriWithQueryParameters("Account/Register", new Dictionary<string, object?> { ["ReturnUrl"] = ReturnUrl }))">Register as a new user</a>
</p>
<p>
<a href="Account/ResendEmailConfirmation">Resend email confirmation</a>
</p>
<MudButton ButtonType="ButtonType.Submit" Variant="Variant.Filled" Color="Color.Primary" Size="Size.Large" FullWidth="true" Class="mb-4">
Log in
</MudButton>
<div class="d-flex flex-column gap-2 mt-4">
<MudLink Href="Account/ForgotPassword" Typo="Typo.body2">Forgot your password?</MudLink>
<MudLink Href="@(NavigationManager.GetUriWithQueryParameters("Account/Register", new Dictionary<string, object?> { ["ReturnUrl"] = ReturnUrl }))" Typo="Typo.body2">Register as a new user</MudLink>
<MudLink Href="Account/ResendEmailConfirmation" Typo="Typo.body2">Resend email confirmation</MudLink>
</div>
</EditForm>
</section>
</div>
<div class="col-lg-4 col-lg-offset-2">
<section>
<h3>Use another service to log in.</h3>
<hr />
<ExternalLoginPicker />
</section>
</div>
</div>
</MudPaper>
</MudItem>
</MudGrid>
@code {
private string? errorMessage;
@@ -1,4 +1,5 @@
@page "/Account/Register"
@layout Layout.IdentityLayout
@using System.ComponentModel.DataAnnotations
@using System.Text
@@ -10,10 +11,10 @@
@inject UserManager<ApplicationUser> UserManager
@inject IUserStore<ApplicationUser> UserStore
@inject SignInManager<ApplicationUser> SignInManager
@inject IEmailSender<ApplicationUser> EmailSender
@inject ILogger<Register> Logger
@inject NavigationManager NavigationManager
@inject IdentityRedirectManager RedirectManager
@inject ApplicationDbContext CouchLogDB
<PageTitle>Register</PageTitle>
@@ -28,9 +29,9 @@
<hr />
<ValidationSummary class="text-danger" role="alert" />
<div class="form-floating mb-3">
<InputText @bind-Value="Input.Email" id="Input.Email" class="form-control" autocomplete="username" aria-required="true" placeholder="name@example.com" />
<label for="Input.Email">Email</label>
<ValidationMessage For="() => Input.Email" class="text-danger" />
<InputText @bind-Value="Input.Username" id="Input.Username" class="form-control" autocomplete="username" aria-required="true" placeholder="NewUsername" />
<label for="Input.Username">Username</label>
<ValidationMessage For="() => Input.Username" class="text-danger" />
</div>
<div class="form-floating mb-3">
<InputText type="password" @bind-Value="Input.Password" id="Input.Password" class="form-control" autocomplete="new-password" aria-required="true" placeholder="password" />
@@ -67,11 +68,19 @@
public async Task RegisterUser(EditContext editContext)
{
if(!CouchLogDB.AccountsSettings.First().IsRegistrationAllowed)
{
identityErrors = new[]
{
new IdentityError { Description = "Registration is deactivated" }
};
return;
}
var user = CreateUser();
await UserStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
var emailStore = GetEmailStore();
await emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
await UserStore.SetUserNameAsync(user, Input.Username, CancellationToken.None);
user.EmailConfirmed = true;
var result = await UserManager.CreateAsync(user, Input.Password);
if (!result.Succeeded)
@@ -80,23 +89,24 @@
return;
}
Logger.LogInformation("User created a new account with password.");
var userId = await UserManager.GetUserIdAsync(user);
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = NavigationManager.GetUriWithQueryParameters(
NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri,
new Dictionary<string, object?> { ["userId"] = userId, ["code"] = code, ["returnUrl"] = ReturnUrl });
await EmailSender.SendConfirmationLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl));
if (UserManager.Options.SignIn.RequireConfirmedAccount)
if(CouchLogDB.CouchLogState.Count() == 0)
{
RedirectManager.RedirectTo(
"Account/RegisterConfirmation",
new() { ["email"] = Input.Email, ["returnUrl"] = ReturnUrl });
await UserManager.AddToRoleAsync(user, "Admin");
await CouchLogDB.CouchLogState.AddAsync(new CouchLogState
{
Id = 1,
InitializationDate = DateTime.Now,
IsInitialized = true
});
}
else
{
await UserManager.AddToRoleAsync(user, "User");
}
await CouchLogDB.SaveChangesAsync();
Logger.LogInformation("User created a new account with password.");
await SignInManager.SignInAsync(user, isPersistent: false);
RedirectManager.RedirectTo(ReturnUrl);
@@ -127,9 +137,8 @@
private sealed class InputModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; } = "";
[Display(Name = "Username")]
public string Username { get; set; } = "";
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
@@ -0,0 +1,51 @@
@page "/AdminSettings/CouchLogSettings"
@rendermode InteractiveServer
@using CouchLog.Data
@using Microsoft.EntityFrameworkCore
@using Microsoft.AspNetCore.Components.Forms
@inject ApplicationDbContext CouchLogDB
<h3>CouchLog Settings</h3>
@if (accountsSettings is null)
{
<p>Lade Einstellungen…</p>
}
else
{
<EditForm Model="accountsSettings">
<div class="form-check form-switch">
<InputCheckbox class="form-check-input"
role="switch"
id="IsRegistrationallowedInput"
Value="accountsSettings.IsRegistrationAllowed"
ValueChanged="OnRegistrationChanged"
ValueExpression="() => accountsSettings.IsRegistrationAllowed" />
<label class="form-check-label" for="IsRegistrationallowedInput">
Is Registration allowed
</label>
</div>
</EditForm>
}
@code {
private AccountsSettings? accountsSettings;
protected override async Task OnInitializedAsync()
{
accountsSettings = await CouchLogDB.AccountsSettings.FirstAsync();
}
private async Task OnRegistrationChanged(bool value)
{
accountsSettings!.IsRegistrationAllowed = value;
CouchLogDB.AccountsSettings.Update(accountsSettings);
await CouchLogDB.SaveChangesAsync();
}
}
@@ -9,7 +9,6 @@
@inject ApplicationDbContext CouchLogDB
@inject UserManager<ApplicationUser> UserManager
@inject RoleManager<IdentityRole> RoleManager
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject NavigationManager NavigationManager
@@ -26,7 +25,7 @@
</button>
</div>
<div>
<!-- <div>
<QuickGrid Items="@gridUsers.AsQueryable()">
<PropertyColumn Title="#" Property="@(u => u.Index)" />
<PropertyColumn Title="Username" Property="@(u => u.UserName)" />
@@ -39,7 +38,7 @@
}
</TemplateColumn>
</QuickGrid>
</div>
</div> -->
<!-- #region UserCreate Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true" style="color: black">
<div class="modal-dialog modal-dialog-centered">
@@ -4,6 +4,9 @@
@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>
@@ -11,8 +14,5 @@
<li class="nav-item">
<NavLink class="nav-link" href=""></NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href=""></NavLink>
</li>
-->
</ul>
+27 -18
View File
@@ -1,23 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="@Assets["CouchLog.styles.css"]" />
<ImportMap />
@* Rendermode get's hand over dynamic *@
<HeadOutlet @rendermode="RenderModeForPage" />
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="@Assets["lib/bootstrap/dist/css/bootstrap.min.css"]" />
<link rel="stylesheet" href="@Assets["app.css"]" />
<link rel="stylesheet" href="@Assets["CouchLog.styles.css"]" />
<ImportMap />
<HeadOutlet />
</head>
<body>
<Routes />
<script src="_framework/blazor.web.js"></script>
<script src="lib/bootstrap/dist/js/bootstrap.js"></script>
<script src="lib/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="lib/bootstrap/dist/js/site.js"></script>
</body>
<!-- #MudBlazor -->
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
<!-- #endregion -->
</head>
<body>
@* Rendermode get's hand over dynamic *@
<Routes @rendermode="RenderModeForPage" />
<script src="_framework/blazor.web.js"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
</body>
</html>
@code {
[CascadingParameter]
private HttpContext HttpContext { get; set; } = default!;
private Microsoft.AspNetCore.Components.IComponentRenderMode? RenderModeForPage => HttpContext.Request.Path.StartsWithSegments("/Account") ? null : Microsoft.AspNetCore.Components.Web.RenderMode.InteractiveServer;
}
@@ -0,0 +1,14 @@
@inherits LayoutComponentBase
@using MudBlazor
<MudThemeProvider IsDarkMode="true" />
<MudDialogProvider />
<MudSnackbarProvider />
<MudLayout>
<MudMainContent>
<MudContainer MaxWidth="MaxWidth.Large" Class="mt-8">
@Body
</MudContainer>
</MudMainContent>
</MudLayout>
+33 -12
View File
@@ -1,19 +1,40 @@
@inherits LayoutComponentBase
@using MudBlazor
<div class="page">
<div class="sidebar">
<MudThemeProvider @bind-IsDarkMode="@_isDarkMode" />
<MudDialogProvider />
<MudSnackbarProvider />
<MudPopoverProvider />
<MudLayout>
<MudAppBar Color="Color.Primary" Fixed="true">
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="@((e) => DrawerToggle())" />
<MudText Typo="Typo.h6">CouchLog</MudText>
</MudAppBar>
<MudDrawer @bind-Open="@_drawerOpen" ClipMode="DrawerClipMode.Always" Elevation="1">
<NavMenu />
</div>
</MudDrawer>
<main>
<article class="content px-4">
<MudMainContent>
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="mt-4">
@Body
</article>
</main>
</MudContainer>
</MudMainContent>
</MudLayout>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<div id="blazor-error-ui" data-nosnippet>
An unhandled error has occurred.
<a href="." class="reload">Reload</a>
<span class="dismiss">🗙</span>
</div>
@code {
bool _drawerOpen = true;
bool _isDarkMode = true;
void DrawerToggle()
{
_drawerOpen = !_drawerOpen;
}
}
@@ -1,98 +0,0 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}
.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}
.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
}
@media (min-width: 641px) {
.page {
flex-direction: row;
}
.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
}
.top-row {
position: sticky;
top: 0;
z-index: 1;
}
.top-row.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}
.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}
#blazor-error-ui {
color-scheme: light only;
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
box-sizing: border-box;
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
+52 -64
View File
@@ -5,81 +5,70 @@
@inject NavigationManager NavigationManager
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject UserManager<ApplicationUser> UserManager
@inject ApplicationDbContext CouchLogDB
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">CouchLog</a>
</div>
</div>
<MudNavMenu>
<MudNavLink Href="" Match="NavLinkMatch.All" Icon="@Icons.Material.Filled.Home">
Home
</MudNavLink>
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
<MudNavLink Href="GlobalList" Icon="@Icons.Material.Filled.List">
Globallist
</MudNavLink>
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<nav class="nav flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="GlobalList">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Global List
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="PrivateList">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Private List
</NavLink>
</div>
<MudNavLink Href="PrivateList" Icon="@Icons.Material.Filled.Lock">
Privatelist
</MudNavLink>
<AuthorizeView Roles="Admin">
<div class="nav-item px-3">
<NavLink class="nav-link" href="AdminSettings">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Admin Settings
</NavLink>
</div>
</AuthorizeView>
<AuthorizeView Roles="Admin">
<MudNavLink Href="AdminSettings" Icon="@Icons.Material.Filled.AdminPanelSettings">
Admin Settings
</MudNavLink>
</AuthorizeView>
<AuthorizeView>
<Authorized>
<div class="nav-item px-3">
<NavLink class="nav-link" href="Account/Manage">
<span class="bi bi-person-fill-nav-menu" aria-hidden="true"></span> @context.User.Identity?.Name
</NavLink>
</div>
<div class="nav-item px-3">
<form action="Account/Logout" method="post">
<AntiforgeryToken />
<input type="hidden" name="ReturnUrl" value="@currentUrl" />
<button type="submit" class="nav-link">
<span class="bi bi-arrow-bar-left-nav-menu" aria-hidden="true"></span> Logout
</button>
</form>
</div>
</Authorized>
<NotAuthorized>
<div class="nav-item px-3">
<NavLink class="nav-link" href="Account/Register">
<span class="bi bi-person-nav-menu" aria-hidden="true"></span> Register
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="Account/Login">
<span class="bi bi-person-badge-nav-menu" aria-hidden="true"></span> Login
</NavLink>
</div>
</NotAuthorized>
</AuthorizeView>
</nav>
</div>
<AuthorizeView>
<Authorized>
<MudDivider Class="my-2" />
<MudNavLink Href="Account/Manage" Icon="@Icons.Material.Filled.Person">
@context.User.Identity?.Name
</MudNavLink>
<form action="Account/Logout" method="post">
<AntiforgeryToken />
<input type="hidden" name="ReturnUrl" value="@currentUrl" />
<MudNavLink Icon="@Icons.Material.Filled.Logout"
OnClick="@(() => { })"
Href="javascript:void(0)"
UserAttributes="@(new Dictionary<string, object>
{ {"onclick", "this.closest('form').submit()"} })">
Logout
</MudNavLink>
</form>
</Authorized>
<NotAuthorized>
<MudDivider Class="my-2" />
@if (_isRegistrationAllowed)
{
<MudNavLink Href="Account/Register" Icon="@Icons.Material.Filled.PersonAdd">
Register
</MudNavLink>
}
<MudNavLink Href="Account/Login" Icon="@Icons.Material.Filled.Badge">
Login
</MudNavLink>
</NotAuthorized>
</AuthorizeView>
</MudNavMenu>
@code {
private string? currentUrl;
private bool _isRegistrationAllowed;
protected override void OnInitialized()
{
currentUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
NavigationManager.LocationChanged += OnLocationChanged;
_isRegistrationAllowed = CouchLogDB.AccountsSettings.First().IsRegistrationAllowed;
}
private void OnLocationChanged(object? sender, LocationChangedEventArgs e)
@@ -92,5 +81,4 @@
{
NavigationManager.LocationChanged -= OnLocationChanged;
}
}
}
@@ -1,125 +0,0 @@
.navbar-toggler {
appearance: none;
cursor: pointer;
width: 3.5rem;
height: 2.5rem;
color: white;
position: absolute;
top: 0.5rem;
right: 1rem;
border: 1px solid rgba(255, 255, 255, 0.1);
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
}
.navbar-toggler:checked {
background-color: rgba(255, 255, 255, 0.5);
}
.top-row {
min-height: 3.5rem;
background-color: rgba(0,0,0,0.4);
}
.navbar-brand {
font-size: 1.1rem;
}
.bi {
display: inline-block;
position: relative;
width: 1.25rem;
height: 1.25rem;
margin-right: 0.75rem;
top: -1px;
background-size: cover;
}
.bi-house-door-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
}
.bi-plus-square-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
}
.bi-list-nested-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}
.bi-lock-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath d='M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2zm3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2zM5 8h6a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1z'/%3E%3C/svg%3E");
}
.bi-person-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-person' viewBox='0 0 16 16'%3E%3Cpath d='M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6Zm2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0Zm4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 3 6 4Zm-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664h10Z'/%3E%3C/svg%3E");
}
.bi-person-badge-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-person-badge' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 2a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-3zM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0z'/%3E%3Cpath d='M4.5 0A2.5 2.5 0 0 0 2 2.5V14a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V2.5A2.5 2.5 0 0 0 11.5 0h-7zM3 2.5A1.5 1.5 0 0 1 4.5 1h7A1.5 1.5 0 0 1 13 2.5v10.795a4.2 4.2 0 0 0-.776-.492C11.392 12.387 10.063 12 8 12s-3.392.387-4.224.803a4.2 4.2 0 0 0-.776.492V2.5z'/%3E%3C/svg%3E");
}
.bi-person-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-person-fill' viewBox='0 0 16 16'%3E%3Cpath d='M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H3Zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z'/%3E%3C/svg%3E");
}
.bi-arrow-bar-left-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-arrow-bar-left' viewBox='0 0 16 16'%3E%3Cpath d='M12.5 15a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5ZM10 8a.5.5 0 0 1-.5.5H3.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L3.707 7.5H9.5a.5.5 0 0 1 .5.5Z'/%3E%3C/svg%3E");
}
.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item ::deep .nav-link {
color: #d7d7d7;
background: none;
border: none;
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
width: 100%;
}
.nav-item ::deep a.active {
background-color: rgba(255,255,255,0.37);
color: white;
}
.nav-item ::deep .nav-link:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
.nav-scrollable {
display: none;
}
.navbar-toggler:checked ~ .nav-scrollable {
display: block;
}
@media (min-width: 641px) {
.navbar-toggler {
display: none;
}
.nav-scrollable {
/* Never collapse the sidebar for wide screens */
display: block;
/* Allow sidebar to scroll for tall menus */
height: calc(100vh - 3.5rem);
overflow-y: auto;
}
}
+1 -1
View File
@@ -33,4 +33,4 @@
protected override void OnInitialized() =>
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
}
}
-322
View File
@@ -1,322 +0,0 @@
@page "/GlobalList"
@rendermode InteractiveServer
@using Microsoft.AspNetCore.Authorization
@using CouchLog.Data
@using Microsoft.AspNetCore.Identity
@using Microsoft.EntityFrameworkCore
@inject ApplicationDbContext CouchLogDB
@inject UserManager<ApplicationUser> UserManager
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject NavigationManager NavigationManager
@attribute [Authorize]
<PageTitle>GlobalList</PageTitle>
<div class="container-fluid mt-4">
<!-- #region CreateEntity -->
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Global List</h2>
<button class="btn btn-info" type="button" @onclick="ToogleCollapseNewGlobalEntity">@(isCollapseNewGlobalEntityOpen ? "X" : "Add Entity")</button>
</div>
<div class="collapse @(isCollapseNewGlobalEntityOpen ? "show" : "")" id="CollapseCreateNewGlobalEntity">
<div class="mb-4 align-items-center CreateNewGlobalEntity-Container">
<EditForm Model="@GlobalEntity" OnSubmit="CreateNewGlobalEntity" FormName="CreateNewGlobalEntityForm">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="mb-3">
<label for="Title" class="form-label">Title</label>
<InputText id="Title" class="form-control" @bind-Value="GlobalEntity.Title"></InputText>
<ValidationMessage For="@(() => GlobalEntity.Title)"></ValidationMessage>
</div>
<div class="mb-3">
<label for="Type" class="form-label">Type</label>
<select id="Type" class="form-select" @bind="SelectedMediaTypeId">
@foreach(MediaType Type in MediaTypes)
{
<option value="@Type.Id">@Type.Name</option>
}
</select>
</div>
<div class="mb-3">
<label for="Picture" class="form-label">Picture</label>
<InputFile OnChange="@LoadFiles" accept="image/*"/>
@if (!string.IsNullOrEmpty(ImagePreviewUrl))
{
<img src="@ImagePreviewUrl" style="max-width: 256px; max-height: 256px;" />
}
</div>
<div class="mb-3">
<label for="Genres" class="form-label">Genres</label>
@foreach(Genre Genre in Genres)
{
<button class="btn-primary btn me-2" value="@Genre.Id" type="button" @onclick="() => AddGenreToList(Genre.Id)">@Genre.Name</button>
}
</div>
<div class="mb-3">
<InputCheckbox DisplayName="isPrivate" @bind-Value="isPrivate" ></InputCheckbox>
</div>
<button type="submit" class="btn-success btn">Create new Global Entity</button>
</EditForm>
</div>
</div>
<!-- #endregion -->
<!-- #region Show Entitys -->
<div class="row">
@foreach (var Entity in GlobalEntities)
{
if (!Entity.IsPrivate || (Entity.IsPrivate && (Entity.CreatorId == AppUser.Id)))
{
<div name="Enity-Container" class="col-12 col-md-6 col-lg-3 mb-4 Entity-Container">
<div name="Entity-Container-Card" class="Entity-Container-Card">
<div class="Entity-Container-Menu-Button dropdown ms-1">
<button type="button" class="Entity-Container-Menu-Button btn menu-btn" data-bs-toggle="modal" data-bs-target="#modal-@Entity.Id">&#8942;</button>
</div>
<div class="modal fade" id="@Entity.Id" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Modal title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
This is a vertically centered modal.
</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">Understood</button>
</div>
</div>
</div>
</div>
<div name="Enity-Container-Image" class="">
<a href="javascript:void(0)" class="Entity-Container-Image">
<img src="/@Entity.PicturePath" alt="" class="Entity-Container-Image" />
</a>
</div>
<div name="Entity-Container-Data" class="">
<h3 class="">@Entity.Title</h3>
</div>
<div name="Entity-Container-Button" class="d-flex Entity-Container-Button" style="gap: 10px;">
<button class="btn btn-info" type="button" @onclick="() => AddToPrivateList(Entity)">
@(IsInPrivateList(Entity.Id) ? "Added" : "Add to Private List")
</button>
<button class="btn btn-info" type="button" @onclick="() => throw new NotImplementedException()">Add to Shared List</button>
</div>
</div>
</div>
<div class="modal fade" id="modal-@Entity.Id" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Optionen</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<button class="btn btn-danger" type="button" @onclick="() => DeleteEntity(Entity)">Delete Entity</button>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Schließen</button>
</div>
</div>
</div>
</div>
}
}
</div>
<!-- #endregion -->
</div>
@code
{
private List<MediaType> MediaTypes = new List<MediaType>();
private List<Genre> Genres = new List<Genre>();
private List<GlobalEntity> GlobalEntities = new List<GlobalEntity>();
private List<int> GenreIds = new List<int>();
private HashSet<int> UserPrivateEntityIds = new();
private string? ImagePreviewUrl;
private IBrowserFile? Picture;
System.Security.Claims.ClaimsPrincipal User = new();
ApplicationUser? AppUser = new();
private GlobalEntity GlobalEntity = new();
private bool isCollapseNewGlobalEntityOpen = false;
private int SelectedMediaTypeId;
private bool isPrivate = false;
/// <summary>
///
/// </summary>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
protected override async Task OnInitializedAsync()
{
GlobalEntities = await CouchLogDB.GlobalEntities.OrderByDescending(Entity => Entity.Id).ToListAsync();
MediaTypes = await CouchLogDB.MediaType.OrderBy(Type => Type.Id).ToListAsync();
Genres = await CouchLogDB.Genres.OrderBy(Genre => Genre.Id).ToListAsync();
var AuthState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
User = AuthState.User;
AppUser = await UserManager.GetUserAsync(User);
if (AppUser == null)
{
throw new NotImplementedException();
}
var TempUserPrivateEntityIds = await CouchLogDB.PrivateEntities
.Where(p => p.UserId == AppUser.Id)
.Select(p => p.GlobalEntityId)
.ToListAsync();
UserPrivateEntityIds = TempUserPrivateEntityIds.ToHashSet();
}
/// <summary>
///
/// </summary>
/// <param name="eventArgs"></param>
/// <returns></returns>
private async Task LoadFiles(InputFileChangeEventArgs eventArgs)
{
Picture = eventArgs.File;
//Byte[] Buffer = new byte[Picture.Size];
//await Picture.OpenReadStream().ReadAsync(Buffer);
//await Picture.OpenReadStream(maxAllowedSize: 10_000_000).ReadAsync(Buffer);
//string Base64 = Convert.ToBase64String(Buffer);
//ImagePreviewUrl = $"data:{Picture.ContentType};base64,{Base64}";
}
/// <summary>
///
/// </summary>
private void ToogleCollapseNewGlobalEntity()
{
isCollapseNewGlobalEntityOpen = !isCollapseNewGlobalEntityOpen;
StateHasChanged();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
private async Task CreateNewGlobalEntity()
{
if (Picture is null)
{
throw new InvalidOperationException("No Picture selected.");
}
if (AppUser is null)
{
throw new InvalidOperationException("User not loaded or not logged in.");
}
//Save Picture and Name it
string NewFileName = $"{GlobalEntity.Title.Replace(" ", "-")}-{Guid.NewGuid()}{Path.GetExtension(Picture.Name)}";
string PicturePath = Path.Combine("wwwroot", "Pictures", NewFileName);
using FileStream FileStream = File.Create(PicturePath);
await Picture.OpenReadStream().CopyToAsync(FileStream);
//await Picture.OpenReadStream(maxAllowedSize: 10_000_000).CopyToAsync(FileStream);
GlobalEntity.PicturePath = $"Pictures/{NewFileName}";
GlobalEntity.CreatorId = AppUser.Id;
GlobalEntity.TypeId = SelectedMediaTypeId;
GlobalEntity.CreationTime = DateTime.Now;
GlobalEntity.IsPrivate = isPrivate;
CouchLogDB.Add(GlobalEntity);
await CouchLogDB.SaveChangesAsync();
foreach(int GenreId in GenreIds)
{
LinkTableGlobalGenre LinkTableGlobalGenre = new() { GenreId = GenreId, GlobalEntityId = GlobalEntity.Id };
CouchLogDB.Add(LinkTableGlobalGenre);
}
await CouchLogDB.SaveChangesAsync();
NavigationManager.NavigateTo(NavigationManager.Uri, true);
}
/// <summary>
///
/// </summary>
/// <param name="GolbalEntityId"></param>
/// <returns></returns>
private bool IsInPrivateList(int GolbalEntityId)
{
return UserPrivateEntityIds.Contains(GolbalEntityId);
}
/// <summary>
///
/// </summary>
/// <param name="GlobalEntity"></param>
/// <returns></returns>
private async Task AddToPrivateList(GlobalEntity GlobalEntity)
{
if(User.Identity?.IsAuthenticated == true)
{
if(!IsInPrivateList(GlobalEntity.Id))
{
if (AppUser is null)
{
throw new InvalidOperationException("User not loaded or not logged in.");
}
PrivateEntity PrivateEntity = new()
{
UserId = AppUser.Id,
CreationTime = DateTime.Now,
GlobalEntityId = GlobalEntity.Id,
UserWatchStatusId = 1,
};
CouchLogDB.PrivateEntities.Add(PrivateEntity);
await CouchLogDB.SaveChangesAsync();
UserPrivateEntityIds.Add(GlobalEntity.Id);
StateHasChanged();
}
}
}
private void AddGenreToList(int GenreId)
{
if (!GenreIds.Contains(GenreId))
{
GenreIds.Add(GenreId);
}
}
private async Task DeleteEntity(GlobalEntity entity)
{
if(entity.PicturePath != null)
{
try
{
File.Delete(Path.Combine("wwwroot", entity.PicturePath));
}
catch(Exception)
{
}
}
CouchLogDB.GlobalEntities.Remove(entity);
await CouchLogDB.SaveChangesAsync();
NavigationManager.NavigateTo(NavigationManager.Uri, true);
}
}
@@ -1,88 +0,0 @@
/*For Development*/
/** {
outline: 1px solid red !important;
}*/
.Entity-Container {
color:aliceblue
}
.Entity-Container:hover {
color: rgba(0,255,255,0.5);
}
.Entity-Container-Card {
position: relative;
max-width: 275px;
background: #111;
display: flex;
flex-direction: column;
align-items: center;
overflow: hidden;
border-radius: 15px;
transition: all 0.5s ease;
text-align: center;
gap: 0.75rem;
padding-top: 10px;
padding-bottom: 24px;
}
.Entity-Container-Card h2 {
color: #0ff;
font-size: 2rem;
position: relative;
z-index: 2;
margin: 0;
}
.Entity-Container-Card::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient( 0deg, transparent, transparent 30%, rgba(0,255,255,0.3) );
transform: rotate(-45deg);
transition: all 0.5s ease;
opacity: 0;
}
.Entity-Container-Card:hover {
transform: scale(1.05);
box-shadow: 0 0 20px rgba(0,255,255,0.5);
}
.Entity-Container-Card:hover::before {
opacity: 1;
transform: rotate(-45deg) translateY(100%);
}
.Entity-Container-Image {
margin: 4px;
margin-top: 6px;
max-width: 256px;
max-height: 256px;
}
.Entity-Container-Button {
display: flex;
flex-direction: column
}
.CreateNewGlobalEntity-Container {
background: #111;
border-radius: 15px;
}
.Entity-Container-Menu-Button {
position: absolute;
top: 2px;
right: 7.5px;
background: transparent;
border: none;
color: #0dcaf0;
font-size: 2.5rem;
cursor: pointer;
}
@@ -0,0 +1,37 @@
@using CouchLog.Data
<MudCard>
<MudPaper Elevation="0">
<MudCardMedia Image="@GlobalEntity.PicturePath" Height="400" Style="object-fit: contain;"/>
</MudPaper>
<MudCardContent>
<MudText Typo="Typo.h5">@GlobalEntity.Title</MudText>
</MudCardContent>
<MudCardActions>
<MudTooltip Text="Add to shared List">
<MudIconButton Icon="@Icons.Material.Rounded.People" Color="Color.Primary" OnClick="@(() => OnAddToShared.InvokeAsync(GlobalEntity))" />
</MudTooltip>
<MudSpacer/>
@if (UserPrivateEntityIds.Contains(GlobalEntity.Id))
{
<MudTooltip Text="Already added to Private List">
<MudIconButton Icon="@Icons.Material.Rounded.Check" Color="Color.Primary"/>
</MudTooltip>
}
else
{
<MudTooltip Text="Add to private List">
<MudIconButton Icon="@Icons.Material.Rounded.Lock" Color="Color.Primary" OnClick="@(() => OnAddToPrivate.InvokeAsync(GlobalEntity))" />
</MudTooltip>
}
</MudCardActions>
</MudCard>
@code
{
[Parameter] public GlobalEntity GlobalEntity { get; set; } = null!;
[Parameter] public HashSet<int> UserPrivateEntityIds { get; set; } = null!;
[Parameter] public EventCallback<GlobalEntity> OnAddToPrivate { get; set; }
[Parameter] public EventCallback<GlobalEntity> OnAddToShared { get; set; }
}
@@ -0,0 +1,277 @@
@using CouchLog.Data
@using Microsoft.EntityFrameworkCore
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Identity
@using System.ComponentModel.DataAnnotations
@inject ApplicationDbContext CouchLogDB
@inject UserManager<ApplicationUser> UserManager
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject NavigationManager NavigationManager
@inject ISnackbar Snackbar
<style>
.drop-zone-hover-overlay {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.45);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity .2s ease;
border-radius: 6px;
}
.drop-zone-wrapper:hover .drop-zone-hover-overlay {
opacity: 1;
}
</style>
<MudDialog>
<TitleContent>
<MudText Typo="Typo.h6">Add Global Entity</MudText>
</TitleContent>
<DialogContent>
<EditForm EditContext="_editContext" OnSubmit="HandleSubmit">
<DataAnnotationsValidator />
<MudGrid Style="min-height: 400px;">
<MudItem xs="5" Style="display: flex;">
<MudFileUpload T="IBrowserFile" @ref="_fileUpload" FilesChanged="OnPictureUploaded" Accept="image/*" Style="width: 100%; display: flex;">
<ActivatorContent>
<div @onclick="OpenFilePicker"
@ondragenter="@(() => _isDragOver = true)"
@ondragleave="@(() => _isDragOver = false)"
@ondragover:preventDefault="true"
@ondrop:preventDefault="true"
@ondrop="@(() => _isDragOver = false)"
style="@GetDropZoneStyle()"
class="d-flex align-center justify-center cursor-pointer drop-zone-wrapper">
@if (_imagePreviewUrl is not null)
{
<div style="position: relative; width: 100%; height: 100%;">
<img src="@_imagePreviewUrl"
alt="Preview"
style="width: 100%; height: 100%; object-fit: cover; border-radius: 6px; display: block;" />
<div class="drop-zone-hover-overlay">
<MudStack AlignItems="AlignItems.Center" Spacing="1">
<MudIcon Icon="@Icons.Material.Filled.Edit"
Color="Color.Surface"
Size="Size.Large" />
<MudText Typo="Typo.caption" Style="color: white;">
Replace Image
</MudText>
</MudStack>
</div>
</div>
}
else
{
<MudStack AlignItems="AlignItems.Center" Spacing="2">
<MudIcon Icon="@Icons.Material.Filled.CloudUpload"
Size="Size.Large"
Color="@(_isDragOver ? Color.Primary : Color.Secondary)" />
<MudText Typo="Typo.body2"
Align="Align.Center"
Color="@(_isDragOver ? Color.Primary : Color.Secondary)">
Bild hierher ziehen<br />oder klicken zum Auswählen
</MudText>
</MudStack>
}
</div>
</ActivatorContent>
</MudFileUpload>
</MudItem>
<MudItem xs="7">
<MudStack Spacing="3">
<MudTextField @bind-Value="_form.Title"
Label="Title"
For="@(() => _form.Title)"
Variant="Variant.Outlined" />
<MudSelect T="int"
@bind-Value="_selectedMediaTypeId"
Label="Type"
Variant="Variant.Outlined">
@foreach (var type in _mediaTypes)
{
<MudSelectItem T="int" Value="@type.Id">@type.Name</MudSelectItem>
}
</MudSelect>
<MudSelect T="int"
Label="Genres"
MultiSelection="true"
@bind-SelectedValues="_selectedGenreIds"
Variant="Variant.Outlined">
@foreach (var genre in _genres)
{
<MudSelectItem T="int" Value="@genre.Id">@genre.Name</MudSelectItem>
}
</MudSelect>
<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>
</MudItem>
</MudGrid>
</EditForm>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel" Variant="Variant.Text" Disabled="_isSaving">Cancel</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
private IMudDialogInstance MudDialog { get; set; } = null!;
private ApplicationUser? _appUser;
private EditContext _editContext = null!;
private List<MediaType> _mediaTypes = new();
private List<Genre> _genres = new();
private GlobalEntityFormModel _form = new();
private MudFileUpload<IBrowserFile>? _fileUpload;
private IBrowserFile? _picture;
private string? _imagePreviewUrl;
private bool _isDragOver;
private int _selectedMediaTypeId;
private bool _isPrivate;
private bool _isSaving;
private IEnumerable<int> _selectedGenreIds = new HashSet<int>();
private class GlobalEntityFormModel
{
[Required(ErrorMessage = "Title is required.")]
[MaxLength(200, ErrorMessage = "Title may not exceed 200 characters.")]
public string Title { get; set; } = string.Empty;
}
private string GetDropZoneStyle() =>
"width: 100%; height: 100%; min-height: 380px; border-radius: 6px; " +
$"border: 2px dashed {(_isDragOver ? "var(--mud-palette-primary)" : "var(--mud-palette-lines-default)")}; " +
$"background-color: {(_isDragOver ? "var(--mud-palette-primary-hover)" : "transparent")}; " +
"transition: border-color .2s ease, background-color .2s ease;";
private Task OpenFilePicker() =>
_fileUpload?.OpenFilePickerAsync() ?? Task.CompletedTask;
protected override async Task OnInitializedAsync()
{
_editContext = new EditContext(_form);
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
_appUser = await UserManager.GetUserAsync(authState.User);
if (_appUser is null)
{
Snackbar.Add("Not authenticated.", Severity.Error);
MudDialog.Cancel();
return;
}
_mediaTypes = await CouchLogDB.MediaType.OrderBy(t => t.Id).ToListAsync();
_genres = await CouchLogDB.Genres.OrderBy(t => t.Id).ToListAsync();
if (_mediaTypes.Count > 0)
_selectedMediaTypeId = _mediaTypes.First().Id;
}
private async Task OnPictureUploaded(IBrowserFile file)
{
_picture = file;
const long maxPreviewSize = 5 * 1024 * 1024;
await using var stream = file.OpenReadStream(maxPreviewSize);
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
var base64 = Convert.ToBase64String(ms.ToArray());
_imagePreviewUrl = $"data:{file.ContentType};base64,{base64}";
}
private void Cancel() => MudDialog.Cancel();
private async Task HandleSubmit(EditContext ctx)
{
if (!ctx.Validate()) return;
if (_picture is null)
{
Snackbar.Add("Please upload a picture.", Severity.Warning);
return;
}
if (_selectedMediaTypeId == 0)
{
Snackbar.Add("Please select a media type.", Severity.Warning);
return;
}
_isSaving = true;
try
{
string safeTitle = _form.Title.Replace(" ", "-");
string fileName = $"{safeTitle}-{Guid.NewGuid()}{Path.GetExtension(_picture.Name)}";
string picturePath = Path.Combine("wwwroot", "Pictures", fileName);
await using (var fs = File.Create(picturePath))
{
await _picture.OpenReadStream(maxAllowedSize: 10_000_000).CopyToAsync(fs);
}
var entity = new GlobalEntity
{
Title = _form.Title,
PicturePath = $"Pictures/{fileName}",
CreatorId = _appUser!.Id,
TypeId = _selectedMediaTypeId,
CreationTime = DateTime.UtcNow,
IsPrivate = _isPrivate
};
CouchLogDB.Add(entity);
await CouchLogDB.SaveChangesAsync();
foreach (int genreId in _selectedGenreIds)
{
CouchLogDB.Add(new LinkTableGlobalGenre
{
GenreId = genreId,
GlobalEntityId = entity.Id
});
}
await CouchLogDB.SaveChangesAsync();
Snackbar.Add("Global Entity created!", Severity.Success);
MudDialog.Close(DialogResult.Ok(entity));
}
catch (Exception ex)
{
Snackbar.Add($"Error: {ex.Message}", Severity.Error);
}
finally
{
_isSaving = false;
}
}
}
@@ -0,0 +1,207 @@
@page "/GlobalList"
@rendermode InteractiveServer
@using Microsoft.AspNetCore.Authorization
@using CouchLog.Data
@using Microsoft.AspNetCore.Identity
@using Microsoft.EntityFrameworkCore
@using CouchLog.Components.Pages.GlobalList.Dialogs
@using CouchLog.Components.Pages.GlobalList.Components
@inject ApplicationDbContext CouchLogDb
@inject UserManager<ApplicationUser> UserManager
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject IDialogService DialogService
@inject ISnackbar Snackbar
@attribute [Authorize]
<PageTitle>GlobalList</PageTitle>
<MudContainer MaxWidth="MaxWidth.False">
@*Top of the Page*@
<MudContainer>
<MudStack Row="true" AlignItems="AlignItems.Center">
@*Page Title*@
<MudText Typo="Typo.h3" Style="font-weight: bold;">Global List</MudText>
@*Spacer*@
<MudSpacer />
@*Button for adding GlobalEntity*@
<MudTooltip Text="Add GlobalEntity">
<MudFab StartIcon="@Icons.Material.Filled.Add" Color="Color.Primary" OnClick="OpenAddNewGlobalEntityDialog"/>
</MudTooltip>
</MudStack>
</MudContainer>
<br/>
<MudContainer>
<MudPaper Class="pa-2" Elevation="1">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
@*Search bar*@
<MudTextField @bind-Value="_searchString"
Placeholder="Search..."
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Search"
Immediate="true"
Variant="Variant.Outlined"
Margin="Margin.Dense"
Style="max-width: 300px;"/>
@*Refresh button*@
@*<MudTooltip Text="Refresh Entities">
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="RefreshPage"/>
</MudTooltip>*@
@*More Filters button*@
<MudTooltip Text="More filters">
<MudIconButton Icon="@(_showFilters
? Icons.Material.Filled.ExpandLess
: Icons.Material.Filled.FilterList)"
OnClick="@(() => _showFilters = !_showFilters)"
Color="Color.Default"/>
</MudTooltip>
</MudStack>
<MudCollapse Expanded="_showFilters">
@*Entity allready added*@
<MudSwitch @bind-Value="_showAlreadyAddedEntities" Color="Color.Primary">
Show already added Entities
</MudSwitch>
@*To be exstendet*@
</MudCollapse>
</MudPaper>
</MudContainer>
<br/>
<MudContainer MaxWidth="MaxWidth.False">
<MudGrid Spacing="6" Justify="Justify.Center">
@foreach (var globalEntity in VisibleGlobalEntities)
{
<MudItem xs="12" sm="6" md="4" lg="3" xl="2">
<GlobalEntityCard GlobalEntity="globalEntity"
UserPrivateEntityIds="_userPrivateEntityIds"
OnAddToPrivate="AddGlobalEntityToPrivateList"
OnAddToShared="AddGlobalEntityToSharedList"/>
</MudItem>
}
</MudGrid>
</MudContainer>
<br/>
<MudContainer>
<MudStack AlignItems="AlignItems.Center">
<MudPagination Count="@TotalPages" @bind-Selected="_currentPage"/>
</MudStack>
</MudContainer>
</MudContainer>
@code
{
//Filter
private string? _searchString;
private bool _showFilters;
private bool _showAlreadyAddedEntities = false;
private IEnumerable<GlobalEntity> FiltertGlobalEntities => _globalEntities.Where(entity =>
(string.IsNullOrWhiteSpace(_searchString) || entity.Title.Contains(_searchString, StringComparison.OrdinalIgnoreCase)) && //Search bar
(_showAlreadyAddedEntities || !_userPrivateEntityIds.Contains(entity.Id)) //Filter for already added Entitys
);
//Paging
private int _currentPage = 1;
private int _pageSize = 12;
private int TotalPages => (int)Math.Ceiling(FiltertGlobalEntities.Count() / (double)_pageSize);
private IEnumerable<GlobalEntity> VisibleGlobalEntities => FiltertGlobalEntities.Skip((_currentPage - 1) * _pageSize).Take(_pageSize);
//User
private ApplicationUser? _appUser;
//Core
private List<GlobalEntity> _globalEntities = [];
private HashSet<int> _userPrivateEntityIds = [];
protected override async Task OnInitializedAsync()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
_appUser = await UserManager.GetUserAsync(authState.User);
if (_appUser is null)
{
Snackbar.Add("Not authenticated.", Severity.Error);
return;
}
_globalEntities = await CouchLogDb.GlobalEntities
.OrderByDescending(entity => entity.CreationTime)
.Where(entity => !entity.IsPrivate || (entity.IsPrivate && entity.CreatorId == _appUser.Id))
.ToListAsync();
_userPrivateEntityIds = await CouchLogDb.PrivateEntities
.Where(entity => entity.UserId == _appUser.Id)
.Select(p => p.GlobalEntityId)
.ToHashSetAsync();
}
private async Task RefreshPage()
{
_currentPage = 1;
StateHasChanged();
}
private async Task OnNewEntity(GlobalEntity entity)
{
_globalEntities.Add(entity);
await RefreshPage();
}
private async Task OpenAddNewGlobalEntityDialog()
{
var dialog = await DialogService.ShowAsync<AddNewGlobalEntityDialog>("Add new Global Entity");
var result = await dialog.Result;
if (!result!.Canceled)
{
var entity = (GlobalEntity)result.Data!;
await OnNewEntity(entity);
}
}
public async Task AddGlobalEntityToPrivateList(GlobalEntity globalEntity)
{
if(_appUser is null) { Snackbar.Add("Not logged in!", Severity.Error); return;}
if (!await IsInPrivateList(globalEntity.Id))
{
PrivateEntity privateEntity = new()
{
UserId = _appUser.Id,
CreationTime = DateTime.Now,
GlobalEntityId = globalEntity.Id,
UserWatchStatusId = 1,
};
CouchLogDb.PrivateEntities.Add(privateEntity);
await CouchLogDb.SaveChangesAsync();
_userPrivateEntityIds.Add(globalEntity.Id);
StateHasChanged();
Snackbar.Add("Added Entity to private List", Severity.Success);
}
else
{
Snackbar.Add("Entity already in private List!", Severity.Warning);
}
}
private async Task<bool> IsInPrivateList(int globalEntityId)
{
if (_appUser is null) return false;
return await CouchLogDb.PrivateEntities.Where(entity => entity.UserId == _appUser.Id).AnyAsync(entity => entity.GlobalEntityId == globalEntityId);
}
private void AddGlobalEntityToSharedList()
{
Snackbar.Add("Feature not implemented yet!", Severity.Error);
}
}
+2 -1
View File
@@ -1,5 +1,6 @@
@page "/"
@using CouchLog.Components.Layout
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@@ -8,4 +9,4 @@
<h1>Hello, world!</h1>
Welcome to your new app.
Welcome to your new app.
-216
View File
@@ -1,216 +0,0 @@
@page "/PrivateList"
@rendermode InteractiveServer
@using Microsoft.AspNetCore.Authorization
@using CouchLog.Data
@using Microsoft.AspNetCore.Identity
@using Microsoft.EntityFrameworkCore
@inject ApplicationDbContext CouchLogDB
@inject UserManager<ApplicationUser> UserManager
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject NavigationManager NavigationManager
@attribute [Authorize]
<PageTitle>Private List</PageTitle>
<div class="container-fluid mt-4 px-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Private List</h2>
</div>
<!-- Grid-Layout -->
<div class="row g-4">
@foreach (var Entity in PrivateEntities)
{
/*
Hier bleiben wir bei col-lg-6 (halbe Bildschirmbreite),
wie du es im roten Kasten wolltest.
*/
<div class="col-12 col-lg-6 col-xxl-4">
<div class="card shadow-sm border-0 overflow-hidden private-entity-card">
<div class="row g-0 h-100">
<!-- BILD: col-auto sorgt dafür, dass sich die Breite nach CSS richtet -->
<div class="col-auto img-wrapper">
@if (!string.IsNullOrEmpty(Entity.GlobalEntity?.PicturePath))
{
<img src="/@Entity.GlobalEntity.PicturePath"
class="entity-img"
alt="@Entity.GlobalEntity.Title">
}
else
{
<div class="d-flex align-items-center justify-content-center h-100 bg-light text-muted small px-2">
Kein Bild
</div>
}
</div>
<!-- INFO: 'col' nimmt automatisch den RESTLICHEN Platz ein -->
<div class="col">
<div class="card-body d-flex flex-column h-100 py-2 px-3">
<!-- Header -->
<div class="d-flex justify-content-between align-items-start">
<div class="overflow-hidden">
<h5 class="card-title fw-bold mb-0 text-truncate">@Entity.GlobalEntity?.Title</h5>
<small class="text-muted meta-text">
@Entity.CreationTime.ToShortDateString()
</small>
</div>
<div class="dropdown ms-1">
<button class="btn btn-link menu-btn" type="button" data-bs-toggle="modal" data-bs-target="#modal-@Entity.Id">
&#8942;
</button>
</div>
</div>
<select class="form-select"
value="@Entity.UserWatchStatusId"
@onchange="@(e => UpdateWatchStatus(Entity, e.Value))">
@foreach (var status in userWatchStatuses)
{
<option value="@status.Id">@status.Name</option>
}
</select>
@if (Entity.GlobalEntity?.MediaType.Name == "Series")
{
<select class="form-select" value="@Entity.Season" @onchange="@(e => UpdateSeason(Entity, e.Value))">
@{
int currentSeason = Entity.Season ?? 1;
int startOffsetSeason = currentSeason > 1 ? -1 : 0;
}
@for (int i = startOffsetSeason; i <= 5; i++)
{
int season = currentSeason + i;
<option value="@season">
Staffel @season
</option>
}
</select>
<select class="form-select" value="@Entity.Episode" @onchange="@(e => UpdateEpisode(Entity, e.Value))">
@{
int currentEpisode = Entity.Episode ?? 1;
int startOffsetEpisode = currentEpisode > 1 ? -1 : 0;
}
@for (int i = startOffsetEpisode; i <= 5; i++)
{
int episode = currentEpisode + i;
<option value="@episode">
Episode @episode
</option>
}
</select>
}
<!-- Beschreibung -->
<!--<p class="card-text text-truncate-multiline flex-grow-1 text-muted small mt-1 mb-2">
@(!string.IsNullOrWhiteSpace(Entity.Description)
? Entity.Description
: (Entity.Description ?? "Keine Beschreibung."))
</p>
-->
<!-- Footer Button -->
<div class="mt-auto d-flex justify-content-end">
<button class="btn btn-outline-secondary btn-sm btn-details" type="button">Details</button>
</div>
</div>
</div>
</div>
</div>
<!-- Modal (unverändert) -->
<div class="modal fade" id="modal-@Entity.Id" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Optionen</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">Optionen...</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Schließen</button>
</div>
</div>
</div>
</div>
</div>
}
</div>
</div>
@code
{
private List<PrivateEntity> PrivateEntities = new List<PrivateEntity>();
private List<UserWatchStatus> userWatchStatuses = new List<UserWatchStatus>();
ApplicationUser? AppUser = new();
protected override async Task OnInitializedAsync()
{
var AuthState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
AppUser = await UserManager.GetUserAsync(AuthState.User);
if (AppUser != null)
{
PrivateEntities = await CouchLogDB.PrivateEntities
.Where(x => x.UserId == AppUser.Id)
.Include(x => x.GlobalEntity)
.Include(x => x.UserWatchStatus)
.Include(x => x.GlobalEntity.MediaType)
.OrderByDescending(Entity => Entity.LastChange ?? Entity.CreationTime)
.ToListAsync();
userWatchStatuses = await CouchLogDB.UserWatchStatuses.OrderByDescending(Entity => Entity.Id).ToListAsync();
}
}
private async Task UpdateWatchStatus(PrivateEntity entity, object? newValue)
{
// Wir holen uns die gewählte ID aus dem Dropdown
if (int.TryParse(newValue?.ToString(), out int newId))
{
// 1. ID im Objekt aktualisieren
entity.UserWatchStatusId = newId;
// 2. Navigation Property aktualisieren (damit die UI nicht flackert)
// Wir suchen das passende Objekt aus der geladenen Liste
entity.UserWatchStatus = userWatchStatuses.FirstOrDefault(s => s.Id == newId);
// 3. Ab in die Datenbank
await CouchLogDB.SaveChangesAsync();
}
}
private async Task UpdateSeason(PrivateEntity entity, object? newSeasonValue)
{
if(int.TryParse(newSeasonValue?.ToString(), out int newSeason))
{
entity.Season = newSeason;
await CouchLogDB.SaveChangesAsync();
}
}
private async Task UpdateEpisode(PrivateEntity entity, object? newEpisodeValue)
{
if (int.TryParse(newEpisodeValue?.ToString(), out int newEpisode))
{
entity.Episode = newEpisode;
await CouchLogDB.SaveChangesAsync();
}
}
}
@@ -1,61 +0,0 @@
/* PrivateList.razor.css */
.private-entity-card {
height: 220px;
background-color: #fff;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.private-entity-card:hover {
transform: translateY(-3px);
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
}
/*
NEU: Feste Breite für das Bild!
150px Breite bei 220px Höhe passt perfekt für Poster.
*/
.img-wrapper {
width: 150px;
background-color: #f0f2f5;
position: relative;
}
/* Bild füllt den Wrapper komplett aus */
.entity-img {
object-fit: cover;
width: 100%;
height: 100%;
/* Positioniert das Bild oben, falls das Format doch leicht abweicht (Köpfe/Titel sind meist oben) */
object-position: top center;
}
.text-truncate-multiline {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 1.2;
}
.meta-text {
font-size: 0.75rem;
white-space: nowrap;
}
.menu-btn {
color: #212529;
text-decoration: none;
font-size: 1.2rem;
line-height: 1;
padding: 0 0.25rem;
}
.menu-btn:hover {
color: #000;
}
.btn-details {
font-size: 0.7rem;
padding: 0.2rem 0.6rem;
}
@@ -0,0 +1,91 @@
@using CouchLog.Data
<MudCard Style="max-width: 500px; background-color: #2a2a2a; color: white;" Elevation="4">
<MudCardContent Class="pa-0">
<MudGrid Spacing="0">
<!-- Poster Image -->
<MudItem xs="4">
<MudImage
Src="@PrivateEntity.GlobalEntity.PicturePath"
Alt=""
ObjectFit="ObjectFit.Cover"
Style="width: 100%; height: 100%; min-height: 180px; border-radius: 4px 0 0 4px;" />
</MudItem>
<!-- Content -->
<MudItem xs="8">
<MudStack Class="pa-3" Spacing="2">
<!-- Title Row with Menu -->
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween">
<MudText Typo="Typo.h6" Style="color: white; font-weight: 600;">@PrivateEntity.GlobalEntity.Title</MudText>
<MudMenu Icon="@Icons.Material.Filled.MoreVert" Color="Color.Default" Dense="true">
<MudMenuItem>Bearbeiten</MudMenuItem>
<MudMenuItem>Entfernen</MudMenuItem>
</MudMenu>
</MudStack>
<!-- Date -->
<!-- <MudText Typo="Typo.caption" Style="color: #aaaaaa;">@Date.ToString("MM/dd/yyyy")</MudText> -->
<!-- Status Dropdown -->
<MudSelect
T="string"
@bind-Value="@PrivateEntity.UserWatchStatus!.Name"
Dense="true"
Variant="Variant.Outlined"
Style="background-color: #3a3a3a; color: white;"
Label="">
<!--@foreach (var status in )
{
<MudSelectItem Value="@status">@status</MudSelectItem>
}-->
</MudSelect>
<!-- Season Dropdown -->
<MudSelect
T="int?"
@bind-Value="@PrivateEntity.Season"
Dense="true"
Variant="Variant.Outlined"
Style="background-color: #3a3a3a; color: white;"
Label="">
<!-- @foreach (var season in SeasonOptions)
{
<MudSelectItem Value="@season">@season</MudSelectItem>
}-->
</MudSelect>
<!-- Episode Dropdown -->
<MudSelect
T="int?"
@bind-Value="@PrivateEntity.Episode"
Dense="true"
Variant="Variant.Outlined"
Style="background-color: #3a3a3a; color: white;"
Label="">
<!--@foreach (var episode in EpisodeOptions)
{
<MudSelectItem Value="@episode">@episode</MudSelectItem>
}-->
</MudSelect>
<!-- Details Button -->
<MudStack Row="true" Justify="Justify.FlexEnd">
<MudButton
Variant="Variant.Outlined"
Size="Size.Small"
Style="color: #aaaaaa; border-color: #555555;">
Details
</MudButton>
</MudStack>
</MudStack>
</MudItem>
</MudGrid>
</MudCardContent>
</MudCard>
@code
{
[Parameter] public PrivateEntity PrivateEntity { get; set; } = null!;
[Parameter] public List<UserWatchStatus> UserWatchStatuses { get; set; } = null!;
}
@@ -0,0 +1,160 @@
@page "/PrivateList"
@rendermode InteractiveServer
@using CouchLog.Components.Pages.PrivateList.Components
@using Microsoft.AspNetCore.Authorization
@using CouchLog.Data
@using Microsoft.AspNetCore.Identity
@using Microsoft.EntityFrameworkCore
@inject ApplicationDbContext CouchLogDb
@inject UserManager<ApplicationUser> UserManager
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject NavigationManager NavigationManager
@attribute [Authorize]
<PageTitle>Private List</PageTitle>
<MudContainer MaxWidth="MaxWidth.False">
@*Top of the Page*@
<MudContainer>
<MudStack Row="true" AlignItems="AlignItems.Center">
@*Page Title*@
<MudText Typo="Typo.h3" Style="font-weight: bold;">Private List</MudText>
</MudStack>
</MudContainer>
<br/>
<MudContainer>
<MudPaper Class="pa-2" Elevation="1">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
@*Search bar*@
<MudTextField @bind-Value="_searchString"
Placeholder="Search..."
Adornment="Adornment.Start"
AdornmentIcon="@Icons.Material.Filled.Search"
Immediate="true"
Variant="Variant.Outlined"
Margin="Margin.Dense"
Style="max-width: 300px;"/>
@*Refresh button*@
@*<MudTooltip Text="Refresh Entities">
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="RefreshPage"/>
</MudTooltip>*@
@*More Filters button*@
<MudTooltip Text="More filters">
<MudIconButton Icon="@(_showFilters
? Icons.Material.Filled.ExpandLess
: Icons.Material.Filled.FilterList)"
OnClick="@(() => _showFilters = !_showFilters)"
Color="Color.Default"/>
</MudTooltip>
</MudStack>
<MudCollapse Expanded="_showFilters">
@*Entity allready added*@
<MudSwitch @bind-Value="_showAlreadyAddedEntities" Color="Color.Primary">
Show already added Entities
</MudSwitch>
@*To be exstendet*@
</MudCollapse>
</MudPaper>
</MudContainer>
<br/>
<!--<editor-fold desc="Active Watching">-->
<MudContainer MaxWidth="MaxWidth.False">
<MudText Typo="Typo.h4">Active Watching</MudText>
<br/>
<MudGrid Spacing="6" Justify="Justify.Center">
@foreach (var privateEntity in VisibleActiveWatchingEntities)
{
<PrivateEntityCard PrivateEntity="privateEntity"/>
}
</MudGrid>
</MudContainer>
<br/>
<MudContainer>
<MudStack AlignItems="AlignItems.Center">
<MudPagination Count="@TotalPagesActiveWatching" @bind-Selected="_currentActiveWatchingPage"/>
</MudStack>
</MudContainer>
<!--</editor-fold>-->
<!--<editor-fold desc="Library">-->
<MudContainer MaxWidth="MaxWidth.False">
<MudText Typo="Typo.h4">Library</MudText>
<br/>
<MudGrid Spacing="6" Justify="Justify.Center">
@foreach (var privateEntity in VisibleLibraryEntities)
{
<PrivateEntityCard PrivateEntity="privateEntity"/>
}
</MudGrid>
</MudContainer>
<br/>
<MudContainer>
<MudStack AlignItems="AlignItems.Center">
<MudPagination Count="@TotalPagesLibrary" @bind-Selected="_currentLibraryPage"/>
</MudStack>
</MudContainer>
<!--</editor-fold>-->
</MudContainer>
@code
{
//Filter
private string? _searchString;
private bool _showFilters;
private bool _showAlreadyAddedEntities = false;
private IEnumerable<PrivateEntity> FiltertGlobalEntities => _privateEntities.Where(entity =>
(string.IsNullOrWhiteSpace(_searchString) || entity.GlobalEntity.Title.Contains(_searchString, StringComparison.OrdinalIgnoreCase)) //Search bar
);
//Paging Active Watching
private int _currentActiveWatchingPage = 1;
private int _pageSizeActiveWatching = 12;
private int TotalPagesActiveWatching => (int)Math.Ceiling(ActiveWatchingEntities.Count() / (double)_pageSizeActiveWatching);
private IEnumerable<PrivateEntity> VisibleActiveWatchingEntities => ActiveWatchingEntities.Skip((_currentActiveWatchingPage - 1) * _pageSizeActiveWatching).Take(_pageSizeActiveWatching);
//Paging Library
private int _currentLibraryPage = 1;
private int _pageSizeLibrary = 12;
private int TotalPagesLibrary => (int)Math.Ceiling(LibraryEntities.Count() / (double)_pageSizeLibrary);
private IEnumerable<PrivateEntity> VisibleLibraryEntities => LibraryEntities.Skip((_currentLibraryPage - 1) * _pageSizeLibrary).Take(_pageSizeLibrary);
//User
private ApplicationUser? _appUser;
//Entities
private List<PrivateEntity> _privateEntities = [];
private List<UserWatchStatus> _userWatchStatuses = [];
private IEnumerable<PrivateEntity> ActiveWatchingEntities => 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()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
_appUser = await UserManager.GetUserAsync(authState.User);
if (_appUser != null)
{
_privateEntities = await CouchLogDb.PrivateEntities
.Where(x => x.UserId == _appUser.Id)
.Include(x => x.GlobalEntity)
.Include(x => x.UserWatchStatus)
.Include(x => x.GlobalEntity.MediaType)
.OrderByDescending(entity => entity.LastChange ?? entity.CreationTime)
.ToListAsync();
_userWatchStatuses = await CouchLogDb.UserWatchStatuses.OrderByDescending(entity => entity.Id).ToListAsync();
}
}
}
+1 -1
View File
@@ -8,4 +8,4 @@
</AuthorizeRouteView>
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>
</Router>
+1
View File
@@ -9,3 +9,4 @@
@using Microsoft.JSInterop
@using CouchLog
@using CouchLog.Components
@using MudBlazor
+1
View File
@@ -77,6 +77,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="MudBlazor" Version="8.15.0" />
</ItemGroup>
<ItemGroup>
+1 -1
View File
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.1.11304.174 d18.0
VisualStudioVersion = 18.1.11304.174
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CouchLog", "CouchLog.csproj", "{4FBF15C3-5FC5-4605-9EBC-3F7DA1ADC47A}"
EndProject
+4
View File
@@ -23,5 +23,9 @@ namespace CouchLog.Data
public DbSet<SharedListEntity> SharedListEntities { get; set; }
public DbSet<SharedListLabel> SharedListLabels { get; set; }
public DbSet<SharedWatchStatus> SharedWatchStatuses { get; set; }
//Settings
public DbSet<CouchLogState> CouchLogState { get; set; }
public DbSet<AccountsSettings> AccountsSettings { get; set; }
}
}
@@ -27,6 +27,6 @@ namespace CouchLog.Data
[ForeignKey(nameof(CreatorId))]
public virtual ApplicationUser User { get; set; } = null!;
public virtual ICollection<LinkTablePrivateLabel> LinkTablePrivateLabels { get; set; } = new List<LinkTablePrivateLabel>();
public virtual ICollection<LinkTablePrivateLabel> LinkTablePrivateLabels { get; set; } = [];
}
}
@@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace CouchLog.Data
{
public class AccountsSettings
{
[Key]
public int Id { get; set; }
public bool IsRegistrationAllowed { get; set; }
//More Options that can be added
}
}
@@ -0,0 +1,9 @@
namespace CouchLog.Data
{
public class CouchLogState
{
public int Id { get; set; }
public bool IsInitialized { get; set; }
public DateTime? InitializationDate { get; set; }
}
}
@@ -0,0 +1,978 @@
// <auto-generated />
using System;
using CouchLog.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace CouchLog.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20251229004324_CouchLogState")]
partial class CouchLogState
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "10.0.1");
modelBuilder.Entity("CouchLog.Data.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("CouchLog.Data.CouchLogState", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime?>("InitializationDate")
.HasColumnType("TEXT");
b.Property<bool>("IsInitialized")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("CouchLogState");
});
modelBuilder.Entity("CouchLog.Data.Genre", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Genres");
});
modelBuilder.Entity("CouchLog.Data.GlobalEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("CreatorId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("IsPrivate")
.HasColumnType("INTEGER");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("PicturePath")
.HasMaxLength(500)
.HasColumnType("TEXT");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("TEXT");
b.Property<int>("TypeId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CreatorId");
b.HasIndex("TypeId");
b.ToTable("GlobalEntities");
});
modelBuilder.Entity("CouchLog.Data.Label", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("CreatorId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CreatorId");
b.ToTable("Labels");
});
modelBuilder.Entity("CouchLog.Data.LinkTableGlobalGenre", b =>
{
b.Property<int>("GlobalEntityId")
.HasColumnType("INTEGER")
.HasColumnOrder(0);
b.Property<int>("GenreId")
.HasColumnType("INTEGER")
.HasColumnOrder(1);
b.HasKey("GlobalEntityId", "GenreId");
b.HasIndex("GenreId");
b.ToTable("LinkTableGlobalGenre");
});
modelBuilder.Entity("CouchLog.Data.LinkTablePrivateLabel", b =>
{
b.Property<int>("PrivateEntityId")
.HasColumnType("INTEGER");
b.Property<int>("LabelId")
.HasColumnType("INTEGER");
b.HasKey("PrivateEntityId", "LabelId");
b.HasIndex("LabelId");
b.ToTable("LinkTablePrivateLabel");
});
modelBuilder.Entity("CouchLog.Data.LinkTablePrivateStreamingPlatform", b =>
{
b.Property<int>("PrivateEntityId")
.HasColumnType("INTEGER");
b.Property<int>("StreamingPlatformId")
.HasColumnType("INTEGER");
b.Property<int?>("SharedListEntityId")
.HasColumnType("INTEGER");
b.HasKey("PrivateEntityId", "StreamingPlatformId");
b.HasIndex("SharedListEntityId");
b.HasIndex("StreamingPlatformId");
b.ToTable("LinkTablePrivateStreamingPlatform");
});
modelBuilder.Entity("CouchLog.Data.LinkTableSharedLabel", b =>
{
b.Property<int>("SharedListLabelId")
.HasColumnType("INTEGER");
b.Property<int>("SharedListEntityId")
.HasColumnType("INTEGER");
b.HasKey("SharedListLabelId", "SharedListEntityId");
b.HasIndex("SharedListEntityId");
b.ToTable("LinkTableSharedLabel");
});
modelBuilder.Entity("CouchLog.Data.LinkTableSharedUser", b =>
{
b.Property<int>("SharedListId")
.HasColumnType("INTEGER");
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.HasKey("SharedListId", "UserId");
b.HasIndex("UserId");
b.ToTable("LinkTableSharedUser");
});
modelBuilder.Entity("CouchLog.Data.MediaType", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("MediaType");
});
modelBuilder.Entity("CouchLog.Data.PrivateEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("TEXT");
b.Property<int?>("Episode")
.HasColumnType("INTEGER");
b.Property<bool>("Favorite")
.HasColumnType("INTEGER");
b.Property<int>("GlobalEntityId")
.HasColumnType("INTEGER");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<int?>("Rating")
.HasColumnType("INTEGER");
b.Property<int?>("Season")
.HasColumnType("INTEGER");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("UserWatchStatusId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("GlobalEntityId");
b.HasIndex("UserId");
b.HasIndex("UserWatchStatusId");
b.ToTable("PrivateEntities");
});
modelBuilder.Entity("CouchLog.Data.SharedList", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(150)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("SharedLists");
});
modelBuilder.Entity("CouchLog.Data.SharedListEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("TEXT");
b.Property<int?>("Episode")
.HasColumnType("INTEGER");
b.Property<bool>("Favorite")
.HasColumnType("INTEGER");
b.Property<int>("GlobalEntityId")
.HasColumnType("INTEGER");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<int?>("Season")
.HasColumnType("INTEGER");
b.Property<int>("SharedListId")
.HasColumnType("INTEGER");
b.Property<int?>("SharedWatchStatusId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("GlobalEntityId");
b.HasIndex("SharedListId");
b.HasIndex("SharedWatchStatusId");
b.ToTable("SharedListEntities");
});
modelBuilder.Entity("CouchLog.Data.SharedListLabel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<int>("SharedListId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("SharedListId");
b.ToTable("SharedListLabels");
});
modelBuilder.Entity("CouchLog.Data.SharedWatchStatus", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ColorCode")
.HasMaxLength(7)
.HasColumnType("TEXT");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<int>("SharedListId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("SharedListId");
b.ToTable("SharedWatchStatuses");
});
modelBuilder.Entity("CouchLog.Data.StreamingPlatform", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("TEXT");
b.Property<string>("PicturePath")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("StreamingPlatforms");
});
modelBuilder.Entity("CouchLog.Data.UserWatchStatus", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ColorCode")
.HasMaxLength(7)
.HasColumnType("TEXT");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("UserWatchStatuses");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("CouchLog.Data.GlobalEntity", b =>
{
b.HasOne("CouchLog.Data.ApplicationUser", "User")
.WithMany("CreatedGlobalEntities")
.HasForeignKey("CreatorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.MediaType", "MediaType")
.WithMany("GlobalEntities")
.HasForeignKey("TypeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("MediaType");
b.Navigation("User");
});
modelBuilder.Entity("CouchLog.Data.Label", b =>
{
b.HasOne("CouchLog.Data.ApplicationUser", "User")
.WithMany("CreatedLabels")
.HasForeignKey("CreatorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("CouchLog.Data.LinkTableGlobalGenre", b =>
{
b.HasOne("CouchLog.Data.Genre", "Genre")
.WithMany("LinkTableGlobalGenres")
.HasForeignKey("GenreId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.GlobalEntity", "GlobalEntity")
.WithMany("LinkTableGlobalGenres")
.HasForeignKey("GlobalEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Genre");
b.Navigation("GlobalEntity");
});
modelBuilder.Entity("CouchLog.Data.LinkTablePrivateLabel", b =>
{
b.HasOne("CouchLog.Data.Label", "Label")
.WithMany("LinkTablePrivateLabels")
.HasForeignKey("LabelId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.PrivateEntity", "PrivateEntity")
.WithMany("PrivateEntityLabels")
.HasForeignKey("PrivateEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Label");
b.Navigation("PrivateEntity");
});
modelBuilder.Entity("CouchLog.Data.LinkTablePrivateStreamingPlatform", b =>
{
b.HasOne("CouchLog.Data.PrivateEntity", "PrivateEntity")
.WithMany("PrivateStreamingPlatforms")
.HasForeignKey("PrivateEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.SharedListEntity", null)
.WithMany("LinkTablePrivateStreamingPlatforms")
.HasForeignKey("SharedListEntityId");
b.HasOne("CouchLog.Data.StreamingPlatform", "StreamingPlatform")
.WithMany()
.HasForeignKey("StreamingPlatformId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("PrivateEntity");
b.Navigation("StreamingPlatform");
});
modelBuilder.Entity("CouchLog.Data.LinkTableSharedLabel", b =>
{
b.HasOne("CouchLog.Data.SharedListEntity", "SharedListEntity")
.WithMany("LinkTableSharedLabels")
.HasForeignKey("SharedListEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.SharedListLabel", "SharedListLabel")
.WithMany("LinkTableSharedLabels")
.HasForeignKey("SharedListLabelId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SharedListEntity");
b.Navigation("SharedListLabel");
});
modelBuilder.Entity("CouchLog.Data.LinkTableSharedUser", b =>
{
b.HasOne("CouchLog.Data.SharedList", "SharedList")
.WithMany("SharedListUsers")
.HasForeignKey("SharedListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.ApplicationUser", "User")
.WithMany("LinkTableSharedUsers")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SharedList");
b.Navigation("User");
});
modelBuilder.Entity("CouchLog.Data.PrivateEntity", b =>
{
b.HasOne("CouchLog.Data.GlobalEntity", "GlobalEntity")
.WithMany("PrivateEntities")
.HasForeignKey("GlobalEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.ApplicationUser", "User")
.WithMany("PrivateEntities")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.UserWatchStatus", "UserWatchStatus")
.WithMany()
.HasForeignKey("UserWatchStatusId");
b.Navigation("GlobalEntity");
b.Navigation("User");
b.Navigation("UserWatchStatus");
});
modelBuilder.Entity("CouchLog.Data.SharedListEntity", b =>
{
b.HasOne("CouchLog.Data.GlobalEntity", "GlobalEntity")
.WithMany("SharedListEntities")
.HasForeignKey("GlobalEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.SharedList", "SharedList")
.WithMany("SharedListEntities")
.HasForeignKey("SharedListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.SharedWatchStatus", "SharedWatchStatus")
.WithMany("SharedListEntities")
.HasForeignKey("SharedWatchStatusId");
b.Navigation("GlobalEntity");
b.Navigation("SharedList");
b.Navigation("SharedWatchStatus");
});
modelBuilder.Entity("CouchLog.Data.SharedListLabel", b =>
{
b.HasOne("CouchLog.Data.SharedList", "SharedList")
.WithMany("SharedListLabels")
.HasForeignKey("SharedListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SharedList");
});
modelBuilder.Entity("CouchLog.Data.SharedWatchStatus", b =>
{
b.HasOne("CouchLog.Data.SharedList", "SharedList")
.WithMany("SharedWatchStatuses")
.HasForeignKey("SharedListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SharedList");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("CouchLog.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("CouchLog.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("CouchLog.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("CouchLog.Data.ApplicationUser", b =>
{
b.Navigation("CreatedGlobalEntities");
b.Navigation("CreatedLabels");
b.Navigation("LinkTableSharedUsers");
b.Navigation("PrivateEntities");
});
modelBuilder.Entity("CouchLog.Data.Genre", b =>
{
b.Navigation("LinkTableGlobalGenres");
});
modelBuilder.Entity("CouchLog.Data.GlobalEntity", b =>
{
b.Navigation("LinkTableGlobalGenres");
b.Navigation("PrivateEntities");
b.Navigation("SharedListEntities");
});
modelBuilder.Entity("CouchLog.Data.Label", b =>
{
b.Navigation("LinkTablePrivateLabels");
});
modelBuilder.Entity("CouchLog.Data.MediaType", b =>
{
b.Navigation("GlobalEntities");
});
modelBuilder.Entity("CouchLog.Data.PrivateEntity", b =>
{
b.Navigation("PrivateEntityLabels");
b.Navigation("PrivateStreamingPlatforms");
});
modelBuilder.Entity("CouchLog.Data.SharedList", b =>
{
b.Navigation("SharedListEntities");
b.Navigation("SharedListLabels");
b.Navigation("SharedListUsers");
b.Navigation("SharedWatchStatuses");
});
modelBuilder.Entity("CouchLog.Data.SharedListEntity", b =>
{
b.Navigation("LinkTablePrivateStreamingPlatforms");
b.Navigation("LinkTableSharedLabels");
});
modelBuilder.Entity("CouchLog.Data.SharedListLabel", b =>
{
b.Navigation("LinkTableSharedLabels");
});
modelBuilder.Entity("CouchLog.Data.SharedWatchStatus", b =>
{
b.Navigation("SharedListEntities");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,36 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CouchLog.Migrations
{
/// <inheritdoc />
public partial class CouchLogState : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "CouchLogState",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
IsInitialized = table.Column<bool>(type: "INTEGER", nullable: false),
InitializationDate = table.Column<DateTime>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_CouchLogState", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CouchLogState");
}
}
}
@@ -0,0 +1,992 @@
// <auto-generated />
using System;
using CouchLog.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace CouchLog.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20260103202104_AccountsSettings")]
partial class AccountsSettings
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "10.0.1");
modelBuilder.Entity("CouchLog.Data.AccountsSettings", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<bool>("IsRegistrationAllowed")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("AccountsSettings");
});
modelBuilder.Entity("CouchLog.Data.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("CouchLog.Data.CouchLogState", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime?>("InitializationDate")
.HasColumnType("TEXT");
b.Property<bool>("IsInitialized")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("CouchLogState");
});
modelBuilder.Entity("CouchLog.Data.Genre", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Genres");
});
modelBuilder.Entity("CouchLog.Data.GlobalEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("CreatorId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("IsPrivate")
.HasColumnType("INTEGER");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("PicturePath")
.HasMaxLength(500)
.HasColumnType("TEXT");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("TEXT");
b.Property<int>("TypeId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CreatorId");
b.HasIndex("TypeId");
b.ToTable("GlobalEntities");
});
modelBuilder.Entity("CouchLog.Data.Label", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("CreatorId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CreatorId");
b.ToTable("Labels");
});
modelBuilder.Entity("CouchLog.Data.LinkTableGlobalGenre", b =>
{
b.Property<int>("GlobalEntityId")
.HasColumnType("INTEGER")
.HasColumnOrder(0);
b.Property<int>("GenreId")
.HasColumnType("INTEGER")
.HasColumnOrder(1);
b.HasKey("GlobalEntityId", "GenreId");
b.HasIndex("GenreId");
b.ToTable("LinkTableGlobalGenre");
});
modelBuilder.Entity("CouchLog.Data.LinkTablePrivateLabel", b =>
{
b.Property<int>("PrivateEntityId")
.HasColumnType("INTEGER");
b.Property<int>("LabelId")
.HasColumnType("INTEGER");
b.HasKey("PrivateEntityId", "LabelId");
b.HasIndex("LabelId");
b.ToTable("LinkTablePrivateLabel");
});
modelBuilder.Entity("CouchLog.Data.LinkTablePrivateStreamingPlatform", b =>
{
b.Property<int>("PrivateEntityId")
.HasColumnType("INTEGER");
b.Property<int>("StreamingPlatformId")
.HasColumnType("INTEGER");
b.Property<int?>("SharedListEntityId")
.HasColumnType("INTEGER");
b.HasKey("PrivateEntityId", "StreamingPlatformId");
b.HasIndex("SharedListEntityId");
b.HasIndex("StreamingPlatformId");
b.ToTable("LinkTablePrivateStreamingPlatform");
});
modelBuilder.Entity("CouchLog.Data.LinkTableSharedLabel", b =>
{
b.Property<int>("SharedListLabelId")
.HasColumnType("INTEGER");
b.Property<int>("SharedListEntityId")
.HasColumnType("INTEGER");
b.HasKey("SharedListLabelId", "SharedListEntityId");
b.HasIndex("SharedListEntityId");
b.ToTable("LinkTableSharedLabel");
});
modelBuilder.Entity("CouchLog.Data.LinkTableSharedUser", b =>
{
b.Property<int>("SharedListId")
.HasColumnType("INTEGER");
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.HasKey("SharedListId", "UserId");
b.HasIndex("UserId");
b.ToTable("LinkTableSharedUser");
});
modelBuilder.Entity("CouchLog.Data.MediaType", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("MediaType");
});
modelBuilder.Entity("CouchLog.Data.PrivateEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("TEXT");
b.Property<int?>("Episode")
.HasColumnType("INTEGER");
b.Property<bool>("Favorite")
.HasColumnType("INTEGER");
b.Property<int>("GlobalEntityId")
.HasColumnType("INTEGER");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<int?>("Rating")
.HasColumnType("INTEGER");
b.Property<int?>("Season")
.HasColumnType("INTEGER");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("UserWatchStatusId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("GlobalEntityId");
b.HasIndex("UserId");
b.HasIndex("UserWatchStatusId");
b.ToTable("PrivateEntities");
});
modelBuilder.Entity("CouchLog.Data.SharedList", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(150)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("SharedLists");
});
modelBuilder.Entity("CouchLog.Data.SharedListEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("TEXT");
b.Property<int?>("Episode")
.HasColumnType("INTEGER");
b.Property<bool>("Favorite")
.HasColumnType("INTEGER");
b.Property<int>("GlobalEntityId")
.HasColumnType("INTEGER");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<int?>("Season")
.HasColumnType("INTEGER");
b.Property<int>("SharedListId")
.HasColumnType("INTEGER");
b.Property<int?>("SharedWatchStatusId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("GlobalEntityId");
b.HasIndex("SharedListId");
b.HasIndex("SharedWatchStatusId");
b.ToTable("SharedListEntities");
});
modelBuilder.Entity("CouchLog.Data.SharedListLabel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<int>("SharedListId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("SharedListId");
b.ToTable("SharedListLabels");
});
modelBuilder.Entity("CouchLog.Data.SharedWatchStatus", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ColorCode")
.HasMaxLength(7)
.HasColumnType("TEXT");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<int>("SharedListId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("SharedListId");
b.ToTable("SharedWatchStatuses");
});
modelBuilder.Entity("CouchLog.Data.StreamingPlatform", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("TEXT");
b.Property<string>("PicturePath")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("StreamingPlatforms");
});
modelBuilder.Entity("CouchLog.Data.UserWatchStatus", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ColorCode")
.HasMaxLength(7)
.HasColumnType("TEXT");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("UserWatchStatuses");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("CouchLog.Data.GlobalEntity", b =>
{
b.HasOne("CouchLog.Data.ApplicationUser", "User")
.WithMany("CreatedGlobalEntities")
.HasForeignKey("CreatorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.MediaType", "MediaType")
.WithMany("GlobalEntities")
.HasForeignKey("TypeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("MediaType");
b.Navigation("User");
});
modelBuilder.Entity("CouchLog.Data.Label", b =>
{
b.HasOne("CouchLog.Data.ApplicationUser", "User")
.WithMany("CreatedLabels")
.HasForeignKey("CreatorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("CouchLog.Data.LinkTableGlobalGenre", b =>
{
b.HasOne("CouchLog.Data.Genre", "Genre")
.WithMany("LinkTableGlobalGenres")
.HasForeignKey("GenreId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.GlobalEntity", "GlobalEntity")
.WithMany("LinkTableGlobalGenres")
.HasForeignKey("GlobalEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Genre");
b.Navigation("GlobalEntity");
});
modelBuilder.Entity("CouchLog.Data.LinkTablePrivateLabel", b =>
{
b.HasOne("CouchLog.Data.Label", "Label")
.WithMany("LinkTablePrivateLabels")
.HasForeignKey("LabelId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.PrivateEntity", "PrivateEntity")
.WithMany("PrivateEntityLabels")
.HasForeignKey("PrivateEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Label");
b.Navigation("PrivateEntity");
});
modelBuilder.Entity("CouchLog.Data.LinkTablePrivateStreamingPlatform", b =>
{
b.HasOne("CouchLog.Data.PrivateEntity", "PrivateEntity")
.WithMany("PrivateStreamingPlatforms")
.HasForeignKey("PrivateEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.SharedListEntity", null)
.WithMany("LinkTablePrivateStreamingPlatforms")
.HasForeignKey("SharedListEntityId");
b.HasOne("CouchLog.Data.StreamingPlatform", "StreamingPlatform")
.WithMany()
.HasForeignKey("StreamingPlatformId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("PrivateEntity");
b.Navigation("StreamingPlatform");
});
modelBuilder.Entity("CouchLog.Data.LinkTableSharedLabel", b =>
{
b.HasOne("CouchLog.Data.SharedListEntity", "SharedListEntity")
.WithMany("LinkTableSharedLabels")
.HasForeignKey("SharedListEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.SharedListLabel", "SharedListLabel")
.WithMany("LinkTableSharedLabels")
.HasForeignKey("SharedListLabelId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SharedListEntity");
b.Navigation("SharedListLabel");
});
modelBuilder.Entity("CouchLog.Data.LinkTableSharedUser", b =>
{
b.HasOne("CouchLog.Data.SharedList", "SharedList")
.WithMany("SharedListUsers")
.HasForeignKey("SharedListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.ApplicationUser", "User")
.WithMany("LinkTableSharedUsers")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SharedList");
b.Navigation("User");
});
modelBuilder.Entity("CouchLog.Data.PrivateEntity", b =>
{
b.HasOne("CouchLog.Data.GlobalEntity", "GlobalEntity")
.WithMany("PrivateEntities")
.HasForeignKey("GlobalEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.ApplicationUser", "User")
.WithMany("PrivateEntities")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.UserWatchStatus", "UserWatchStatus")
.WithMany()
.HasForeignKey("UserWatchStatusId");
b.Navigation("GlobalEntity");
b.Navigation("User");
b.Navigation("UserWatchStatus");
});
modelBuilder.Entity("CouchLog.Data.SharedListEntity", b =>
{
b.HasOne("CouchLog.Data.GlobalEntity", "GlobalEntity")
.WithMany("SharedListEntities")
.HasForeignKey("GlobalEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.SharedList", "SharedList")
.WithMany("SharedListEntities")
.HasForeignKey("SharedListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.SharedWatchStatus", "SharedWatchStatus")
.WithMany("SharedListEntities")
.HasForeignKey("SharedWatchStatusId");
b.Navigation("GlobalEntity");
b.Navigation("SharedList");
b.Navigation("SharedWatchStatus");
});
modelBuilder.Entity("CouchLog.Data.SharedListLabel", b =>
{
b.HasOne("CouchLog.Data.SharedList", "SharedList")
.WithMany("SharedListLabels")
.HasForeignKey("SharedListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SharedList");
});
modelBuilder.Entity("CouchLog.Data.SharedWatchStatus", b =>
{
b.HasOne("CouchLog.Data.SharedList", "SharedList")
.WithMany("SharedWatchStatuses")
.HasForeignKey("SharedListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SharedList");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("CouchLog.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("CouchLog.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("CouchLog.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("CouchLog.Data.ApplicationUser", b =>
{
b.Navigation("CreatedGlobalEntities");
b.Navigation("CreatedLabels");
b.Navigation("LinkTableSharedUsers");
b.Navigation("PrivateEntities");
});
modelBuilder.Entity("CouchLog.Data.Genre", b =>
{
b.Navigation("LinkTableGlobalGenres");
});
modelBuilder.Entity("CouchLog.Data.GlobalEntity", b =>
{
b.Navigation("LinkTableGlobalGenres");
b.Navigation("PrivateEntities");
b.Navigation("SharedListEntities");
});
modelBuilder.Entity("CouchLog.Data.Label", b =>
{
b.Navigation("LinkTablePrivateLabels");
});
modelBuilder.Entity("CouchLog.Data.MediaType", b =>
{
b.Navigation("GlobalEntities");
});
modelBuilder.Entity("CouchLog.Data.PrivateEntity", b =>
{
b.Navigation("PrivateEntityLabels");
b.Navigation("PrivateStreamingPlatforms");
});
modelBuilder.Entity("CouchLog.Data.SharedList", b =>
{
b.Navigation("SharedListEntities");
b.Navigation("SharedListLabels");
b.Navigation("SharedListUsers");
b.Navigation("SharedWatchStatuses");
});
modelBuilder.Entity("CouchLog.Data.SharedListEntity", b =>
{
b.Navigation("LinkTablePrivateStreamingPlatforms");
b.Navigation("LinkTableSharedLabels");
});
modelBuilder.Entity("CouchLog.Data.SharedListLabel", b =>
{
b.Navigation("LinkTableSharedLabels");
});
modelBuilder.Entity("CouchLog.Data.SharedWatchStatus", b =>
{
b.Navigation("SharedListEntities");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CouchLog.Migrations
{
/// <inheritdoc />
public partial class AccountsSettings : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AccountsSettings",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
IsRegistrationAllowed = table.Column<bool>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AccountsSettings", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AccountsSettings");
}
}
}
@@ -0,0 +1,992 @@
// <auto-generated />
using System;
using CouchLog.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace CouchLog.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20260104151950_FixAccountsSettingsKey")]
partial class FixAccountsSettingsKey
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "10.0.1");
modelBuilder.Entity("CouchLog.Data.AccountsSettings", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<bool>("IsRegistrationAllowed")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("AccountsSettings");
});
modelBuilder.Entity("CouchLog.Data.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("CouchLog.Data.CouchLogState", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime?>("InitializationDate")
.HasColumnType("TEXT");
b.Property<bool>("IsInitialized")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("CouchLogState");
});
modelBuilder.Entity("CouchLog.Data.Genre", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Genres");
});
modelBuilder.Entity("CouchLog.Data.GlobalEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("CreatorId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<bool>("IsPrivate")
.HasColumnType("INTEGER");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("PicturePath")
.HasMaxLength(500)
.HasColumnType("TEXT");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("TEXT");
b.Property<int>("TypeId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("CreatorId");
b.HasIndex("TypeId");
b.ToTable("GlobalEntities");
});
modelBuilder.Entity("CouchLog.Data.Label", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("CreatorId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CreatorId");
b.ToTable("Labels");
});
modelBuilder.Entity("CouchLog.Data.LinkTableGlobalGenre", b =>
{
b.Property<int>("GlobalEntityId")
.HasColumnType("INTEGER")
.HasColumnOrder(0);
b.Property<int>("GenreId")
.HasColumnType("INTEGER")
.HasColumnOrder(1);
b.HasKey("GlobalEntityId", "GenreId");
b.HasIndex("GenreId");
b.ToTable("LinkTableGlobalGenre");
});
modelBuilder.Entity("CouchLog.Data.LinkTablePrivateLabel", b =>
{
b.Property<int>("PrivateEntityId")
.HasColumnType("INTEGER");
b.Property<int>("LabelId")
.HasColumnType("INTEGER");
b.HasKey("PrivateEntityId", "LabelId");
b.HasIndex("LabelId");
b.ToTable("LinkTablePrivateLabel");
});
modelBuilder.Entity("CouchLog.Data.LinkTablePrivateStreamingPlatform", b =>
{
b.Property<int>("PrivateEntityId")
.HasColumnType("INTEGER");
b.Property<int>("StreamingPlatformId")
.HasColumnType("INTEGER");
b.Property<int?>("SharedListEntityId")
.HasColumnType("INTEGER");
b.HasKey("PrivateEntityId", "StreamingPlatformId");
b.HasIndex("SharedListEntityId");
b.HasIndex("StreamingPlatformId");
b.ToTable("LinkTablePrivateStreamingPlatform");
});
modelBuilder.Entity("CouchLog.Data.LinkTableSharedLabel", b =>
{
b.Property<int>("SharedListLabelId")
.HasColumnType("INTEGER");
b.Property<int>("SharedListEntityId")
.HasColumnType("INTEGER");
b.HasKey("SharedListLabelId", "SharedListEntityId");
b.HasIndex("SharedListEntityId");
b.ToTable("LinkTableSharedLabel");
});
modelBuilder.Entity("CouchLog.Data.LinkTableSharedUser", b =>
{
b.Property<int>("SharedListId")
.HasColumnType("INTEGER");
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.HasKey("SharedListId", "UserId");
b.HasIndex("UserId");
b.ToTable("LinkTableSharedUser");
});
modelBuilder.Entity("CouchLog.Data.MediaType", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("MediaType");
});
modelBuilder.Entity("CouchLog.Data.PrivateEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("TEXT");
b.Property<int?>("Episode")
.HasColumnType("INTEGER");
b.Property<bool>("Favorite")
.HasColumnType("INTEGER");
b.Property<int>("GlobalEntityId")
.HasColumnType("INTEGER");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<int?>("Rating")
.HasColumnType("INTEGER");
b.Property<int?>("Season")
.HasColumnType("INTEGER");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int?>("UserWatchStatusId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("GlobalEntityId");
b.HasIndex("UserId");
b.HasIndex("UserWatchStatusId");
b.ToTable("PrivateEntities");
});
modelBuilder.Entity("CouchLog.Data.SharedList", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(150)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("SharedLists");
});
modelBuilder.Entity("CouchLog.Data.SharedListEntity", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("TEXT");
b.Property<int?>("Episode")
.HasColumnType("INTEGER");
b.Property<bool>("Favorite")
.HasColumnType("INTEGER");
b.Property<int>("GlobalEntityId")
.HasColumnType("INTEGER");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<int?>("Season")
.HasColumnType("INTEGER");
b.Property<int>("SharedListId")
.HasColumnType("INTEGER");
b.Property<int?>("SharedWatchStatusId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("GlobalEntityId");
b.HasIndex("SharedListId");
b.HasIndex("SharedWatchStatusId");
b.ToTable("SharedListEntities");
});
modelBuilder.Entity("CouchLog.Data.SharedListLabel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<int>("SharedListId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("SharedListId");
b.ToTable("SharedListLabels");
});
modelBuilder.Entity("CouchLog.Data.SharedWatchStatus", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ColorCode")
.HasMaxLength(7)
.HasColumnType("TEXT");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<int>("SharedListId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("SharedListId");
b.ToTable("SharedWatchStatuses");
});
modelBuilder.Entity("CouchLog.Data.StreamingPlatform", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("TEXT");
b.Property<string>("PicturePath")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("StreamingPlatforms");
});
modelBuilder.Entity("CouchLog.Data.UserWatchStatus", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ColorCode")
.HasMaxLength(7)
.HasColumnType("TEXT");
b.Property<DateTime>("CreationTime")
.HasColumnType("TEXT");
b.Property<string>("Description")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime?>("LastChange")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("UserWatchStatuses");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("CouchLog.Data.GlobalEntity", b =>
{
b.HasOne("CouchLog.Data.ApplicationUser", "User")
.WithMany("CreatedGlobalEntities")
.HasForeignKey("CreatorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.MediaType", "MediaType")
.WithMany("GlobalEntities")
.HasForeignKey("TypeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("MediaType");
b.Navigation("User");
});
modelBuilder.Entity("CouchLog.Data.Label", b =>
{
b.HasOne("CouchLog.Data.ApplicationUser", "User")
.WithMany("CreatedLabels")
.HasForeignKey("CreatorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("CouchLog.Data.LinkTableGlobalGenre", b =>
{
b.HasOne("CouchLog.Data.Genre", "Genre")
.WithMany("LinkTableGlobalGenres")
.HasForeignKey("GenreId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.GlobalEntity", "GlobalEntity")
.WithMany("LinkTableGlobalGenres")
.HasForeignKey("GlobalEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Genre");
b.Navigation("GlobalEntity");
});
modelBuilder.Entity("CouchLog.Data.LinkTablePrivateLabel", b =>
{
b.HasOne("CouchLog.Data.Label", "Label")
.WithMany("LinkTablePrivateLabels")
.HasForeignKey("LabelId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.PrivateEntity", "PrivateEntity")
.WithMany("PrivateEntityLabels")
.HasForeignKey("PrivateEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Label");
b.Navigation("PrivateEntity");
});
modelBuilder.Entity("CouchLog.Data.LinkTablePrivateStreamingPlatform", b =>
{
b.HasOne("CouchLog.Data.PrivateEntity", "PrivateEntity")
.WithMany("PrivateStreamingPlatforms")
.HasForeignKey("PrivateEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.SharedListEntity", null)
.WithMany("LinkTablePrivateStreamingPlatforms")
.HasForeignKey("SharedListEntityId");
b.HasOne("CouchLog.Data.StreamingPlatform", "StreamingPlatform")
.WithMany()
.HasForeignKey("StreamingPlatformId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("PrivateEntity");
b.Navigation("StreamingPlatform");
});
modelBuilder.Entity("CouchLog.Data.LinkTableSharedLabel", b =>
{
b.HasOne("CouchLog.Data.SharedListEntity", "SharedListEntity")
.WithMany("LinkTableSharedLabels")
.HasForeignKey("SharedListEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.SharedListLabel", "SharedListLabel")
.WithMany("LinkTableSharedLabels")
.HasForeignKey("SharedListLabelId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SharedListEntity");
b.Navigation("SharedListLabel");
});
modelBuilder.Entity("CouchLog.Data.LinkTableSharedUser", b =>
{
b.HasOne("CouchLog.Data.SharedList", "SharedList")
.WithMany("SharedListUsers")
.HasForeignKey("SharedListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.ApplicationUser", "User")
.WithMany("LinkTableSharedUsers")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SharedList");
b.Navigation("User");
});
modelBuilder.Entity("CouchLog.Data.PrivateEntity", b =>
{
b.HasOne("CouchLog.Data.GlobalEntity", "GlobalEntity")
.WithMany("PrivateEntities")
.HasForeignKey("GlobalEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.ApplicationUser", "User")
.WithMany("PrivateEntities")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.UserWatchStatus", "UserWatchStatus")
.WithMany()
.HasForeignKey("UserWatchStatusId");
b.Navigation("GlobalEntity");
b.Navigation("User");
b.Navigation("UserWatchStatus");
});
modelBuilder.Entity("CouchLog.Data.SharedListEntity", b =>
{
b.HasOne("CouchLog.Data.GlobalEntity", "GlobalEntity")
.WithMany("SharedListEntities")
.HasForeignKey("GlobalEntityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.SharedList", "SharedList")
.WithMany("SharedListEntities")
.HasForeignKey("SharedListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.SharedWatchStatus", "SharedWatchStatus")
.WithMany("SharedListEntities")
.HasForeignKey("SharedWatchStatusId");
b.Navigation("GlobalEntity");
b.Navigation("SharedList");
b.Navigation("SharedWatchStatus");
});
modelBuilder.Entity("CouchLog.Data.SharedListLabel", b =>
{
b.HasOne("CouchLog.Data.SharedList", "SharedList")
.WithMany("SharedListLabels")
.HasForeignKey("SharedListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SharedList");
});
modelBuilder.Entity("CouchLog.Data.SharedWatchStatus", b =>
{
b.HasOne("CouchLog.Data.SharedList", "SharedList")
.WithMany("SharedWatchStatuses")
.HasForeignKey("SharedListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("SharedList");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("CouchLog.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("CouchLog.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("CouchLog.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("CouchLog.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("CouchLog.Data.ApplicationUser", b =>
{
b.Navigation("CreatedGlobalEntities");
b.Navigation("CreatedLabels");
b.Navigation("LinkTableSharedUsers");
b.Navigation("PrivateEntities");
});
modelBuilder.Entity("CouchLog.Data.Genre", b =>
{
b.Navigation("LinkTableGlobalGenres");
});
modelBuilder.Entity("CouchLog.Data.GlobalEntity", b =>
{
b.Navigation("LinkTableGlobalGenres");
b.Navigation("PrivateEntities");
b.Navigation("SharedListEntities");
});
modelBuilder.Entity("CouchLog.Data.Label", b =>
{
b.Navigation("LinkTablePrivateLabels");
});
modelBuilder.Entity("CouchLog.Data.MediaType", b =>
{
b.Navigation("GlobalEntities");
});
modelBuilder.Entity("CouchLog.Data.PrivateEntity", b =>
{
b.Navigation("PrivateEntityLabels");
b.Navigation("PrivateStreamingPlatforms");
});
modelBuilder.Entity("CouchLog.Data.SharedList", b =>
{
b.Navigation("SharedListEntities");
b.Navigation("SharedListLabels");
b.Navigation("SharedListUsers");
b.Navigation("SharedWatchStatuses");
});
modelBuilder.Entity("CouchLog.Data.SharedListEntity", b =>
{
b.Navigation("LinkTablePrivateStreamingPlatforms");
b.Navigation("LinkTableSharedLabels");
});
modelBuilder.Entity("CouchLog.Data.SharedListLabel", b =>
{
b.Navigation("LinkTableSharedLabels");
});
modelBuilder.Entity("CouchLog.Data.SharedWatchStatus", b =>
{
b.Navigation("SharedListEntities");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace CouchLog.Migrations
{
/// <inheritdoc />
public partial class FixAccountsSettingsKey : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
@@ -17,6 +17,20 @@ namespace CouchLog.Migrations
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "10.0.1");
modelBuilder.Entity("CouchLog.Data.AccountsSettings", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<bool>("IsRegistrationAllowed")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("AccountsSettings");
});
modelBuilder.Entity("CouchLog.Data.ApplicationUser", b =>
{
b.Property<string>("Id")
@@ -81,6 +95,23 @@ namespace CouchLog.Migrations
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("CouchLog.Data.CouchLogState", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime?>("InitializationDate")
.HasColumnType("TEXT");
b.Property<bool>("IsInitialized")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.ToTable("CouchLogState");
});
modelBuilder.Entity("CouchLog.Data.Genre", b =>
{
b.Property<int>("Id")
+74 -27
View File
@@ -1,23 +1,52 @@
using CouchLog.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
namespace CouchLog
{
public class OnStartUp
public class OnStartUp(ApplicationDbContext CouchLogDB, RoleManager<IdentityRole> RoleManager)
{
private ApplicationDbContext CouchLogDB;
private readonly ApplicationDbContext CouchLogDB = CouchLogDB;
private readonly RoleManager<IdentityRole> RoleManager = RoleManager;
public OnStartUp(ApplicationDbContext CouchLogDB)
public async Task CreateRoles()
{
this.CouchLogDB = CouchLogDB;
string[] roleNames = ["Admin", "User"];
IdentityResult roleResult;
foreach (var roleName in roleNames)
{
var roleExist = await RoleManager.RoleExistsAsync(roleName);
if (!roleExist)
{
roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
}
}
}
public void AddBasicDatabaseEntries()
public async Task CreateStanardSettings()
{
if(!CouchLogDB.AccountsSettings.Any())
{
AccountsSettings accountsSettings = new()
{
Id = 1,
IsRegistrationAllowed = true,
};
await CouchLogDB.AccountsSettings.AddAsync(accountsSettings);
await CouchLogDB.SaveChangesAsync();
}
}
public async Task AddBasicDatabaseEntries()
{
//##################
//### MediaTypes ###
//##################
List<MediaType> MediaTypes = new List<MediaType>
{
List<MediaType> MediaTypes =
[
//Video based
new() { Name="Movie" },
new() { Name="Series" },
@@ -38,23 +67,24 @@ namespace CouchLog
//Text based
new() { Name = "Book" },
new() { Name = "Comic" },
};
];
foreach (MediaType MediaType in MediaTypes)
{
if(!CouchLogDB.MediaType.Any(m => m.Name == MediaType.Name))
if(!await CouchLogDB.MediaType.AnyAsync(m => m.Name == MediaType.Name))
{
CouchLogDB.MediaType.Add(MediaType);
await CouchLogDB.MediaType.AddAsync(MediaType);
}
}
//##############
//### Genres ###
//##############
List<Genre> Genres = new List<Genre>
{
List<Genre> Genres =
[
new() { Name = "Action", CreationTime = DateTime.Now },
new() { Name = "Animation", CreationTime = DateTime.Now },
new() { Name = "Adventure", CreationTime = DateTime.Now },
new() { Name = "Comedy", CreationTime = DateTime.Now },
new() { Name = "Crime", CreationTime = DateTime.Now },
new() { Name = "Drama", CreationTime = DateTime.Now },
@@ -69,13 +99,13 @@ namespace CouchLog
new() { Name = "Thriller", CreationTime = DateTime.Now },
new() { Name = "War", CreationTime = DateTime.Now },
new() { Name = "Western", CreationTime = DateTime.Now },
};
];
foreach(Genre Genre in Genres)
{
if(!CouchLogDB.Genres.Any(m => m.Name == Genre.Name))
if(! await CouchLogDB.Genres.AnyAsync(m => m.Name == Genre.Name))
{
CouchLogDB.Genres.Add(Genre);
await CouchLogDB.Genres.AddAsync(Genre);
}
}
@@ -83,8 +113,8 @@ namespace CouchLog
//##########################
//### StreamingPlatforms ###
//##########################
List<StreamingPlatform> StreamingPlatforms = new List<StreamingPlatform>
{
List<StreamingPlatform> StreamingPlatforms =
[
new() { Name = "Netflix", PicturePath="StreamingPlatforms/Netflix.png", CreationTime = DateTime.Now },
new() { Name = "Prime Video", PicturePath="StreamingPlatforms/Prime-Video.png", CreationTime = DateTime.Now },
new() { Name = "Disney+", PicturePath="StreamingPlatforms/Disney+.png", CreationTime = DateTime.Now },
@@ -92,38 +122,55 @@ namespace CouchLog
new() { Name = "WOW TV", PicturePath="StreamingPlatforms/WOWTV.png", CreationTime = DateTime.Now },
new() { Name = "Paramount+", PicturePath="StreamingPlatforms/Paramount+.png", CreationTime = DateTime.Now },
new() { Name = "Joyn", PicturePath="StreamingPlatforms/Joyn.png", CreationTime = DateTime.Now },
};
];
foreach(StreamingPlatform StreamingPlatform in StreamingPlatforms)
{
if(!CouchLogDB.StreamingPlatforms.Any(m => m.Name == StreamingPlatform.Name))
if(! await CouchLogDB.StreamingPlatforms.AnyAsync(m => m.Name == StreamingPlatform.Name))
{
CouchLogDB.StreamingPlatforms.Add(StreamingPlatform);
await CouchLogDB.StreamingPlatforms.AddAsync(StreamingPlatform);
}
}
//##########################
//###### WatchStates #######
//##########################
List<UserWatchStatus> UserWatchStatuses = new List<UserWatchStatus>
{
List<UserWatchStatus> UserWatchStatuses =
[
new() { Name = "Not watched", CreationTime = DateTime.Now },
new() { Name = "Started", CreationTime = DateTime.Now },
new() { Name = "Watching", CreationTime = DateTime.Now },
new() { Name = "Finished", CreationTime = DateTime.Now },
new() { Name = "Paused", CreationTime = DateTime.Now },
new() { Name = "Aborted", CreationTime= DateTime.Now },
};
];
foreach(UserWatchStatus UserWatchStatus in UserWatchStatuses)
{
if(!CouchLogDB.UserWatchStatuses.Any(m => m.Name == UserWatchStatus.Name))
if(! await CouchLogDB.UserWatchStatuses.AnyAsync(m => m.Name == UserWatchStatus.Name))
{
CouchLogDB.UserWatchStatuses.Add(UserWatchStatus);
await CouchLogDB.UserWatchStatuses.AddAsync(UserWatchStatus);
}
}
//#################################################
//###### Old-Things that need to be deleted #######
//#################################################
List<UserWatchStatus> oldUserWatchStatuses =
[
new() { Name = "Started" , CreationTime = DateTime.Now },
];
CouchLogDB.SaveChanges();
foreach(UserWatchStatus oldUserWatchStatus in oldUserWatchStatuses)
{
UserWatchStatus? toDeletedUserWatchStatus = await CouchLogDB.UserWatchStatuses.FirstOrDefaultAsync(m => m.Name == oldUserWatchStatus.Name);
if(toDeletedUserWatchStatus != null)
{
CouchLogDB.UserWatchStatuses.Remove(toDeletedUserWatchStatus);
}
}
await CouchLogDB.SaveChangesAsync();
}
}
}
+11 -84
View File
@@ -5,6 +5,8 @@ using CouchLog.Components;
using CouchLog.Components.Account;
using CouchLog.Data;
using CouchLog;
using MudBlazor;
using MudBlazor.Services;
var builder = WebApplication.CreateBuilder(args);
@@ -35,6 +37,10 @@ builder.Services.AddIdentityCore<ApplicationUser>(options => options.SignIn.Requ
.AddDefaultTokenProviders();
builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
builder.Services.AddMudServices(config =>
{
config.SnackbarConfiguration.PositionClass = Defaults.Classes.Position.BottomCenter;
});
var app = builder.Build();
@@ -65,91 +71,12 @@ using (var scope = app.Services.CreateScope())
var CouchLogDB = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
CouchLogDB.Database.Migrate();
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
var RoleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
string[] roleNames = { "Admin", "User" };
IdentityResult roleResult;
foreach (var roleName in roleNames)
{
var roleExist = await roleManager.RoleExistsAsync(roleName);
if (!roleExist)
{
roleResult = await roleManager.CreateAsync(new IdentityRole(roleName));
}
}
var adminUsername = "Admin";
var normalUsername = "User";
var adminPassword = "EinSehrSicheresPasswort123!";
var adminUser = await userManager.FindByNameAsync(adminUsername);
var normalUser = await userManager.FindByNameAsync(normalUsername);
if (adminUser == null)
{
adminUser = new ApplicationUser
{
UserName = adminUsername,
EmailConfirmed = true
};
var createResult = await userManager.CreateAsync(adminUser, adminPassword);
if (createResult.Succeeded)
{
await userManager.AddToRoleAsync(adminUser, "Admin");
}
}
else if (normalUser == null)
{
normalUser = new ApplicationUser
{
UserName = normalUsername,
EmailConfirmed = true
};
var createResult = await userManager.CreateAsync(normalUser, adminPassword);
if (createResult.Succeeded)
{
await userManager.AddToRoleAsync(adminUser, "User");
}
}
else if (!await userManager.IsInRoleAsync(normalUser, "User"))
{
await userManager.AddToRoleAsync(normalUser, "User");
}
else if (!await userManager.IsInRoleAsync(adminUser, "Admin"))
{
await userManager.AddToRoleAsync(adminUser, "Admin");
}
MediaType mediaType = new()
{
Name = "Movie"
};
if (!CouchLogDB.MediaType.Any())
CouchLogDB.MediaType.Add(mediaType);
await CouchLogDB.SaveChangesAsync();
GlobalEntity globalEntity = new()
{
Title = "Inception",
CreationTime = DateTime.Now,
CreatorId = (await userManager.FindByNameAsync("Admin"))!.Id,
TypeId = 1,
PicturePath = "Pictures/Inception.jpg"
};
if (!CouchLogDB.GlobalEntities.Any())
CouchLogDB.GlobalEntities.Add(globalEntity);
await CouchLogDB.SaveChangesAsync();
OnStartUp onStartUp = new(CouchLogDB);
onStartUp.AddBasicDatabaseEntries();
OnStartUp onStartUp = new(CouchLogDB, RoleManager);
await onStartUp.CreateRoles();
await onStartUp.AddBasicDatabaseEntries();
await onStartUp.CreateStanardSettings();
}
app.Run();
+7
View File
@@ -6,6 +6,13 @@
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
},
"Console": {
"FormatterName": "simple",
"FormatterOptions": {
"TimestampFormat": "[HH:mm:ss]",
"SingleLine": true
}
}
},
"AllowedHosts": "*"
-62
View File
@@ -1,62 +0,0 @@
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
background: #2d2d2d;
color: white
}
a, .btn-link {
color: #006bb7;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}
.content {
padding-top: 1.1rem;
}
h1:focus {
outline: none;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
.invalid {
outline: 1px solid #e50000;
}
.validation-message {
color: #e50000;
}
.blazor-error-boundary {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
padding: 1rem 1rem 1rem 3.7rem;
color: white;
}
.blazor-error-boundary::after {
content: "An error has occurred."
}
.darker-border-checkbox.form-check-input {
border-color: #929292;
}
.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
color: var(--bs-secondary-color);
text-align: end;
}
.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
text-align: start;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,597 +0,0 @@
/*!
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
:root,
[data-bs-theme=light] {
--bs-blue: #0d6efd;
--bs-indigo: #6610f2;
--bs-purple: #6f42c1;
--bs-pink: #d63384;
--bs-red: #dc3545;
--bs-orange: #fd7e14;
--bs-yellow: #ffc107;
--bs-green: #198754;
--bs-teal: #20c997;
--bs-cyan: #0dcaf0;
--bs-black: #000;
--bs-white: #fff;
--bs-gray: #6c757d;
--bs-gray-dark: #343a40;
--bs-gray-100: #f8f9fa;
--bs-gray-200: #e9ecef;
--bs-gray-300: #dee2e6;
--bs-gray-400: #ced4da;
--bs-gray-500: #adb5bd;
--bs-gray-600: #6c757d;
--bs-gray-700: #495057;
--bs-gray-800: #343a40;
--bs-gray-900: #212529;
--bs-primary: #0d6efd;
--bs-secondary: #6c757d;
--bs-success: #198754;
--bs-info: #0dcaf0;
--bs-warning: #ffc107;
--bs-danger: #dc3545;
--bs-light: #f8f9fa;
--bs-dark: #212529;
--bs-primary-rgb: 13, 110, 253;
--bs-secondary-rgb: 108, 117, 125;
--bs-success-rgb: 25, 135, 84;
--bs-info-rgb: 13, 202, 240;
--bs-warning-rgb: 255, 193, 7;
--bs-danger-rgb: 220, 53, 69;
--bs-light-rgb: 248, 249, 250;
--bs-dark-rgb: 33, 37, 41;
--bs-primary-text-emphasis: #052c65;
--bs-secondary-text-emphasis: #2b2f32;
--bs-success-text-emphasis: #0a3622;
--bs-info-text-emphasis: #055160;
--bs-warning-text-emphasis: #664d03;
--bs-danger-text-emphasis: #58151c;
--bs-light-text-emphasis: #495057;
--bs-dark-text-emphasis: #495057;
--bs-primary-bg-subtle: #cfe2ff;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-success-bg-subtle: #d1e7dd;
--bs-info-bg-subtle: #cff4fc;
--bs-warning-bg-subtle: #fff3cd;
--bs-danger-bg-subtle: #f8d7da;
--bs-light-bg-subtle: #fcfcfd;
--bs-dark-bg-subtle: #ced4da;
--bs-primary-border-subtle: #9ec5fe;
--bs-secondary-border-subtle: #c4c8cb;
--bs-success-border-subtle: #a3cfbb;
--bs-info-border-subtle: #9eeaf9;
--bs-warning-border-subtle: #ffe69c;
--bs-danger-border-subtle: #f1aeb5;
--bs-light-border-subtle: #e9ecef;
--bs-dark-border-subtle: #adb5bd;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-body-font-family: var(--bs-font-sans-serif);
--bs-body-font-size: 1rem;
--bs-body-font-weight: 400;
--bs-body-line-height: 1.5;
--bs-body-color: #212529;
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg: #fff;
--bs-body-bg-rgb: 255, 255, 255;
--bs-emphasis-color: #000;
--bs-emphasis-color-rgb: 0, 0, 0;
--bs-secondary-color: rgba(33, 37, 41, 0.75);
--bs-secondary-color-rgb: 33, 37, 41;
--bs-secondary-bg: #e9ecef;
--bs-secondary-bg-rgb: 233, 236, 239;
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
--bs-tertiary-color-rgb: 33, 37, 41;
--bs-tertiary-bg: #f8f9fa;
--bs-tertiary-bg-rgb: 248, 249, 250;
--bs-heading-color: inherit;
--bs-link-color: #0d6efd;
--bs-link-color-rgb: 13, 110, 253;
--bs-link-decoration: underline;
--bs-link-hover-color: #0a58ca;
--bs-link-hover-color-rgb: 10, 88, 202;
--bs-code-color: #d63384;
--bs-highlight-color: #212529;
--bs-highlight-bg: #fff3cd;
--bs-border-width: 1px;
--bs-border-style: solid;
--bs-border-color: #dee2e6;
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
--bs-border-radius: 0.375rem;
--bs-border-radius-sm: 0.25rem;
--bs-border-radius-lg: 0.5rem;
--bs-border-radius-xl: 1rem;
--bs-border-radius-xxl: 2rem;
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
--bs-border-radius-pill: 50rem;
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
--bs-focus-ring-width: 0.25rem;
--bs-focus-ring-opacity: 0.25;
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
--bs-form-valid-color: #198754;
--bs-form-valid-border-color: #198754;
--bs-form-invalid-color: #dc3545;
--bs-form-invalid-border-color: #dc3545;
}
[data-bs-theme=dark] {
color-scheme: dark;
--bs-body-color: #dee2e6;
--bs-body-color-rgb: 222, 226, 230;
--bs-body-bg: #212529;
--bs-body-bg-rgb: 33, 37, 41;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-secondary-color: rgba(222, 226, 230, 0.75);
--bs-secondary-color-rgb: 222, 226, 230;
--bs-secondary-bg: #343a40;
--bs-secondary-bg-rgb: 52, 58, 64;
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
--bs-tertiary-color-rgb: 222, 226, 230;
--bs-tertiary-bg: #2b3035;
--bs-tertiary-bg-rgb: 43, 48, 53;
--bs-primary-text-emphasis: #6ea8fe;
--bs-secondary-text-emphasis: #a7acb1;
--bs-success-text-emphasis: #75b798;
--bs-info-text-emphasis: #6edff6;
--bs-warning-text-emphasis: #ffda6a;
--bs-danger-text-emphasis: #ea868f;
--bs-light-text-emphasis: #f8f9fa;
--bs-dark-text-emphasis: #dee2e6;
--bs-primary-bg-subtle: #031633;
--bs-secondary-bg-subtle: #161719;
--bs-success-bg-subtle: #051b11;
--bs-info-bg-subtle: #032830;
--bs-warning-bg-subtle: #332701;
--bs-danger-bg-subtle: #2c0b0e;
--bs-light-bg-subtle: #343a40;
--bs-dark-bg-subtle: #1a1d20;
--bs-primary-border-subtle: #084298;
--bs-secondary-border-subtle: #41464b;
--bs-success-border-subtle: #0f5132;
--bs-info-border-subtle: #087990;
--bs-warning-border-subtle: #997404;
--bs-danger-border-subtle: #842029;
--bs-light-border-subtle: #495057;
--bs-dark-border-subtle: #343a40;
--bs-heading-color: inherit;
--bs-link-color: #6ea8fe;
--bs-link-hover-color: #8bb9fe;
--bs-link-color-rgb: 110, 168, 254;
--bs-link-hover-color-rgb: 139, 185, 254;
--bs-code-color: #e685b5;
--bs-highlight-color: #dee2e6;
--bs-highlight-bg: #664d03;
--bs-border-color: #495057;
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
--bs-form-valid-color: #75b798;
--bs-form-valid-border-color: #75b798;
--bs-form-invalid-color: #ea868f;
--bs-form-invalid-border-color: #ea868f;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
color: var(--bs-body-color);
text-align: var(--bs-body-text-align);
background-color: var(--bs-body-bg);
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
border: 0;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: var(--bs-heading-color);
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-left: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.1875em;
color: var(--bs-highlight-color);
background-color: var(--bs-highlight-bg);
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
text-decoration: underline;
}
a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: var(--bs-font-monospace);
font-size: 1em;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: var(--bs-code-color);
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.1875rem 0.375rem;
font-size: 0.875em;
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
border-radius: 0.25rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: var(--bs-secondary-color);
text-align: left;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
display: none !important;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: left;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: left;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
/* rtl:raw:
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
::file-selector-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,594 +0,0 @@
/*!
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
:root,
[data-bs-theme=light] {
--bs-blue: #0d6efd;
--bs-indigo: #6610f2;
--bs-purple: #6f42c1;
--bs-pink: #d63384;
--bs-red: #dc3545;
--bs-orange: #fd7e14;
--bs-yellow: #ffc107;
--bs-green: #198754;
--bs-teal: #20c997;
--bs-cyan: #0dcaf0;
--bs-black: #000;
--bs-white: #fff;
--bs-gray: #6c757d;
--bs-gray-dark: #343a40;
--bs-gray-100: #f8f9fa;
--bs-gray-200: #e9ecef;
--bs-gray-300: #dee2e6;
--bs-gray-400: #ced4da;
--bs-gray-500: #adb5bd;
--bs-gray-600: #6c757d;
--bs-gray-700: #495057;
--bs-gray-800: #343a40;
--bs-gray-900: #212529;
--bs-primary: #0d6efd;
--bs-secondary: #6c757d;
--bs-success: #198754;
--bs-info: #0dcaf0;
--bs-warning: #ffc107;
--bs-danger: #dc3545;
--bs-light: #f8f9fa;
--bs-dark: #212529;
--bs-primary-rgb: 13, 110, 253;
--bs-secondary-rgb: 108, 117, 125;
--bs-success-rgb: 25, 135, 84;
--bs-info-rgb: 13, 202, 240;
--bs-warning-rgb: 255, 193, 7;
--bs-danger-rgb: 220, 53, 69;
--bs-light-rgb: 248, 249, 250;
--bs-dark-rgb: 33, 37, 41;
--bs-primary-text-emphasis: #052c65;
--bs-secondary-text-emphasis: #2b2f32;
--bs-success-text-emphasis: #0a3622;
--bs-info-text-emphasis: #055160;
--bs-warning-text-emphasis: #664d03;
--bs-danger-text-emphasis: #58151c;
--bs-light-text-emphasis: #495057;
--bs-dark-text-emphasis: #495057;
--bs-primary-bg-subtle: #cfe2ff;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-success-bg-subtle: #d1e7dd;
--bs-info-bg-subtle: #cff4fc;
--bs-warning-bg-subtle: #fff3cd;
--bs-danger-bg-subtle: #f8d7da;
--bs-light-bg-subtle: #fcfcfd;
--bs-dark-bg-subtle: #ced4da;
--bs-primary-border-subtle: #9ec5fe;
--bs-secondary-border-subtle: #c4c8cb;
--bs-success-border-subtle: #a3cfbb;
--bs-info-border-subtle: #9eeaf9;
--bs-warning-border-subtle: #ffe69c;
--bs-danger-border-subtle: #f1aeb5;
--bs-light-border-subtle: #e9ecef;
--bs-dark-border-subtle: #adb5bd;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-body-font-family: var(--bs-font-sans-serif);
--bs-body-font-size: 1rem;
--bs-body-font-weight: 400;
--bs-body-line-height: 1.5;
--bs-body-color: #212529;
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg: #fff;
--bs-body-bg-rgb: 255, 255, 255;
--bs-emphasis-color: #000;
--bs-emphasis-color-rgb: 0, 0, 0;
--bs-secondary-color: rgba(33, 37, 41, 0.75);
--bs-secondary-color-rgb: 33, 37, 41;
--bs-secondary-bg: #e9ecef;
--bs-secondary-bg-rgb: 233, 236, 239;
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
--bs-tertiary-color-rgb: 33, 37, 41;
--bs-tertiary-bg: #f8f9fa;
--bs-tertiary-bg-rgb: 248, 249, 250;
--bs-heading-color: inherit;
--bs-link-color: #0d6efd;
--bs-link-color-rgb: 13, 110, 253;
--bs-link-decoration: underline;
--bs-link-hover-color: #0a58ca;
--bs-link-hover-color-rgb: 10, 88, 202;
--bs-code-color: #d63384;
--bs-highlight-color: #212529;
--bs-highlight-bg: #fff3cd;
--bs-border-width: 1px;
--bs-border-style: solid;
--bs-border-color: #dee2e6;
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
--bs-border-radius: 0.375rem;
--bs-border-radius-sm: 0.25rem;
--bs-border-radius-lg: 0.5rem;
--bs-border-radius-xl: 1rem;
--bs-border-radius-xxl: 2rem;
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
--bs-border-radius-pill: 50rem;
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
--bs-focus-ring-width: 0.25rem;
--bs-focus-ring-opacity: 0.25;
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
--bs-form-valid-color: #198754;
--bs-form-valid-border-color: #198754;
--bs-form-invalid-color: #dc3545;
--bs-form-invalid-border-color: #dc3545;
}
[data-bs-theme=dark] {
color-scheme: dark;
--bs-body-color: #dee2e6;
--bs-body-color-rgb: 222, 226, 230;
--bs-body-bg: #212529;
--bs-body-bg-rgb: 33, 37, 41;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-secondary-color: rgba(222, 226, 230, 0.75);
--bs-secondary-color-rgb: 222, 226, 230;
--bs-secondary-bg: #343a40;
--bs-secondary-bg-rgb: 52, 58, 64;
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
--bs-tertiary-color-rgb: 222, 226, 230;
--bs-tertiary-bg: #2b3035;
--bs-tertiary-bg-rgb: 43, 48, 53;
--bs-primary-text-emphasis: #6ea8fe;
--bs-secondary-text-emphasis: #a7acb1;
--bs-success-text-emphasis: #75b798;
--bs-info-text-emphasis: #6edff6;
--bs-warning-text-emphasis: #ffda6a;
--bs-danger-text-emphasis: #ea868f;
--bs-light-text-emphasis: #f8f9fa;
--bs-dark-text-emphasis: #dee2e6;
--bs-primary-bg-subtle: #031633;
--bs-secondary-bg-subtle: #161719;
--bs-success-bg-subtle: #051b11;
--bs-info-bg-subtle: #032830;
--bs-warning-bg-subtle: #332701;
--bs-danger-bg-subtle: #2c0b0e;
--bs-light-bg-subtle: #343a40;
--bs-dark-bg-subtle: #1a1d20;
--bs-primary-border-subtle: #084298;
--bs-secondary-border-subtle: #41464b;
--bs-success-border-subtle: #0f5132;
--bs-info-border-subtle: #087990;
--bs-warning-border-subtle: #997404;
--bs-danger-border-subtle: #842029;
--bs-light-border-subtle: #495057;
--bs-dark-border-subtle: #343a40;
--bs-heading-color: inherit;
--bs-link-color: #6ea8fe;
--bs-link-hover-color: #8bb9fe;
--bs-link-color-rgb: 110, 168, 254;
--bs-link-hover-color-rgb: 139, 185, 254;
--bs-code-color: #e685b5;
--bs-highlight-color: #dee2e6;
--bs-highlight-bg: #664d03;
--bs-border-color: #495057;
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
--bs-form-valid-color: #75b798;
--bs-form-valid-border-color: #75b798;
--bs-form-invalid-color: #ea868f;
--bs-form-invalid-border-color: #ea868f;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
color: var(--bs-body-color);
text-align: var(--bs-body-text-align);
background-color: var(--bs-body-bg);
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
border: 0;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: var(--bs-heading-color);
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-right: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-right: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.1875em;
color: var(--bs-highlight-color);
background-color: var(--bs-highlight-bg);
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
text-decoration: underline;
}
a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: var(--bs-font-monospace);
font-size: 1em;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: var(--bs-code-color);
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.1875rem 0.375rem;
font-size: 0.875em;
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
border-radius: 0.25rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: var(--bs-secondary-color);
text-align: right;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
display: none !important;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: right;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: right;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
::file-selector-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-8
View File
@@ -1,8 +0,0 @@
window.initializeBootstrapCollapse = () => {
var collapseElementList = [].slice.call(document.querySelectorAll('[data-bs-toggle="collapse"]'))
var collapseList = collapseElementList.map(function (collapseEl) {
return new bootstrap.Collapse(document.querySelector(collapseEl.dataset.bsTarget), {
toggle: false
})
})
};