This commit is contained in:
younes elhaddoury
2025-09-17 10:28:02 +02:00
parent 9c8fb9b205
commit bb13759af4
288 changed files with 102393 additions and 0 deletions
@@ -0,0 +1,138 @@
using System.Security.Claims;
using LEA.Models;
using LEA.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace LEA.Controllers;
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly ILogger<AccountController> _logger;
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
ILogger<AccountController> logger)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
}
[HttpGet]
[AllowAnonymous]
public IActionResult Register()
{
return View(new RegisterViewModel());
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var existingUser = await _userManager.FindByEmailAsync(model.Email);
if (existingUser != null)
{
ModelState.AddModelError(nameof(model.Email), "Diese E-Mail-Adresse wird bereits verwendet.");
return View(model);
}
var user = new ApplicationUser
{
FullName = model.FullName.Trim(),
Email = model.Email.Trim(),
UserName = model.Email.Trim(),
CreatedAt = DateTime.UtcNow
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _userManager.AddClaimAsync(user, new Claim("FullName", user.FullName));
await _signInManager.SignInAsync(user, isPersistent: true);
_logger.LogInformation("Neuer Benutzer {Email} wurde erstellt und angemeldet.", user.Email);
TempData["Success"] = "Registrierung erfolgreich. Willkommen zurück!";
return RedirectToAction("Index", "Applications");
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, TranslateIdentityError(error));
}
return View(model);
}
[HttpGet]
[AllowAnonymous]
public IActionResult Login(string? returnUrl = null)
{
return View(new LoginViewModel { ReturnUrl = returnUrl });
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null)
{
ModelState.AddModelError(string.Empty, "Ungültige Anmeldedaten.");
return View(model);
}
var result = await _signInManager.PasswordSignInAsync(user, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
_logger.LogInformation("Benutzer {Email} hat sich angemeldet.", user.Email);
if (!string.IsNullOrWhiteSpace(model.ReturnUrl) && Url.IsLocalUrl(model.ReturnUrl))
{
return Redirect(model.ReturnUrl);
}
TempData["Success"] = "Erfolgreich angemeldet.";
return RedirectToAction("Index", "Applications");
}
ModelState.AddModelError(string.Empty, "Ungültige Anmeldedaten.");
return View(model);
}
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
await _signInManager.SignOutAsync();
TempData["Success"] = "Sie wurden abgemeldet.";
return RedirectToAction("Index", "Home");
}
private static string TranslateIdentityError(IdentityError error) => error.Code switch
{
"PasswordTooShort" => "Das Passwort ist zu kurz.",
"PasswordRequiresNonAlphanumeric" => "Das Passwort muss mindestens ein Sonderzeichen enthalten.",
"PasswordRequiresDigit" => "Das Passwort muss mindestens eine Zahl enthalten.",
"PasswordRequiresUpper" => "Das Passwort muss mindestens einen Großbuchstaben enthalten.",
"PasswordRequiresLower" => "Das Passwort muss mindestens einen Kleinbuchstaben enthalten.",
"DuplicateEmail" or "DuplicateUserName" => "Diese E-Mail-Adresse wird bereits verwendet.",
"InvalidEmail" => "Bitte eine gültige E-Mail-Adresse eingeben.",
_ => error.Description
};
}
@@ -0,0 +1,289 @@
using LEA.Data;
using LEA.Extensions;
using LEA.Models;
using LEA.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
namespace LEA.Controllers;
[Authorize]
public class ApplicationsController : Controller
{
private readonly AppDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
public ApplicationsController(AppDbContext context, UserManager<ApplicationUser> userManager)
{
_context = context;
_userManager = userManager;
}
public async Task<IActionResult> Index(string? searchTerm, ApplicationStatus? status)
{
var userId = GetCurrentUserId();
var query = _context.Applications
.Include(a => a.Contact)
.Where(a => a.UserId == userId);
if (!string.IsNullOrWhiteSpace(searchTerm))
{
var term = $"%{searchTerm.Trim()}%";
query = query.Where(a =>
EF.Functions.Like(a.Company, term) ||
EF.Functions.Like(a.Role, term) ||
EF.Functions.Like(a.Source ?? string.Empty, term) ||
EF.Functions.Like(a.Notes ?? string.Empty, term) ||
(a.Contact != null && (
EF.Functions.Like(a.Contact.FullName ?? string.Empty, term) ||
EF.Functions.Like(a.Contact.Email ?? string.Empty, term))));
}
if (status.HasValue)
{
query = query.Where(a => a.Status == status.Value);
}
var applications = await query
.OrderByDescending(a => a.UpdatedAt)
.ThenByDescending(a => a.AppliedOn)
.ToListAsync();
var statsQuery = _context.Applications.Where(a => a.UserId == userId);
var statusCounts = await statsQuery
.GroupBy(a => a.Status)
.Select(g => new { g.Key, Count = g.Count() })
.ToListAsync();
var statusDictionary = Enum.GetValues<ApplicationStatus>()
.ToDictionary(s => s, s => statusCounts.FirstOrDefault(sc => sc.Key == s)?.Count ?? 0);
var model = new ApplicationListViewModel
{
Applications = applications,
SearchTerm = searchTerm,
StatusFilter = status,
StatusOptions = GetStatusSelectList(status, includeAllOption: true),
StatusCounts = statusDictionary,
TotalApplications = await statsQuery.CountAsync()
};
return View(model);
}
[HttpGet]
public IActionResult Create()
{
var model = new ApplicationFormViewModel
{
AppliedOn = DateTime.Today,
Status = ApplicationStatus.Applied,
StatusOptions = GetStatusSelectList(ApplicationStatus.Applied),
Heading = "Bewerbung hinzufügen",
SubmitText = "Speichern"
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(ApplicationFormViewModel model)
{
if (!ModelState.IsValid)
{
model.StatusOptions = GetStatusSelectList(model.Status);
model.Heading = "Bewerbung hinzufügen";
model.SubmitText = "Speichern";
return View(model);
}
var application = new Application
{
Role = model.Role.Trim(),
Company = model.Company.Trim(),
Source = string.IsNullOrWhiteSpace(model.Source) ? null : model.Source.Trim(),
Status = model.Status,
AppliedOn = model.AppliedOn,
Notes = string.IsNullOrWhiteSpace(model.Notes) ? null : model.Notes.Trim(),
UserId = GetCurrentUserId(),
UpdatedAt = DateTime.UtcNow
};
if (HasContactInformation(model))
{
application.Contact = new Contact
{
FullName = string.IsNullOrWhiteSpace(model.ContactName) ? null : model.ContactName.Trim(),
Email = string.IsNullOrWhiteSpace(model.ContactEmail) ? null : model.ContactEmail.Trim(),
Phone = string.IsNullOrWhiteSpace(model.ContactPhone) ? null : model.ContactPhone.Trim()
};
}
_context.Applications.Add(application);
await _context.SaveChangesAsync();
TempData["Success"] = "Die Bewerbung wurde gespeichert.";
return RedirectToAction(nameof(Index));
}
[HttpGet]
public async Task<IActionResult> Edit(int id)
{
var application = await FindApplicationForCurrentUserAsync(id);
if (application == null)
{
return NotFound();
}
var model = new ApplicationFormViewModel
{
Id = application.Id,
Role = application.Role,
Company = application.Company,
Source = application.Source,
Status = application.Status,
AppliedOn = application.AppliedOn,
Notes = application.Notes,
ContactName = application.Contact?.FullName,
ContactEmail = application.Contact?.Email,
ContactPhone = application.Contact?.Phone,
StatusOptions = GetStatusSelectList(application.Status),
Heading = "Bewerbung bearbeiten",
SubmitText = "Änderungen speichern"
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, ApplicationFormViewModel model)
{
if (id != model.Id)
{
return NotFound();
}
if (!ModelState.IsValid)
{
model.StatusOptions = GetStatusSelectList(model.Status);
model.Heading = "Bewerbung bearbeiten";
model.SubmitText = "Änderungen speichern";
return View(model);
}
var application = await FindApplicationForCurrentUserAsync(id);
if (application == null)
{
return NotFound();
}
application.Role = model.Role.Trim();
application.Company = model.Company.Trim();
application.Source = string.IsNullOrWhiteSpace(model.Source) ? null : model.Source.Trim();
application.Status = model.Status;
application.AppliedOn = model.AppliedOn;
application.Notes = string.IsNullOrWhiteSpace(model.Notes) ? null : model.Notes.Trim();
application.UpdatedAt = DateTime.UtcNow;
if (HasContactInformation(model))
{
if (application.Contact == null)
{
application.Contact = new Contact();
}
application.Contact.FullName = string.IsNullOrWhiteSpace(model.ContactName) ? null : model.ContactName.Trim();
application.Contact.Email = string.IsNullOrWhiteSpace(model.ContactEmail) ? null : model.ContactEmail.Trim();
application.Contact.Phone = string.IsNullOrWhiteSpace(model.ContactPhone) ? null : model.ContactPhone.Trim();
}
else if (application.Contact != null)
{
_context.Contacts.Remove(application.Contact);
}
await _context.SaveChangesAsync();
TempData["Success"] = "Die Bewerbung wurde aktualisiert.";
return RedirectToAction(nameof(Index));
}
[HttpGet]
public async Task<IActionResult> Details(int id)
{
var application = await FindApplicationForCurrentUserAsync(id);
if (application == null)
{
return NotFound();
}
return View(application);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Delete(int id)
{
var application = await FindApplicationForCurrentUserAsync(id);
if (application == null)
{
return NotFound();
}
_context.Applications.Remove(application);
await _context.SaveChangesAsync();
TempData["Success"] = "Die Bewerbung wurde gelöscht.";
return RedirectToAction(nameof(Index));
}
private async Task<Application?> FindApplicationForCurrentUserAsync(int id)
{
var userId = GetCurrentUserId();
return await _context.Applications
.Include(a => a.Contact)
.Where(a => a.UserId == userId)
.FirstOrDefaultAsync(a => a.Id == id);
}
private IEnumerable<SelectListItem> GetStatusSelectList(ApplicationStatus? selectedStatus, bool includeAllOption = false)
{
var options = Enum.GetValues<ApplicationStatus>()
.Select(status => new SelectListItem
{
Text = status.GetDisplayName(),
Value = status.ToString(),
Selected = selectedStatus.HasValue && selectedStatus.Value == status
})
.ToList();
if (includeAllOption)
{
options.Insert(0, new SelectListItem
{
Text = "Alle Status",
Value = string.Empty,
Selected = !selectedStatus.HasValue
});
}
return options;
}
private bool HasContactInformation(ApplicationFormViewModel model)
{
return !string.IsNullOrWhiteSpace(model.ContactName)
|| !string.IsNullOrWhiteSpace(model.ContactEmail)
|| !string.IsNullOrWhiteSpace(model.ContactPhone);
}
private string GetCurrentUserId()
{
return _userManager.GetUserId(User)
?? throw new InvalidOperationException("Benutzer ist nicht angemeldet.");
}
}
@@ -0,0 +1,97 @@
using System.Diagnostics;
using System.Globalization;
using LEA.Data;
using LEA.Models;
using LEA.ViewModels;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LEA.Controllers;
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly AppDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
public HomeController(
ILogger<HomeController> logger,
AppDbContext context,
UserManager<ApplicationUser> userManager)
{
_logger = logger;
_context = context;
_userManager = userManager;
}
public async Task<IActionResult> Index()
{
var viewModel = new DashboardViewModel();
if (User.Identity?.IsAuthenticated ?? false)
{
var userId = _userManager.GetUserId(User);
var user = await _userManager.GetUserAsync(User);
var applications = await _context.Applications
.Include(a => a.Contact)
.Where(a => a.UserId == userId)
.OrderByDescending(a => a.UpdatedAt)
.ThenByDescending(a => a.AppliedOn)
.Take(5)
.ToListAsync();
var statusCounts = await _context.Applications
.Where(a => a.UserId == userId)
.GroupBy(a => a.Status)
.Select(group => new { group.Key, Count = group.Count() })
.ToListAsync();
var startOfCurrentMonth = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
var startDate = startOfCurrentMonth.AddMonths(-5);
var monthlyResults = await _context.Applications
.Where(a => a.UserId == userId && a.AppliedOn >= startDate)
.GroupBy(a => new { a.AppliedOn.Year, a.AppliedOn.Month })
.Select(group => new { group.Key.Year, group.Key.Month, Count = group.Count() })
.ToListAsync();
var germanCulture = CultureInfo.GetCultureInfo("de-DE");
var monthlyStatistics = new List<MonthlyApplicationStat>();
for (var offset = 0; offset < 6; offset++)
{
var currentMonth = startDate.AddMonths(offset);
var match = monthlyResults.FirstOrDefault(result =>
result.Year == currentMonth.Year && result.Month == currentMonth.Month);
var label = currentMonth.ToString("MMM yyyy", germanCulture);
monthlyStatistics.Add(new MonthlyApplicationStat(label, match?.Count ?? 0));
}
viewModel.IsAuthenticated = true;
viewModel.FullName = user?.FullName;
viewModel.RecentApplications = applications;
viewModel.TotalApplications = statusCounts.Sum(sc => sc.Count);
viewModel.AppliedCount = statusCounts.FirstOrDefault(sc => sc.Key == ApplicationStatus.Applied)?.Count ?? 0;
viewModel.InterviewCount = statusCounts.FirstOrDefault(sc => sc.Key == ApplicationStatus.Interview)?.Count ?? 0;
viewModel.OfferCount = statusCounts.FirstOrDefault(sc => sc.Key == ApplicationStatus.Offer)?.Count ?? 0;
viewModel.RejectedCount = statusCounts.FirstOrDefault(sc => sc.Key == ApplicationStatus.Rejected)?.Count ?? 0;
viewModel.MonthlyApplications = monthlyStatistics;
}
return View(viewModel);
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
+37
View File
@@ -0,0 +1,37 @@
using LEA.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace LEA.Data;
public class AppDbContext : IdentityDbContext<ApplicationUser>
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<Application> Applications { get; set; } = null!;
public DbSet<Contact> Contacts { get; set; } = null!;
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Application>()
.HasOne(a => a.User)
.WithMany()
.HasForeignKey(a => a.UserId)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<Application>()
.HasOne(a => a.Contact)
.WithOne(c => c.Application)
.HasForeignKey<Contact>(c => c.ApplicationId)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<Application>()
.Property(a => a.Status)
.HasConversion<string>()
.HasMaxLength(50);
}
}
@@ -0,0 +1,15 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
namespace LEA.Extensions;
public static class EnumExtensions
{
public static string GetDisplayName(this Enum value)
{
var memberInfo = value.GetType().GetMember(value.ToString()).FirstOrDefault();
var displayAttribute = memberInfo?.GetCustomAttribute<DisplayAttribute>();
return displayAttribute?.GetName() ?? value.ToString();
}
}
+23
View File
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MySqlConnector" Version="2.3.7" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
</ItemGroup>
</Project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>https</ActiveDebugProfile>
</PropertyGroup>
</Project>
@@ -0,0 +1,343 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Pomelo.EntityFrameworkCore.MySql.Metadata;
#nullable disable
namespace LEA.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterDatabase()
.Annotation("MySql:CharSet", "utf8mb4")
.Annotation("Relational:MaxIdentifierLength", 64);
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Name = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
NormalizedName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
ConcurrencyStamp = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
FullName = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UserName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
NormalizedUserName = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
Email = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
NormalizedEmail = table.Column<string>(type: "varchar(256)", maxLength: 256, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
EmailConfirmed = table.Column<bool>(type: "tinyint(1)", nullable: false),
PasswordHash = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
SecurityStamp = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
ConcurrencyStamp = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
PhoneNumber = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
PhoneNumberConfirmed = table.Column<bool>(type: "tinyint(1)", nullable: false),
TwoFactorEnabled = table.Column<bool>(type: "tinyint(1)", nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(type: "datetime(6)", nullable: true),
LockoutEnabled = table.Column<bool>(type: "tinyint(1)", nullable: false),
AccessFailedCount = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
RoleId = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ClaimType = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
ClaimValue = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
UserId = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ClaimType = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
ClaimValue = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "AspNetUserLogins",
columns: table => new
{
LoginProvider = table.Column<string>(type: "varchar(128)", maxLength: 128, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ProviderKey = table.Column<string>(type: "varchar(128)", maxLength: 128, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
ProviderDisplayName = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
UserId = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
table.ForeignKey(
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
RoleId = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
LoginProvider = table.Column<string>(type: "varchar(128)", maxLength: 128, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Name = table.Column<string>(type: "varchar(128)", maxLength: 128, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Value = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Applications",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Role = table.Column<string>(type: "varchar(150)", maxLength: 150, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Company = table.Column<string>(type: "varchar(150)", maxLength: 150, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Source = table.Column<string>(type: "varchar(200)", maxLength: 200, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
Status = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
AppliedOn = table.Column<DateTime>(type: "date", nullable: false),
Notes = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UserId = table.Column<string>(type: "varchar(255)", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_Applications", x => x.Id);
table.ForeignKey(
name: "FK_Applications_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "Contacts",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
FullName = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
Email = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
Phone = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
ApplicationId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Contacts", x => x.Id);
table.ForeignKey(
name: "FK_Contacts_Applications_ApplicationId",
column: x => x.ApplicationId,
principalTable: "Applications",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_Applications_UserId",
table: "Applications",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
table: "AspNetRoles",
column: "NormalizedName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AspNetUserClaims_UserId",
table: "AspNetUserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserLogins_UserId",
table: "AspNetUserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId",
table: "AspNetUserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
table: "AspNetUsers",
column: "NormalizedUserName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Contacts_ApplicationId",
table: "Contacts",
column: "ApplicationId",
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AspNetRoleClaims");
migrationBuilder.DropTable(
name: "AspNetUserClaims");
migrationBuilder.DropTable(
name: "AspNetUserLogins");
migrationBuilder.DropTable(
name: "AspNetUserRoles");
migrationBuilder.DropTable(
name: "AspNetUserTokens");
migrationBuilder.DropTable(
name: "Contacts");
migrationBuilder.DropTable(
name: "AspNetRoles");
migrationBuilder.DropTable(
name: "Applications");
migrationBuilder.DropTable(
name: "AspNetUsers");
}
}
}
@@ -0,0 +1,357 @@
using System;
using LEA.Data;
using LEA.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Pomelo.EntityFrameworkCore.MySql.Metadata;
#nullable disable
namespace LEA.Migrations
{
[DbContext(typeof(AppDbContext))]
partial class AppDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "8.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
MySqlModelBuilderExtensions.HasCharSet(modelBuilder, "utf8mb4");
modelBuilder.Entity("LEA.Models.Application", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
b.Property<DateTime>("AppliedOn")
.HasColumnType("date");
b.Property<string>("Company")
.IsRequired()
.HasMaxLength(150)
.HasColumnType("varchar(150)");
b.Property<string>("Notes")
.HasColumnType("longtext");
b.Property<string>("Role")
.IsRequired()
.HasMaxLength(150)
.HasColumnType("varchar(150)");
b.Property<string>("Source")
.HasMaxLength(200)
.HasColumnType("varchar(200)");
b.Property<ApplicationStatus>("Status")
.HasMaxLength(50)
.HasColumnType("varchar(50)")
.HasConversion<string>();
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("varchar(255)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Applications");
b.HasOne("LEA.Models.ApplicationUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Contact");
b.Navigation("User");
});
modelBuilder.Entity("LEA.Models.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("varchar(255)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.HasColumnType("longtext");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("tinyint(1)");
b.Property<string>("FullName")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("varchar(100)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("tinyint(1)");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetime(6)");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("longtext");
b.Property<string>("PhoneNumber")
.HasColumnType("longtext");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("tinyint(1)");
b.Property<string>("SecurityStamp")
.HasColumnType("longtext");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("tinyint(1)");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("LEA.Models.Contact", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
b.Property<int>("ApplicationId")
.HasColumnType("int");
b.Property<string>("Email")
.HasColumnType("longtext");
b.Property<string>("FullName")
.HasMaxLength(100)
.HasColumnType("varchar(100)");
b.Property<string>("Phone")
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("ApplicationId")
.IsUnique();
b.ToTable("Contacts");
b.HasOne("LEA.Models.Application", "Application")
.WithOne("Contact")
.HasForeignKey("LEA.Models.Contact", "ApplicationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Application");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("varchar(255)");
b.Property<string>("ConcurrencyStamp")
.HasColumnType("longtext");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
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("int")
.HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType")
.HasColumnType("longtext");
b.Property<string>("ClaimValue")
.HasColumnType("longtext");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("varchar(255)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType")
.HasColumnType("longtext");
b.Property<string>("ClaimValue")
.HasColumnType("longtext");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("varchar(255)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
b.HasOne("LEA.Models.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("varchar(128)");
b.Property<string>("ProviderKey")
.HasMaxLength(128)
.HasColumnType("varchar(128)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("longtext");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("varchar(255)");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
b.HasOne("LEA.Models.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("varchar(255)");
b.Property<string>("RoleId")
.HasColumnType("varchar(255)");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("LEA.Models.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("varchar(255)");
b.Property<string>("LoginProvider")
.HasMaxLength(128)
.HasColumnType("varchar(128)");
b.Property<string>("Name")
.HasMaxLength(128)
.HasColumnType("varchar(128)");
b.Property<string>("Value")
.HasColumnType("longtext");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
b.HasOne("LEA.Models.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
}
}
}
+45
View File
@@ -0,0 +1,45 @@
using System.ComponentModel.DataAnnotations;
namespace LEA.Models;
public class Application
{
public int Id { get; set; }
[Required(ErrorMessage = "Bitte geben Sie eine Rolle oder Position an.")]
[StringLength(150, ErrorMessage = "Der Titel darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Rolle / Position")]
public string Role { get; set; } = string.Empty;
[Required(ErrorMessage = "Bitte geben Sie ein Unternehmen an.")]
[StringLength(150, ErrorMessage = "Der Firmenname darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Unternehmen")]
public string Company { get; set; } = string.Empty;
[StringLength(200, ErrorMessage = "Die Quelle darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Quelle")]
public string? Source { get; set; }
[Display(Name = "Status")]
public ApplicationStatus Status { get; set; } = ApplicationStatus.Applied;
[DataType(DataType.Date)]
[Display(Name = "Beworben am")]
public DateTime AppliedOn { get; set; } = DateTime.Today;
[Display(Name = "Notizen")]
[DataType(DataType.MultilineText)]
public string? Notes { get; set; }
[Display(Name = "Letzte Aktualisierung")]
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
[Required]
public string UserId { get; set; } = string.Empty;
[Display(Name = "Benutzer")]
public ApplicationUser? User { get; set; }
[Display(Name = "Ansprechpartner")]
public Contact? Contact { get; set; }
}
@@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
namespace LEA.Models;
public enum ApplicationStatus
{
[Display(Name = "Beworben")]
Applied,
[Display(Name = "Interview")]
Interview,
[Display(Name = "Angebot")]
Offer,
[Display(Name = "Abgelehnt")]
Rejected
}
@@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Identity;
namespace LEA.Models;
public class ApplicationUser : IdentityUser
{
[Required(ErrorMessage = "Bitte geben Sie Ihren vollständigen Namen an.")]
[StringLength(100, ErrorMessage = "Der Name darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Vollständiger Name")]
public string FullName { get; set; } = string.Empty;
[Display(Name = "Registriert am")]
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
+24
View File
@@ -0,0 +1,24 @@
using System.ComponentModel.DataAnnotations;
namespace LEA.Models;
public class Contact
{
public int Id { get; set; }
[StringLength(100, ErrorMessage = "Der Name darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Name")]
public string? FullName { get; set; }
[EmailAddress(ErrorMessage = "Bitte eine gültige E-Mail-Adresse eingeben.")]
[Display(Name = "E-Mail")]
public string? Email { get; set; }
[Phone(ErrorMessage = "Bitte eine gültige Telefonnummer eingeben.")]
[Display(Name = "Telefon")]
public string? Phone { get; set; }
public int ApplicationId { get; set; }
public Application Application { get; set; } = null!;
}
@@ -0,0 +1,9 @@
namespace LEA.Models
{
public class ErrorViewModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}
+63
View File
@@ -0,0 +1,63 @@
using LEA.Data;
using LEA.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
var connectionString = builder.Configuration.GetConnectionString("Default")
?? throw new InvalidOperationException("Keine gültige Datenbank-Verbindungszeichenfolge gefunden.");
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 6;
options.User.RequireUniqueEmail = true;
})
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
builder.Services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = "LEA.Auth";
options.LoginPath = "/Account/Login";
options.AccessDeniedPath = "/Account/Login";
options.ExpireTimeSpan = TimeSpan.FromDays(14);
options.SlidingExpiration = true;
});
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
dbContext.Database.Migrate();
}
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
@@ -0,0 +1,38 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:22165",
"sslPort": 44360
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5102",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7176;http://localhost:5102",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
@@ -0,0 +1,52 @@
using System.ComponentModel.DataAnnotations;
using LEA.Models;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace LEA.ViewModels;
public class ApplicationFormViewModel
{
public int? Id { get; set; }
[Required(ErrorMessage = "Bitte geben Sie eine Rolle oder Position an.")]
[StringLength(150, ErrorMessage = "Der Titel darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Rolle / Position")]
public string Role { get; set; } = string.Empty;
[Required(ErrorMessage = "Bitte geben Sie ein Unternehmen an.")]
[StringLength(150, ErrorMessage = "Der Firmenname darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Unternehmen")]
public string Company { get; set; } = string.Empty;
[StringLength(200, ErrorMessage = "Die Quelle darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Quelle")]
public string? Source { get; set; }
[Display(Name = "Status")]
public ApplicationStatus Status { get; set; } = ApplicationStatus.Applied;
[DataType(DataType.Date)]
[Display(Name = "Beworben am")]
public DateTime AppliedOn { get; set; } = DateTime.Today;
[Display(Name = "Notizen")]
[DataType(DataType.MultilineText)]
public string? Notes { get; set; }
[Display(Name = "Name")]
public string? ContactName { get; set; }
[EmailAddress(ErrorMessage = "Bitte eine gültige E-Mail-Adresse eingeben.")]
[Display(Name = "E-Mail")]
public string? ContactEmail { get; set; }
[Phone(ErrorMessage = "Bitte eine gültige Telefonnummer eingeben.")]
[Display(Name = "Telefon")]
public string? ContactPhone { get; set; }
public IEnumerable<SelectListItem> StatusOptions { get; set; } = Enumerable.Empty<SelectListItem>();
public string Heading { get; set; } = string.Empty;
public string SubmitText { get; set; } = string.Empty;
}
@@ -0,0 +1,30 @@
using LEA.Models;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace LEA.ViewModels;
public class ApplicationListViewModel
{
public IEnumerable<Application> Applications { get; set; } = new List<Application>();
public string? SearchTerm { get; set; }
public ApplicationStatus? StatusFilter { get; set; }
public IEnumerable<SelectListItem> StatusOptions { get; set; } = new List<SelectListItem>();
public IDictionary<ApplicationStatus, int> StatusCounts { get; set; } = new Dictionary<ApplicationStatus, int>();
public int TotalApplications { get; set; }
public int AppliedCount => GetCount(ApplicationStatus.Applied);
public int InterviewCount => GetCount(ApplicationStatus.Interview);
public int OfferCount => GetCount(ApplicationStatus.Offer);
public int RejectedCount => GetCount(ApplicationStatus.Rejected);
private int GetCount(ApplicationStatus status)
=> StatusCounts.TryGetValue(status, out var count) ? count : 0;
}
@@ -0,0 +1,27 @@
using System.Collections.Generic;
using LEA.Models;
namespace LEA.ViewModels;
public class DashboardViewModel
{
public bool IsAuthenticated { get; set; }
public string? FullName { get; set; }
public int TotalApplications { get; set; }
public int AppliedCount { get; set; }
public int InterviewCount { get; set; }
public int OfferCount { get; set; }
public int RejectedCount { get; set; }
public IEnumerable<Application> RecentApplications { get; set; } = new List<Application>();
public IList<MonthlyApplicationStat> MonthlyApplications { get; set; } = new List<MonthlyApplicationStat>();
}
public record MonthlyApplicationStat(string Label, int Count);
@@ -0,0 +1,24 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc;
namespace LEA.ViewModels;
public class LoginViewModel
{
[Required(ErrorMessage = "Bitte geben Sie eine E-Mail-Adresse ein.")]
[EmailAddress(ErrorMessage = "Bitte eine gültige E-Mail-Adresse eingeben.")]
[DataType(DataType.EmailAddress)]
[Display(Name = "E-Mail-Adresse")]
public string Email { get; set; } = string.Empty;
[Required(ErrorMessage = "Bitte geben Sie ein Passwort ein.")]
[DataType(DataType.Password)]
[Display(Name = "Passwort")]
public string Password { get; set; } = string.Empty;
[Display(Name = "Angemeldet bleiben")]
public bool RememberMe { get; set; }
[HiddenInput]
public string? ReturnUrl { get; set; }
}
@@ -0,0 +1,29 @@
using System.ComponentModel.DataAnnotations;
namespace LEA.ViewModels;
public class RegisterViewModel
{
[Required(ErrorMessage = "Bitte geben Sie Ihren vollständigen Namen an.")]
[StringLength(100, ErrorMessage = "Der Name darf maximal {1} Zeichen lang sein.")]
[Display(Name = "Vollständiger Name")]
public string FullName { get; set; } = string.Empty;
[Required(ErrorMessage = "Bitte geben Sie eine E-Mail-Adresse ein.")]
[EmailAddress(ErrorMessage = "Bitte eine gültige E-Mail-Adresse eingeben.")]
[DataType(DataType.EmailAddress)]
[Display(Name = "E-Mail-Adresse")]
public string Email { get; set; } = string.Empty;
[Required(ErrorMessage = "Bitte geben Sie ein Passwort ein.")]
[DataType(DataType.Password)]
[StringLength(100, ErrorMessage = "Das Passwort muss mindestens {2} Zeichen enthalten.", MinimumLength = 6)]
[Display(Name = "Passwort")]
public string Password { get; set; } = string.Empty;
[Required(ErrorMessage = "Bitte wiederholen Sie das Passwort.")]
[DataType(DataType.Password)]
[Compare(nameof(Password), ErrorMessage = "Die Passwörter stimmen nicht überein.")]
[Display(Name = "Passwort bestätigen")]
public string ConfirmPassword { get; set; } = string.Empty;
}
@@ -0,0 +1,45 @@
@model LoginViewModel
@{
ViewData["Title"] = "Anmelden";
}
<section class="auth-container">
<div class="auth-card">
<h1>Willkommen zurück</h1>
<p class="lead">Melde dich an, um deine Bewerbungen zu verwalten.</p>
<form asp-action="Login" method="post" class="form-grid">
@Html.AntiForgeryToken()
<input type="hidden" asp-for="ReturnUrl" />
<div asp-validation-summary="ModelOnly" class="validation-summary"></div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group form-check">
<input asp-for="RememberMe" />
<label asp-for="RememberMe"></label>
</div>
<button type="submit" class="btn btn-primary">Anmelden</button>
</form>
<p class="auth-switch">
Noch kein Konto? <a asp-action="Register">Jetzt registrieren</a>
</p>
</div>
</section>
@section Scripts
{
<partial name="_ValidationScriptsPartial" />
}
@@ -0,0 +1,51 @@
@model RegisterViewModel
@{
ViewData["Title"] = "Registrieren";
}
<section class="auth-container">
<div class="auth-card">
<h1>Account erstellen</h1>
<p class="lead">Lege dein persönliches Profil an und verwalte alle Bewerbungen zentral.</p>
<form asp-action="Register" method="post" class="form-grid">
@Html.AntiForgeryToken()
<div asp-validation-summary="ModelOnly" class="validation-summary"></div>
<div class="form-group">
<label asp-for="FullName"></label>
<input asp-for="FullName" />
<span asp-validation-for="FullName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ConfirmPassword"></label>
<input asp-for="ConfirmPassword" />
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Registrieren</button>
</form>
<p class="auth-switch">
Bereits ein Konto? <a asp-action="Login">Jetzt anmelden</a>
</p>
</div>
</section>
@section Scripts
{
<partial name="_ValidationScriptsPartial" />
}
@@ -0,0 +1,28 @@
@model ApplicationFormViewModel
@{
ViewData["Title"] = "Bewerbung hinzufügen";
}
<section class="content-card">
<div class="section-header">
<h1>@Model.Heading</h1>
<a class="link" asp-action="Index">Zurück zur Übersicht</a>
</div>
<form asp-action="Create" method="post">
@Html.AntiForgeryToken()
<div asp-validation-summary="ModelOnly" class="validation-summary"></div>
<partial name="_ApplicationForm" />
<div class="form-actions">
<a class="btn btn-outline" asp-action="Index">Abbrechen</a>
<button type="submit" class="btn btn-primary">@Model.SubmitText</button>
</div>
</form>
</section>
@section Scripts
{
<partial name="_ValidationScriptsPartial" />
}
@@ -0,0 +1,88 @@
@model Application
@{
ViewData["Title"] = "Details";
}
<section class="content-card">
<div class="section-header">
<h1>@Model.Role bei @Model.Company</h1>
<div class="action-group">
<a class="btn btn-outline" asp-action="Edit" asp-route-id="@Model.Id">Bearbeiten</a>
<a class="link" asp-action="Index">Zurück</a>
</div>
</div>
<div class="detail-grid">
<div class="detail-card">
<h2>Überblick</h2>
<dl>
<div>
<dt>Status</dt>
<dd>
<span class="status-badge status-@Model.Status.ToString().ToLowerInvariant()">
@Model.Status.GetDisplayName()
</span>
</dd>
</div>
<div>
<dt>Beworben am</dt>
<dd>@Model.AppliedOn.ToString("dd.MM.yyyy")</dd>
</div>
<div>
<dt>Quelle</dt>
<dd>@(string.IsNullOrWhiteSpace(Model.Source) ? "-" : Model.Source)</dd>
</div>
<div>
<dt>Zuletzt aktualisiert</dt>
<dd>@Model.UpdatedAt.ToLocalTime().ToString("dd.MM.yyyy HH:mm")</dd>
</div>
</dl>
</div>
<div class="detail-card">
<h2>Notizen</h2>
@if (string.IsNullOrWhiteSpace(Model.Notes))
{
<p class="muted">Keine Notizen hinterlegt.</p>
}
else
{
<p>@Model.Notes</p>
}
</div>
<div class="detail-card">
<h2>Ansprechpartner</h2>
@if (Model.Contact == null || (string.IsNullOrWhiteSpace(Model.Contact.FullName) && string.IsNullOrWhiteSpace(Model.Contact.Email) && string.IsNullOrWhiteSpace(Model.Contact.Phone)))
{
<p class="muted">Kein Ansprechpartner hinterlegt.</p>
}
else
{
<dl>
@if (!string.IsNullOrWhiteSpace(Model.Contact.FullName))
{
<div>
<dt>Name</dt>
<dd>@Model.Contact.FullName</dd>
</div>
}
@if (!string.IsNullOrWhiteSpace(Model.Contact.Email))
{
<div>
<dt>E-Mail</dt>
<dd><a href="mailto:@Model.Contact.Email">@Model.Contact.Email</a></dd>
</div>
}
@if (!string.IsNullOrWhiteSpace(Model.Contact.Phone))
{
<div>
<dt>Telefon</dt>
<dd><a href="tel:@Model.Contact.Phone">@Model.Contact.Phone</a></dd>
</div>
}
</dl>
}
</div>
</div>
</section>
@@ -0,0 +1,28 @@
@model ApplicationFormViewModel
@{
ViewData["Title"] = "Bewerbung bearbeiten";
}
<section class="content-card">
<div class="section-header">
<h1>@Model.Heading</h1>
<a class="link" asp-action="Index">Zurück zur Übersicht</a>
</div>
<form asp-action="Edit" asp-route-id="@Model.Id" method="post">
@Html.AntiForgeryToken()
<div asp-validation-summary="ModelOnly" class="validation-summary"></div>
<partial name="_ApplicationForm" />
<div class="form-actions">
<a class="btn btn-outline" asp-action="Index">Abbrechen</a>
<button type="submit" class="btn btn-primary">@Model.SubmitText</button>
</div>
</form>
</section>
@section Scripts
{
<partial name="_ValidationScriptsPartial" />
}
@@ -0,0 +1,142 @@
@model ApplicationListViewModel
@using System.Text.Json
@{
ViewData["Title"] = "Meine Bewerbungen";
}
<section class="page-header">
<div>
<h1>Bewerbungen</h1>
<p class="lead">Verwalte deine Bewerbungen, halte Fortschritte fest und behalte alle Termine im Blick.</p>
</div>
<a class="btn btn-primary" asp-action="Create">Neue Bewerbung</a>
</section>
<section class="stats-grid compact">
<article class="stat-card">
<p class="stat-label">Gesamt</p>
<p class="stat-value">@Model.TotalApplications</p>
<p class="stat-caption">Gespeicherte Bewerbungen</p>
</article>
<article class="stat-card">
<p class="stat-label">Beworben</p>
<p class="stat-value">@Model.AppliedCount</p>
<p class="stat-caption">Offene Bewerbungen</p>
</article>
<article class="stat-card">
<p class="stat-label">Interviews</p>
<p class="stat-value">@Model.InterviewCount</p>
<p class="stat-caption">Anstehende Gespräche</p>
</article>
<article class="stat-card">
<p class="stat-label">Angebote</p>
<p class="stat-value">@Model.OfferCount</p>
<p class="stat-caption">Positive Rückmeldungen</p>
</article>
<article class="stat-card">
<p class="stat-label">Absagen</p>
<p class="stat-value">@Model.RejectedCount</p>
<p class="stat-caption">Abgeschlossene Bewerbungen</p>
</article>
</section>
@if (Model.TotalApplications > 0)
{
var statusChartConfig = JsonSerializer.Serialize(new
{
type = "doughnut",
labels = new[] { "Beworben", "Interview", "Angebot", "Abgelehnt" },
values = new[] { Model.AppliedCount, Model.InterviewCount, Model.OfferCount, Model.RejectedCount },
colors = new[] { "#4460f7", "#3ac0a0", "#ff9f43", "#ef476f" },
borderColor = "#ffffff",
datasetLabel = "Bewerbungen",
legend = true
});
<section class="visual-grid compact">
<article class="chart-card">
<div class="chart-card-header">
<h2>Statusübersicht</h2>
<p>Verteilung deiner Bewerbungen nach aktuellem Status.</p>
</div>
<div class="chart-wrapper">
<canvas id="applications-status-chart" data-chart='@Html.Raw(statusChartConfig)'></canvas>
</div>
</article>
</section>
}
<section class="filter-card">
<form method="get" class="filter-grid">
<div class="form-group">
<label for="searchTerm">Suche</label>
<input id="searchTerm" name="searchTerm" value="@Model.SearchTerm" placeholder="Nach Unternehmen, Rolle oder Notizen suchen" />
</div>
<div class="form-group">
<label for="status">Status</label>
<select id="status" name="status">
@foreach (var option in Model.StatusOptions)
{
<option value="@option.Value" selected="@(option.Selected ? "selected" : null)">@option.Text</option>
}
</select>
</div>
<div class="filter-actions">
<button type="submit" class="btn btn-primary">Filter anwenden</button>
<a class="btn btn-outline" asp-action="Index">Zurücksetzen</a>
</div>
</form>
</section>
@if (!Model.Applications.Any())
{
<div class="empty-state">
<p>Noch keine Bewerbungen erfasst.</p>
<a class="btn btn-primary" asp-action="Create">Jetzt Bewerbung hinzufügen</a>
</div>
}
else
{
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>Unternehmen</th>
<th>Rolle</th>
<th>Quelle</th>
<th>Status</th>
<th>Beworben am</th>
<th>Letzte Aktualisierung</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var application in Model.Applications)
{
<tr>
<td>@application.Company</td>
<td>@application.Role</td>
<td>@(string.IsNullOrWhiteSpace(application.Source) ? "-" : application.Source)</td>
<td>
<span class="status-badge status-@application.Status.ToString().ToLowerInvariant()">
@application.Status.GetDisplayName()
</span>
</td>
<td>@application.AppliedOn.ToString("dd.MM.yyyy")</td>
<td>@application.UpdatedAt.ToLocalTime().ToString("dd.MM.yyyy HH:mm")</td>
<td>
<div class="table-actions">
<a class="link" asp-action="Details" asp-route-id="@application.Id">Details</a>
<a class="link" asp-action="Edit" asp-route-id="@application.Id">Bearbeiten</a>
<form asp-action="Delete" asp-route-id="@application.Id" method="post" class="inline-form" onsubmit="return confirm('Möchtest du diese Bewerbung wirklich löschen?');">
@Html.AntiForgeryToken()
<button type="submit" class="btn-link">Löschen</button>
</form>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
}
@@ -0,0 +1,56 @@
@model ApplicationFormViewModel
<div class="form-grid two-columns">
<div class="form-group">
<label asp-for="Company"></label>
<input asp-for="Company" />
<span asp-validation-for="Company" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Role"></label>
<input asp-for="Role" />
<span asp-validation-for="Role" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Source"></label>
<input asp-for="Source" />
<span asp-validation-for="Source" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AppliedOn"></label>
<input asp-for="AppliedOn" type="date" />
<span asp-validation-for="AppliedOn" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Status"></label>
<select asp-for="Status" asp-items="Model.StatusOptions"></select>
<span asp-validation-for="Status" class="text-danger"></span>
</div>
<div class="form-group full-width">
<label asp-for="Notes"></label>
<textarea asp-for="Notes" rows="4"></textarea>
<span asp-validation-for="Notes" class="text-danger"></span>
</div>
</div>
<div class="form-divider">
<span>Ansprechpartner (optional)</span>
</div>
<div class="form-grid three-columns">
<div class="form-group">
<label asp-for="ContactName"></label>
<input asp-for="ContactName" />
<span asp-validation-for="ContactName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ContactEmail"></label>
<input asp-for="ContactEmail" />
<span asp-validation-for="ContactEmail" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ContactPhone"></label>
<input asp-for="ContactPhone" />
<span asp-validation-for="ContactPhone" class="text-danger"></span>
</div>
</div>
+210
View File
@@ -0,0 +1,210 @@
@model DashboardViewModel
@using System.Text.Encodings.Web
@using System.Text.Json
@using System.Linq
@{
ViewData["Title"] = "Übersicht";
var chartSerializerOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
var monthlyStats = Model.MonthlyApplications ?? Array.Empty<MonthlyApplicationStat>();
var recentApplications = Model.RecentApplications ?? Enumerable.Empty<Application>();
}
@if (!Model.IsAuthenticated)
{
<section class="hero">
<div class="hero-content">
<p class="eyebrow">LEA Bewerbungs-Tracker</p>
<h1>Behalte deine Bewerbungen mühelos im Blick</h1>
<p class="lead">Erfasse Bewerbungen, aktualisiere Status und sammle Notizen an einem Ort. Mit wenigen Klicks bleibst du organisiert und bestens vorbereitet auf jedes Gespräch.</p>
<div class="hero-actions">
<a class="btn btn-primary" asp-controller="Account" asp-action="Register">Jetzt registrieren</a>
<a class="btn btn-outline" asp-controller="Account" asp-action="Login">Anmelden</a>
</div>
</div>
<div class="hero-visual" aria-hidden="true">
<div class="hero-card">
<span class="status-badge status-applied">Beworben</span>
<span class="status-badge status-interview">Interview</span>
<span class="status-badge status-offer">Angebot</span>
<span class="status-badge status-rejected">Abgelehnt</span>
</div>
<div class="hero-highlight">
<p><strong>Smarter Überblick</strong></p>
<p>Analysiere Trends, nutze Filter und führe strukturierte Gespräche mit deinem nächsten Arbeitgeber.</p>
</div>
</div>
</section>
<section class="feature-grid">
<article class="feature-card">
<h3>Bewerbungen speichern</h3>
<p>Lege jede Bewerbung mit wenigen Angaben an inklusive Ansprechpartner, Quelle und individuellen Notizen.</p>
</article>
<article class="feature-card">
<h3>Status &amp; Fortschritt</h3>
<p>Halte Termine und Feedback fest, aktualisiere den Status und bleibe immer einen Schritt voraus.</p>
</article>
<article class="feature-card">
<h3>Individuelle Statistiken</h3>
<p>Erhalte auf einen Blick, wie viele Bewerbungen offen, im Interview oder bereits abgeschlossen sind.</p>
</article>
</section>
}
else
{
<section class="dashboard-header">
<div>
<p class="eyebrow">Willkommen zurück</p>
<h1>@(string.IsNullOrWhiteSpace(Model.FullName) ? "Dein Bewerbungs-Dashboard" : $"Hallo, {Model.FullName}!")</h1>
<p class="lead">Behalte deine Bewerbungen im Blick, aktualisiere den Status und bereite dich optimal auf deine nächsten Schritte vor.</p>
<div class="action-group">
<a class="btn btn-primary" asp-controller="Applications" asp-action="Create">Neue Bewerbung erfassen</a>
<a class="btn btn-outline" asp-controller="Applications" asp-action="Index">Alle Bewerbungen anzeigen</a>
</div>
</div>
</section>
<section class="stats-grid">
<article class="stat-card">
<p class="stat-label">Gesamt</p>
<p class="stat-value">@Model.TotalApplications</p>
<p class="stat-caption">Bewerbungen insgesamt</p>
</article>
<article class="stat-card">
<p class="stat-label">Beworben</p>
<p class="stat-value">@Model.AppliedCount</p>
<p class="stat-caption">Offene Bewerbungen</p>
</article>
<article class="stat-card">
<p class="stat-label">Interviews</p>
<p class="stat-value">@Model.InterviewCount</p>
<p class="stat-caption">Geplante Gespräche</p>
</article>
<article class="stat-card">
<p class="stat-label">Angebote</p>
<p class="stat-value">@Model.OfferCount</p>
<p class="stat-caption">Erfolgreiche Angebote</p>
</article>
<article class="stat-card">
<p class="stat-label">Absagen</p>
<p class="stat-value">@Model.RejectedCount</p>
<p class="stat-caption">Erhaltene Rückmeldungen</p>
</article>
</section>
<section class="visual-grid">
<article class="chart-card">
<div class="chart-card-header">
<h2>Statusübersicht</h2>
<p>Wie sich deine Bewerbungen aktuell verteilen.</p>
</div>
@if (Model.TotalApplications == 0)
{
<p class="chart-empty">Sobald du Bewerbungen erfasst, erscheint hier eine Visualisierung.</p>
}
else
{
var statusChartConfig = JsonSerializer.Serialize(new
{
type = "doughnut",
labels = new[] { "Beworben", "Interview", "Angebot", "Abgelehnt" },
values = new[] { Model.AppliedCount, Model.InterviewCount, Model.OfferCount, Model.RejectedCount },
colors = new[] { "#4460f7", "#3ac0a0", "#ff9f43", "#ef476f" },
borderColor = "#ffffff",
datasetLabel = "Bewerbungen",
legend = true
}, chartSerializerOptions);
<div class="chart-wrapper">
<canvas id="dashboard-status-chart" data-chart='@Html.Raw(statusChartConfig)'></canvas>
</div>
}
</article>
<article class="chart-card">
<div class="chart-card-header">
<h2>Monatlicher Verlauf</h2>
<p>Wie viele Bewerbungen du in den letzten Monaten versendet hast.</p>
</div>
@if (!monthlyStats.Any(stat => stat.Count > 0))
{
<p class="chart-empty">Sobald Bewerbungen vorhanden sind, zeigen wir hier deinen Verlauf.</p>
}
else
{
var monthlyLabels = monthlyStats.Select(stat => stat.Label).ToArray();
var monthlyValues = monthlyStats.Select(stat => stat.Count).ToArray();
var monthlyChartConfig = JsonSerializer.Serialize(new
{
type = "line",
labels = monthlyLabels,
values = monthlyValues,
colors = new[] { "rgba(68, 96, 247, 0.20)" },
borderColor = "#4460f7",
datasetLabel = "Bewerbungen",
tension = 0.35,
fill = true,
legend = false
}, chartSerializerOptions);
<div class="chart-wrapper">
<canvas id="dashboard-monthly-chart" data-chart='@Html.Raw(monthlyChartConfig)'></canvas>
</div>
}
</article>
</section>
<section class="recent-applications">
<div class="section-header">
<h2>Aktuelle Bewerbungen</h2>
<a class="link" asp-controller="Applications" asp-action="Index">Zur Bewerbungsübersicht</a>
</div>
@if (!recentApplications.Any())
{
<div class="empty-state">
<p>Noch keine Bewerbungen erfasst.</p>
<a class="btn btn-primary" asp-controller="Applications" asp-action="Create">Jetzt starten</a>
</div>
}
else
{
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>Unternehmen</th>
<th>Rolle</th>
<th>Status</th>
<th>Beworben am</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var application in recentApplications)
{
<tr>
<td>@application.Company</td>
<td>@application.Role</td>
<td>
<span class="status-badge status-@application.Status.ToString().ToLowerInvariant()">
@application.Status.GetDisplayName()
</span>
</td>
<td>@application.AppliedOn.ToString("dd.MM.yyyy")</td>
<td class="table-actions">
<a class="link" asp-controller="Applications" asp-action="Details" asp-route-id="@application.Id">Details</a>
</td>
</tr>
}
</tbody>
</table>
</div>
}
</section>
}
@@ -0,0 +1,16 @@
@{
ViewData["Title"] = "Datenschutz";
}
<section class="content-card">
<h1>Datenschutzhinweise</h1>
<p>
Der Schutz deiner persönlichen Daten ist uns wichtig. Diese Anwendung speichert ausschließlich die Angaben,
die du selbst für deine Bewerbungsverwaltung hinterlegst. Dazu gehören unter anderem Unternehmensdaten,
Ansprechpartner sowie deine Notizen.
</p>
<p>
Du kannst deine Daten jederzeit bearbeiten oder löschen. Für Fragen oder Feedback kontaktiere uns bitte über
die im Impressum hinterlegte E-Mail-Adresse.
</p>
</section>
@@ -0,0 +1,25 @@
@model ErrorViewModel
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
@@ -0,0 +1,97 @@
@using LEA.Models
@inject UserManager<ApplicationUser> UserManager
@{
ApplicationUser? currentUser = null;
if (User.Identity?.IsAuthenticated ?? false)
{
currentUser = await UserManager.GetUserAsync(User);
}
var displayName = currentUser?.FullName;
}
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>@ViewData["Title"] - LEA Bewerbungs-Tracker</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/LEA.styles.css" asp-append-version="true" />
</head>
<body>
<header class="site-header">
<nav class="main-nav">
<a class="brand" asp-controller="Home" asp-action="Index">LEA Bewerbungs-Tracker</a>
<button class="nav-toggle" type="button" aria-label="Navigation umschalten" data-target="primary-nav">
<span></span>
<span></span>
<span></span>
</button>
<div class="nav-menu" id="primary-nav">
<ul class="nav-links">
<li><a asp-controller="Home" asp-action="Index" class="@(ViewContext.RouteData.Values["controller"]?.ToString() == "Home" ? "active" : null)">Start</a></li>
@if (User.Identity?.IsAuthenticated ?? false)
{
<li><a asp-controller="Applications" asp-action="Index" class="@(ViewContext.RouteData.Values["controller"]?.ToString() == "Applications" ? "active" : null)">Bewerbungen</a></li>
}
<li><a asp-controller="Home" asp-action="Privacy">Datenschutz</a></li>
</ul>
<div class="nav-actions">
@if (User.Identity?.IsAuthenticated ?? false)
{
<span class="nav-user">Hallo, @(string.IsNullOrWhiteSpace(displayName) ? User.Identity?.Name : displayName)</span>
<form asp-controller="Account" asp-action="Logout" method="post">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-outline">Abmelden</button>
</form>
}
else
{
<a class="btn btn-link" asp-controller="Account" asp-action="Login">Anmelden</a>
<a class="btn btn-primary" asp-controller="Account" asp-action="Register">Registrieren</a>
}
</div>
</div>
</nav>
</header>
<div class="flash-container">
@if (TempData["Success"] is string successMessage)
{
<div class="flash-message success" role="status">
<span>@successMessage</span>
<button type="button" class="flash-close" aria-label="Meldung schließen">&times;</button>
</div>
}
@if (TempData["Error"] is string errorMessage)
{
<div class="flash-message error" role="status">
<span>@errorMessage</span>
<button type="button" class="flash-close" aria-label="Meldung schließen">&times;</button>
</div>
}
</div>
<main class="site-main">
@RenderBody()
</main>
<footer class="site-footer">
<div class="footer-inner">
<p>&copy; @DateTime.Now.Year LEA Bewerbungs-Tracker</p>
<div class="footer-links">
<a asp-controller="Home" asp-action="Index">Start</a>
<a asp-controller="Home" asp-action="Privacy">Datenschutz</a>
</div>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js" crossorigin="anonymous"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
@@ -0,0 +1,48 @@
/* Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
for details on configuring this project to bundle and minify static web assets. */
a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}
a {
color: #0077cc;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.border-top {
border-top: 1px solid #e5e5e5;
}
.border-bottom {
border-bottom: 1px solid #e5e5e5;
}
.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
button.accept-policy {
font-size: 1rem;
line-height: inherit;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
white-space: nowrap;
line-height: 60px;
}
@@ -0,0 +1,2 @@
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
@@ -0,0 +1,6 @@
@using LEA
@using LEA.Models
@using LEA.ViewModels
@using LEA.Extensions
@using Microsoft.AspNetCore.Identity
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
+13
View File
@@ -0,0 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"Default": "Server=localhost;Database=jobtracker;User=root;Password=08911395;TreatTinyAsBoolean=true;SslMode=None"
}
}
Binary file not shown.
@@ -0,0 +1,889 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v8.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v8.0": {
"LEA/1.0.0": {
"dependencies": {
"Microsoft.AspNetCore.Identity.EntityFrameworkCore": "8.0.8",
"Microsoft.EntityFrameworkCore.Design": "8.0.8",
"Microsoft.EntityFrameworkCore.Tools": "8.0.8",
"MySqlConnector": "2.3.7",
"Pomelo.EntityFrameworkCore.MySql": "8.0.2"
},
"runtime": {
"LEA.dll": {}
}
},
"Humanizer.Core/2.14.1": {
"runtime": {
"lib/net6.0/Humanizer.dll": {
"assemblyVersion": "2.14.0.0",
"fileVersion": "2.14.1.48190"
}
}
},
"Microsoft.AspNetCore.Cryptography.Internal/8.0.8": {
"runtime": {
"lib/net8.0/Microsoft.AspNetCore.Cryptography.Internal.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.824.36908"
}
}
},
"Microsoft.AspNetCore.Cryptography.KeyDerivation/8.0.8": {
"dependencies": {
"Microsoft.AspNetCore.Cryptography.Internal": "8.0.8"
},
"runtime": {
"lib/net8.0/Microsoft.AspNetCore.Cryptography.KeyDerivation.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.824.36908"
}
}
},
"Microsoft.AspNetCore.Identity.EntityFrameworkCore/8.0.8": {
"dependencies": {
"Microsoft.EntityFrameworkCore.Relational": "8.0.8",
"Microsoft.Extensions.Identity.Stores": "8.0.8"
},
"runtime": {
"lib/net8.0/Microsoft.AspNetCore.Identity.EntityFrameworkCore.dll": {
"assemblyVersion": "8.0.8.0",
"fileVersion": "8.0.824.36908"
}
}
},
"Microsoft.Bcl.AsyncInterfaces/6.0.0": {
"runtime": {
"lib/netstandard2.1/Microsoft.Bcl.AsyncInterfaces.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"Microsoft.CodeAnalysis.Analyzers/3.3.3": {},
"Microsoft.CodeAnalysis.Common/4.5.0": {
"dependencies": {
"Microsoft.CodeAnalysis.Analyzers": "3.3.3",
"System.Collections.Immutable": "6.0.0",
"System.Reflection.Metadata": "6.0.1",
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
"System.Text.Encoding.CodePages": "6.0.0"
},
"runtime": {
"lib/netcoreapp3.1/Microsoft.CodeAnalysis.dll": {
"assemblyVersion": "4.5.0.0",
"fileVersion": "4.500.23.10905"
}
},
"resources": {
"lib/netcoreapp3.1/cs/Microsoft.CodeAnalysis.resources.dll": {
"locale": "cs"
},
"lib/netcoreapp3.1/de/Microsoft.CodeAnalysis.resources.dll": {
"locale": "de"
},
"lib/netcoreapp3.1/es/Microsoft.CodeAnalysis.resources.dll": {
"locale": "es"
},
"lib/netcoreapp3.1/fr/Microsoft.CodeAnalysis.resources.dll": {
"locale": "fr"
},
"lib/netcoreapp3.1/it/Microsoft.CodeAnalysis.resources.dll": {
"locale": "it"
},
"lib/netcoreapp3.1/ja/Microsoft.CodeAnalysis.resources.dll": {
"locale": "ja"
},
"lib/netcoreapp3.1/ko/Microsoft.CodeAnalysis.resources.dll": {
"locale": "ko"
},
"lib/netcoreapp3.1/pl/Microsoft.CodeAnalysis.resources.dll": {
"locale": "pl"
},
"lib/netcoreapp3.1/pt-BR/Microsoft.CodeAnalysis.resources.dll": {
"locale": "pt-BR"
},
"lib/netcoreapp3.1/ru/Microsoft.CodeAnalysis.resources.dll": {
"locale": "ru"
},
"lib/netcoreapp3.1/tr/Microsoft.CodeAnalysis.resources.dll": {
"locale": "tr"
},
"lib/netcoreapp3.1/zh-Hans/Microsoft.CodeAnalysis.resources.dll": {
"locale": "zh-Hans"
},
"lib/netcoreapp3.1/zh-Hant/Microsoft.CodeAnalysis.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.CodeAnalysis.CSharp/4.5.0": {
"dependencies": {
"Microsoft.CodeAnalysis.Common": "4.5.0"
},
"runtime": {
"lib/netcoreapp3.1/Microsoft.CodeAnalysis.CSharp.dll": {
"assemblyVersion": "4.5.0.0",
"fileVersion": "4.500.23.10905"
}
},
"resources": {
"lib/netcoreapp3.1/cs/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "cs"
},
"lib/netcoreapp3.1/de/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "de"
},
"lib/netcoreapp3.1/es/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "es"
},
"lib/netcoreapp3.1/fr/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "fr"
},
"lib/netcoreapp3.1/it/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "it"
},
"lib/netcoreapp3.1/ja/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "ja"
},
"lib/netcoreapp3.1/ko/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "ko"
},
"lib/netcoreapp3.1/pl/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "pl"
},
"lib/netcoreapp3.1/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "pt-BR"
},
"lib/netcoreapp3.1/ru/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "ru"
},
"lib/netcoreapp3.1/tr/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "tr"
},
"lib/netcoreapp3.1/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "zh-Hans"
},
"lib/netcoreapp3.1/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.CodeAnalysis.CSharp.Workspaces/4.5.0": {
"dependencies": {
"Humanizer.Core": "2.14.1",
"Microsoft.CodeAnalysis.CSharp": "4.5.0",
"Microsoft.CodeAnalysis.Common": "4.5.0",
"Microsoft.CodeAnalysis.Workspaces.Common": "4.5.0"
},
"runtime": {
"lib/netcoreapp3.1/Microsoft.CodeAnalysis.CSharp.Workspaces.dll": {
"assemblyVersion": "4.5.0.0",
"fileVersion": "4.500.23.10905"
}
},
"resources": {
"lib/netcoreapp3.1/cs/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "cs"
},
"lib/netcoreapp3.1/de/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "de"
},
"lib/netcoreapp3.1/es/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "es"
},
"lib/netcoreapp3.1/fr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "fr"
},
"lib/netcoreapp3.1/it/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "it"
},
"lib/netcoreapp3.1/ja/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "ja"
},
"lib/netcoreapp3.1/ko/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "ko"
},
"lib/netcoreapp3.1/pl/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "pl"
},
"lib/netcoreapp3.1/pt-BR/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "pt-BR"
},
"lib/netcoreapp3.1/ru/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "ru"
},
"lib/netcoreapp3.1/tr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "tr"
},
"lib/netcoreapp3.1/zh-Hans/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "zh-Hans"
},
"lib/netcoreapp3.1/zh-Hant/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.CodeAnalysis.Workspaces.Common/4.5.0": {
"dependencies": {
"Humanizer.Core": "2.14.1",
"Microsoft.Bcl.AsyncInterfaces": "6.0.0",
"Microsoft.CodeAnalysis.Common": "4.5.0",
"System.Composition": "6.0.0",
"System.IO.Pipelines": "6.0.3",
"System.Threading.Channels": "6.0.0"
},
"runtime": {
"lib/netcoreapp3.1/Microsoft.CodeAnalysis.Workspaces.dll": {
"assemblyVersion": "4.5.0.0",
"fileVersion": "4.500.23.10905"
}
},
"resources": {
"lib/netcoreapp3.1/cs/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "cs"
},
"lib/netcoreapp3.1/de/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "de"
},
"lib/netcoreapp3.1/es/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "es"
},
"lib/netcoreapp3.1/fr/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "fr"
},
"lib/netcoreapp3.1/it/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "it"
},
"lib/netcoreapp3.1/ja/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "ja"
},
"lib/netcoreapp3.1/ko/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "ko"
},
"lib/netcoreapp3.1/pl/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "pl"
},
"lib/netcoreapp3.1/pt-BR/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "pt-BR"
},
"lib/netcoreapp3.1/ru/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "ru"
},
"lib/netcoreapp3.1/tr/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "tr"
},
"lib/netcoreapp3.1/zh-Hans/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "zh-Hans"
},
"lib/netcoreapp3.1/zh-Hant/Microsoft.CodeAnalysis.Workspaces.resources.dll": {
"locale": "zh-Hant"
}
}
},
"Microsoft.EntityFrameworkCore/8.0.8": {
"dependencies": {
"Microsoft.EntityFrameworkCore.Abstractions": "8.0.8",
"Microsoft.EntityFrameworkCore.Analyzers": "8.0.8",
"Microsoft.Extensions.Caching.Memory": "8.0.0",
"Microsoft.Extensions.Logging": "8.0.0"
},
"runtime": {
"lib/net8.0/Microsoft.EntityFrameworkCore.dll": {
"assemblyVersion": "8.0.8.0",
"fileVersion": "8.0.824.36704"
}
}
},
"Microsoft.EntityFrameworkCore.Abstractions/8.0.8": {
"runtime": {
"lib/net8.0/Microsoft.EntityFrameworkCore.Abstractions.dll": {
"assemblyVersion": "8.0.8.0",
"fileVersion": "8.0.824.36704"
}
}
},
"Microsoft.EntityFrameworkCore.Analyzers/8.0.8": {},
"Microsoft.EntityFrameworkCore.Design/8.0.8": {
"dependencies": {
"Humanizer.Core": "2.14.1",
"Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0",
"Microsoft.EntityFrameworkCore.Relational": "8.0.8",
"Microsoft.Extensions.DependencyModel": "8.0.1",
"Mono.TextTemplating": "2.2.1"
},
"runtime": {
"lib/net8.0/Microsoft.EntityFrameworkCore.Design.dll": {
"assemblyVersion": "8.0.8.0",
"fileVersion": "8.0.824.36704"
}
}
},
"Microsoft.EntityFrameworkCore.Relational/8.0.8": {
"dependencies": {
"Microsoft.EntityFrameworkCore": "8.0.8",
"Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
},
"runtime": {
"lib/net8.0/Microsoft.EntityFrameworkCore.Relational.dll": {
"assemblyVersion": "8.0.8.0",
"fileVersion": "8.0.824.36704"
}
}
},
"Microsoft.EntityFrameworkCore.Tools/8.0.8": {
"dependencies": {
"Microsoft.EntityFrameworkCore.Design": "8.0.8"
}
},
"Microsoft.Extensions.Caching.Abstractions/8.0.0": {
"dependencies": {
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Caching.Memory/8.0.0": {
"dependencies": {
"Microsoft.Extensions.Caching.Abstractions": "8.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
"Microsoft.Extensions.Options": "8.0.2",
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Configuration.Abstractions/8.0.0": {
"dependencies": {
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.DependencyInjection/8.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/8.0.0": {},
"Microsoft.Extensions.DependencyModel/8.0.1": {
"dependencies": {
"System.Text.Encodings.Web": "8.0.0",
"System.Text.Json": "8.0.4"
},
"runtime": {
"lib/net8.0/Microsoft.Extensions.DependencyModel.dll": {
"assemblyVersion": "8.0.0.1",
"fileVersion": "8.0.724.31311"
}
}
},
"Microsoft.Extensions.Identity.Core/8.0.8": {
"dependencies": {
"Microsoft.AspNetCore.Cryptography.KeyDerivation": "8.0.8",
"Microsoft.Extensions.Logging": "8.0.0",
"Microsoft.Extensions.Options": "8.0.2"
},
"runtime": {
"lib/net8.0/Microsoft.Extensions.Identity.Core.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.824.36908"
}
}
},
"Microsoft.Extensions.Identity.Stores/8.0.8": {
"dependencies": {
"Microsoft.Extensions.Caching.Abstractions": "8.0.0",
"Microsoft.Extensions.Identity.Core": "8.0.8",
"Microsoft.Extensions.Logging": "8.0.0"
},
"runtime": {
"lib/net8.0/Microsoft.Extensions.Identity.Stores.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.824.36908"
}
}
},
"Microsoft.Extensions.Logging/8.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "8.0.0",
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
"Microsoft.Extensions.Options": "8.0.2"
}
},
"Microsoft.Extensions.Logging.Abstractions/8.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
}
},
"Microsoft.Extensions.Options/8.0.2": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Primitives": "8.0.0"
},
"runtime": {
"lib/net8.0/Microsoft.Extensions.Options.dll": {
"assemblyVersion": "8.0.0.0",
"fileVersion": "8.0.224.6711"
}
}
},
"Microsoft.Extensions.Primitives/8.0.0": {},
"Mono.TextTemplating/2.2.1": {
"dependencies": {
"System.CodeDom": "4.4.0"
},
"runtime": {
"lib/netstandard2.0/Mono.TextTemplating.dll": {
"assemblyVersion": "2.2.0.0",
"fileVersion": "2.2.1.1"
}
}
},
"MySqlConnector/2.3.7": {
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "8.0.0"
},
"runtime": {
"lib/net8.0/MySqlConnector.dll": {
"assemblyVersion": "2.0.0.0",
"fileVersion": "2.3.7.0"
}
}
},
"Pomelo.EntityFrameworkCore.MySql/8.0.2": {
"dependencies": {
"Microsoft.EntityFrameworkCore.Relational": "8.0.8",
"MySqlConnector": "2.3.7"
},
"runtime": {
"lib/net8.0/Pomelo.EntityFrameworkCore.MySql.dll": {
"assemblyVersion": "8.0.2.0",
"fileVersion": "8.0.2.0"
}
}
},
"System.CodeDom/4.4.0": {
"runtime": {
"lib/netstandard2.0/System.CodeDom.dll": {
"assemblyVersion": "4.0.0.0",
"fileVersion": "4.6.25519.3"
}
}
},
"System.Collections.Immutable/6.0.0": {
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Composition/6.0.0": {
"dependencies": {
"System.Composition.AttributedModel": "6.0.0",
"System.Composition.Convention": "6.0.0",
"System.Composition.Hosting": "6.0.0",
"System.Composition.Runtime": "6.0.0",
"System.Composition.TypedParts": "6.0.0"
}
},
"System.Composition.AttributedModel/6.0.0": {
"runtime": {
"lib/net6.0/System.Composition.AttributedModel.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"System.Composition.Convention/6.0.0": {
"dependencies": {
"System.Composition.AttributedModel": "6.0.0"
},
"runtime": {
"lib/net6.0/System.Composition.Convention.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"System.Composition.Hosting/6.0.0": {
"dependencies": {
"System.Composition.Runtime": "6.0.0"
},
"runtime": {
"lib/net6.0/System.Composition.Hosting.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"System.Composition.Runtime/6.0.0": {
"runtime": {
"lib/net6.0/System.Composition.Runtime.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"System.Composition.TypedParts/6.0.0": {
"dependencies": {
"System.Composition.AttributedModel": "6.0.0",
"System.Composition.Hosting": "6.0.0",
"System.Composition.Runtime": "6.0.0"
},
"runtime": {
"lib/net6.0/System.Composition.TypedParts.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"System.IO.Pipelines/6.0.3": {},
"System.Reflection.Metadata/6.0.1": {
"dependencies": {
"System.Collections.Immutable": "6.0.0"
}
},
"System.Runtime.CompilerServices.Unsafe/6.0.0": {},
"System.Text.Encoding.CodePages/6.0.0": {
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Text.Encodings.Web/8.0.0": {},
"System.Text.Json/8.0.4": {
"dependencies": {
"System.Text.Encodings.Web": "8.0.0"
}
},
"System.Threading.Channels/6.0.0": {}
}
},
"libraries": {
"LEA/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"Humanizer.Core/2.14.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==",
"path": "humanizer.core/2.14.1",
"hashPath": "humanizer.core.2.14.1.nupkg.sha512"
},
"Microsoft.AspNetCore.Cryptography.Internal/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-VrfHrQwvj8p5W3z+PhHCkUMgpPnThJ9ZDeu+AJrFGWFvHSA6sI7iP/k9LjqkdFXsFezlOOZWNAgB6EH9gCCA4g==",
"path": "microsoft.aspnetcore.cryptography.internal/8.0.8",
"hashPath": "microsoft.aspnetcore.cryptography.internal.8.0.8.nupkg.sha512"
},
"Microsoft.AspNetCore.Cryptography.KeyDerivation/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Ct73TxRAPhQ8OqckzgIUPjJ1/WXnQbOnGjqbvTyPmMptdOg9YJ9wXQh9U+da2s6nki54mcZAyonvaKXLsrXhKQ==",
"path": "microsoft.aspnetcore.cryptography.keyderivation/8.0.8",
"hashPath": "microsoft.aspnetcore.cryptography.keyderivation.8.0.8.nupkg.sha512"
},
"Microsoft.AspNetCore.Identity.EntityFrameworkCore/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-OtjXwZkWdW7UTQ1XR/IGWmeVCujnly0fjjCbUxsjHC1o3cB9iWQRTREdL4931Ulwk9RRgjYQ/mAo7k8ggcZ9xQ==",
"path": "microsoft.aspnetcore.identity.entityframeworkcore/8.0.8",
"hashPath": "microsoft.aspnetcore.identity.entityframeworkcore.8.0.8.nupkg.sha512"
},
"Microsoft.Bcl.AsyncInterfaces/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg==",
"path": "microsoft.bcl.asyncinterfaces/6.0.0",
"hashPath": "microsoft.bcl.asyncinterfaces.6.0.0.nupkg.sha512"
},
"Microsoft.CodeAnalysis.Analyzers/3.3.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ==",
"path": "microsoft.codeanalysis.analyzers/3.3.3",
"hashPath": "microsoft.codeanalysis.analyzers.3.3.3.nupkg.sha512"
},
"Microsoft.CodeAnalysis.Common/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-lwAbIZNdnY0SUNoDmZHkVUwLO8UyNnyyh1t/4XsbFxi4Ounb3xszIYZaWhyj5ZjyfcwqwmtMbE7fUTVCqQEIdQ==",
"path": "microsoft.codeanalysis.common/4.5.0",
"hashPath": "microsoft.codeanalysis.common.4.5.0.nupkg.sha512"
},
"Microsoft.CodeAnalysis.CSharp/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cM59oMKAOxvdv76bdmaKPy5hfj+oR+zxikWoueEB7CwTko7mt9sVKZI8Qxlov0C/LuKEG+WQwifepqL3vuTiBQ==",
"path": "microsoft.codeanalysis.csharp/4.5.0",
"hashPath": "microsoft.codeanalysis.csharp.4.5.0.nupkg.sha512"
},
"Microsoft.CodeAnalysis.CSharp.Workspaces/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-h74wTpmGOp4yS4hj+EvNzEiPgg/KVs2wmSfTZ81upJZOtPkJsVkgfsgtxxqmAeapjT/vLKfmYV0bS8n5MNVP+g==",
"path": "microsoft.codeanalysis.csharp.workspaces/4.5.0",
"hashPath": "microsoft.codeanalysis.csharp.workspaces.4.5.0.nupkg.sha512"
},
"Microsoft.CodeAnalysis.Workspaces.Common/4.5.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-l4dDRmGELXG72XZaonnOeORyD/T5RpEu5LGHOUIhnv+MmUWDY/m1kWXGwtcgQ5CJ5ynkFiRnIYzTKXYjUs7rbw==",
"path": "microsoft.codeanalysis.workspaces.common/4.5.0",
"hashPath": "microsoft.codeanalysis.workspaces.common.4.5.0.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-iK+jrJzkfbIxutB7or808BPmJtjUEi5O+eSM7cLDwsyde6+3iOujCSfWnrHrLxY3u+EQrJD+aD8DJ6ogPA2Rtw==",
"path": "microsoft.entityframeworkcore/8.0.8",
"hashPath": "microsoft.entityframeworkcore.8.0.8.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore.Abstractions/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-9mMQkZsfL1c2iifBD8MWRmwy59rvsVtR9NOezJj7+g1j4P7g49MJHd8k8faC/v7d5KuHkQ6KOQiSItvoRt9PXA==",
"path": "microsoft.entityframeworkcore.abstractions/8.0.8",
"hashPath": "microsoft.entityframeworkcore.abstractions.8.0.8.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore.Analyzers/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-OlAXMU+VQgLz5y5/SBkLvAa9VeiR3dlJqgIebEEH2M2NGA3evm68/Tv7SLWmSxwnEAtA3nmDEZF2pacK6eXh4Q==",
"path": "microsoft.entityframeworkcore.analyzers/8.0.8",
"hashPath": "microsoft.entityframeworkcore.analyzers.8.0.8.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore.Design/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-MmQAMHdjZR8Iyn/FVQrh9weJQTn0HqtKa3vELS9ffQJat/qXgnTam9M9jqvePphjkYp5Scee+Hy+EJR4nmWmOA==",
"path": "microsoft.entityframeworkcore.design/8.0.8",
"hashPath": "microsoft.entityframeworkcore.design.8.0.8.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore.Relational/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-3WnrwdXxKg4L98cDx0lNEEau8U2lsfuBJCs0Yzht+5XVTmahboM7MukKfQHAzVsHUPszm6ci929S7Qas0WfVHA==",
"path": "microsoft.entityframeworkcore.relational/8.0.8",
"hashPath": "microsoft.entityframeworkcore.relational.8.0.8.nupkg.sha512"
},
"Microsoft.EntityFrameworkCore.Tools/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-wjDNbLJk86QpZt2JxJuNVzpBKIbEQsgcJYHGeIFTBuK6NEgvJvyxgneg059HfSJmTVdInZ61lTO4sJGCfFr7+w==",
"path": "microsoft.entityframeworkcore.tools/8.0.8",
"hashPath": "microsoft.entityframeworkcore.tools.8.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Caching.Abstractions/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==",
"path": "microsoft.extensions.caching.abstractions/8.0.0",
"hashPath": "microsoft.extensions.caching.abstractions.8.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Caching.Memory/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-7pqivmrZDzo1ADPkRwjy+8jtRKWRCPag9qPI+p7sgu7Q4QreWhcvbiWXsbhP+yY8XSiDvZpu2/LWdBv7PnmOpQ==",
"path": "microsoft.extensions.caching.memory/8.0.0",
"hashPath": "microsoft.extensions.caching.memory.8.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Configuration.Abstractions/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
"path": "microsoft.extensions.configuration.abstractions/8.0.0",
"hashPath": "microsoft.extensions.configuration.abstractions.8.0.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==",
"path": "microsoft.extensions.dependencyinjection/8.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.8.0.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==",
"path": "microsoft.extensions.dependencyinjection.abstractions/8.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.8.0.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyModel/8.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-5Ou6varcxLBzQ+Agfm0k0pnH7vrEITYlXMDuE6s7ZHlZHz6/G8XJ3iISZDr5rfwfge6RnXJ1+Wc479mMn52vjA==",
"path": "microsoft.extensions.dependencymodel/8.0.1",
"hashPath": "microsoft.extensions.dependencymodel.8.0.1.nupkg.sha512"
},
"Microsoft.Extensions.Identity.Core/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ICzYDXrPFaJWX2c/fp+AvltzVu2LZIe5eK5+JbxndTjGqYEzAAW9jwKSaBq9c3QF0wM9c3SjI80E2Kcn+l9prg==",
"path": "microsoft.extensions.identity.core/8.0.8",
"hashPath": "microsoft.extensions.identity.core.8.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Identity.Stores/8.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-Lmv3ZQMmanFVacoGChUIP+PhyIsDPFvhbiMX4o/BwJtViZw2ofHXn0CwTlytkBk1zW6S39rGpkfrtAWIUzzLZA==",
"path": "microsoft.extensions.identity.stores/8.0.8",
"hashPath": "microsoft.extensions.identity.stores.8.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Logging/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==",
"path": "microsoft.extensions.logging/8.0.0",
"hashPath": "microsoft.extensions.logging.8.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Logging.Abstractions/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==",
"path": "microsoft.extensions.logging.abstractions/8.0.0",
"hashPath": "microsoft.extensions.logging.abstractions.8.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Options/8.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==",
"path": "microsoft.extensions.options/8.0.2",
"hashPath": "microsoft.extensions.options.8.0.2.nupkg.sha512"
},
"Microsoft.Extensions.Primitives/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==",
"path": "microsoft.extensions.primitives/8.0.0",
"hashPath": "microsoft.extensions.primitives.8.0.0.nupkg.sha512"
},
"Mono.TextTemplating/2.2.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-KZYeKBET/2Z0gY1WlTAK7+RHTl7GSbtvTLDXEZZojUdAPqpQNDL6tHv7VUpqfX5VEOh+uRGKaZXkuD253nEOBQ==",
"path": "mono.texttemplating/2.2.1",
"hashPath": "mono.texttemplating.2.2.1.nupkg.sha512"
},
"MySqlConnector/2.3.7": {
"type": "package",
"serviceable": true,
"sha512": "sha512-YiVOxvJ+vAYW8NT9gHv8RxKCDFCSXAObF3z0Ou/8WRv8Lsn2QsxaPW5xEwPE+xCcAq6BGkrI8GTOC09Xg09blQ==",
"path": "mysqlconnector/2.3.7",
"hashPath": "mysqlconnector.2.3.7.nupkg.sha512"
},
"Pomelo.EntityFrameworkCore.MySql/8.0.2": {
"type": "package",
"serviceable": true,
"sha512": "sha512-XjnlcxVBLnEMbyEc5cZzgZeDyLvAniACZQ04W1slWN0f4rmfNzl98gEMvHnFH0fMDF06z9MmgGi/Sr7hJ+BVnw==",
"path": "pomelo.entityframeworkcore.mysql/8.0.2",
"hashPath": "pomelo.entityframeworkcore.mysql.8.0.2.nupkg.sha512"
},
"System.CodeDom/4.4.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-2sCCb7doXEwtYAbqzbF/8UAeDRMNmPaQbU2q50Psg1J9KzumyVVCgKQY8s53WIPTufNT0DpSe9QRvVjOzfDWBA==",
"path": "system.codedom/4.4.0",
"hashPath": "system.codedom.4.4.0.nupkg.sha512"
},
"System.Collections.Immutable/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==",
"path": "system.collections.immutable/6.0.0",
"hashPath": "system.collections.immutable.6.0.0.nupkg.sha512"
},
"System.Composition/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-d7wMuKQtfsxUa7S13tITC8n1cQzewuhD5iDjZtK2prwFfKVzdYtgrTHgjaV03Zq7feGQ5gkP85tJJntXwInsJA==",
"path": "system.composition/6.0.0",
"hashPath": "system.composition.6.0.0.nupkg.sha512"
},
"System.Composition.AttributedModel/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-WK1nSDLByK/4VoC7fkNiFuTVEiperuCN/Hyn+VN30R+W2ijO1d0Z2Qm0ScEl9xkSn1G2MyapJi8xpf4R8WRa/w==",
"path": "system.composition.attributedmodel/6.0.0",
"hashPath": "system.composition.attributedmodel.6.0.0.nupkg.sha512"
},
"System.Composition.Convention/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-XYi4lPRdu5bM4JVJ3/UIHAiG6V6lWWUlkhB9ab4IOq0FrRsp0F4wTyV4Dj+Ds+efoXJ3qbLqlvaUozDO7OLeXA==",
"path": "system.composition.convention/6.0.0",
"hashPath": "system.composition.convention.6.0.0.nupkg.sha512"
},
"System.Composition.Hosting/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-w/wXjj7kvxuHPLdzZ0PAUt++qJl03t7lENmb2Oev0n3zbxyNULbWBlnd5J5WUMMv15kg5o+/TCZFb6lSwfaUUQ==",
"path": "system.composition.hosting/6.0.0",
"hashPath": "system.composition.hosting.6.0.0.nupkg.sha512"
},
"System.Composition.Runtime/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-qkRH/YBaMPTnzxrS5RDk1juvqed4A6HOD/CwRcDGyPpYps1J27waBddiiq1y93jk2ZZ9wuA/kynM+NO0kb3PKg==",
"path": "system.composition.runtime/6.0.0",
"hashPath": "system.composition.runtime.6.0.0.nupkg.sha512"
},
"System.Composition.TypedParts/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-iUR1eHrL8Cwd82neQCJ00MpwNIBs4NZgXzrPqx8NJf/k4+mwBO0XCRmHYJT4OLSwDDqh5nBLJWkz5cROnrGhRA==",
"path": "system.composition.typedparts/6.0.0",
"hashPath": "system.composition.typedparts.6.0.0.nupkg.sha512"
},
"System.IO.Pipelines/6.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw==",
"path": "system.io.pipelines/6.0.3",
"hashPath": "system.io.pipelines.6.0.3.nupkg.sha512"
},
"System.Reflection.Metadata/6.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==",
"path": "system.reflection.metadata/6.0.1",
"hashPath": "system.reflection.metadata.6.0.1.nupkg.sha512"
},
"System.Runtime.CompilerServices.Unsafe/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==",
"path": "system.runtime.compilerservices.unsafe/6.0.0",
"hashPath": "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512"
},
"System.Text.Encoding.CodePages/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==",
"path": "system.text.encoding.codepages/6.0.0",
"hashPath": "system.text.encoding.codepages.6.0.0.nupkg.sha512"
},
"System.Text.Encodings.Web/8.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==",
"path": "system.text.encodings.web/8.0.0",
"hashPath": "system.text.encodings.web.8.0.0.nupkg.sha512"
},
"System.Text.Json/8.0.4": {
"type": "package",
"serviceable": true,
"sha512": "sha512-bAkhgDJ88XTsqczoxEMliSrpijKZHhbJQldhAmObj/RbrN3sU5dcokuXmWJWsdQAhiMJ9bTayWsL1C9fbbCRhw==",
"path": "system.text.json/8.0.4",
"hashPath": "system.text.json.8.0.4.nupkg.sha512"
},
"System.Threading.Channels/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q==",
"path": "system.threading.channels/6.0.0",
"hashPath": "system.threading.channels.6.0.0.nupkg.sha512"
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,20 @@
{
"runtimeOptions": {
"tfm": "net8.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "8.0.0"
},
{
"name": "Microsoft.AspNetCore.App",
"version": "8.0.0"
}
],
"configProperties": {
"System.GC.Server": true,
"System.Reflection.NullabilityInfoContext.IsSupported": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
@@ -0,0 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"Default": "Server=localhost;Database=jobtracker;User=root;Password=08911395;TreatTinyAsBoolean=true;SslMode=None"
}
}

Some files were not shown because too many files have changed in this diff Show More