This commit is contained in:
DmitriyFadin
2026-09-30 15:05:05 +03:00
commit ca18fc196d
122 changed files with 8794 additions and 0 deletions
@@ -0,0 +1,2 @@
@page "/programs/{id:int}/pdf"
@model TrainingPlanner.Web.Pages.ExportModel
@@ -0,0 +1,23 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using TrainingPlanner.Application;
namespace TrainingPlanner.Web.Pages;
[Authorize]
public class ExportModel(IPlanReader reader, IPlanPdfExporter exporter) : PageModel
{
public async Task<IActionResult> OnGetAsync(int id)
{
var trainerId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (trainerId == null) return Challenge();
var plan = await reader.GetAsync(id, trainerId);
if (plan == null) return NotFound();
var client = await reader.GetClientAsync(plan.ClientId, trainerId);
if (client == null) return NotFound();
Response.Headers.CacheControl = "no-store";
return File(exporter.Export(plan, client), "application/pdf", $"training-program-{id}.pdf");
}
}
@@ -0,0 +1,9 @@
@page
@model TrainingPlanner.Web.Pages.LoginModel
<!DOCTYPE html>
<html lang="ru"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="icon" href="/favicon.svg"/><title>Вход · Training Planner</title><link rel="stylesheet" href="/app.css"/></head>
<body class="login-page"><main class="login-card"><div class="brand-mark">TP</div><p class="eyebrow">РАБОЧЕЕ МЕСТО ТРЕНЕРА</p><h1>С возвращением</h1><p class="muted">Войдите, чтобы продолжить работу с программами.</p>
<form method="post"><div asp-validation-summary="All" class="error" role="alert"></div>
<label>Логин<input asp-for="UserName" autocomplete="username" required autofocus/></label>
<label>Пароль<input asp-for="Password" type="password" autocomplete="current-password" required/></label>
<button class="primary" type="submit">Войти →</button></form></main></body></html>
@@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace TrainingPlanner.Web.Pages;
[AllowAnonymous]
public class LoginModel(SignInManager<IdentityUser> signIn) : PageModel
{
[BindProperty] public string UserName { get; set; } = string.Empty;
[BindProperty] public string Password { get; set; } = string.Empty;
public IActionResult OnGet() => User.Identity?.IsAuthenticated == true ? LocalRedirect("/") : Page();
public async Task<IActionResult> OnPostAsync(string? returnUrl)
{
if (!ModelState.IsValid) return Page();
var result = await signIn.PasswordSignInAsync(UserName, Password, false, lockoutOnFailure: true);
if (result.Succeeded)
return LocalRedirect(Url.IsLocalUrl(returnUrl) ? returnUrl! : "/");
ModelState.AddModelError(string.Empty, result.IsLockedOut
? "Слишком много попыток. Попробуйте позже." : "Не удалось войти. Проверьте логин и пароль.");
return Page();
}
}
@@ -0,0 +1,2 @@
@page
@model TrainingPlanner.Web.Pages.LogoutModel
@@ -0,0 +1,17 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace TrainingPlanner.Web.Pages;
[Authorize]
public class LogoutModel(SignInManager<IdentityUser> signIn) : PageModel
{
public IActionResult OnGet() => LocalRedirect("/");
public async Task<IActionResult> OnPostAsync()
{
await signIn.SignOutAsync();
return LocalRedirect("/Login");
}
}
@@ -0,0 +1 @@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers