Revize 587b80b5
Přidáno uživatelem Dominik Chlouba před téměř 4 roky(ů)
docs/Leuze.Core.Application.Identity.xml | ||
---|---|---|
57 | 57 |
|
58 | 58 |
</summary> |
59 | 59 |
</member> |
60 |
<member name="M:Leuze.Core.Application.Identity.ApplicationRole.#ctor(System.String)"> |
|
60 |
<member name="M:Leuze.Core.Application.Identity.ApplicationRole.#ctor(System.String,System.Boolean)">
|
|
61 | 61 |
<summary> |
62 | 62 |
|
63 | 63 |
</summary> |
64 | 64 |
<param name="name"></param> |
65 |
<param name="isDefault"></param> |
|
65 | 66 |
</member> |
66 | 67 |
<member name="P:Leuze.Core.Application.Identity.ApplicationRole.Permissions"> |
67 | 68 |
<summary> |
... | ... | |
96 | 97 |
</summary> |
97 | 98 |
<param name="name"></param> |
98 | 99 |
</member> |
100 |
<member name="P:Leuze.Core.Application.Identity.ApplicationRole.IsDefault"> |
|
101 |
<summary> |
|
102 |
|
|
103 |
</summary> |
|
104 |
</member> |
|
99 | 105 |
<member name="T:Leuze.Core.Application.Identity.ApplicationRoleClaim"> |
100 | 106 |
<summary> |
101 | 107 |
|
... | ... | |
129 | 135 |
|
130 | 136 |
</summary> |
131 | 137 |
</member> |
138 |
<member name="M:Leuze.Core.Application.Identity.ApplicationUser.ChangeEmail(System.String)"> |
|
139 |
<summary> |
|
140 |
|
|
141 |
</summary> |
|
142 |
<param name="email"></param> |
|
143 |
</member> |
|
132 | 144 |
<member name="T:Leuze.Core.Application.Identity.ApplicationUserClaim"> |
133 | 145 |
<summary> |
134 | 146 |
|
src/Core/Application/Leuze.Core.Application.Identity/ApplicationRole.cs | ||
---|---|---|
19 | 19 |
/// |
20 | 20 |
/// </summary> |
21 | 21 |
/// <param name="name"></param> |
22 |
public ApplicationRole(string name) => (Name, NormalizedName) = (name, name.ToUpper()); |
|
22 |
/// <param name="isDefault"></param> |
|
23 |
public ApplicationRole(string name, bool isDefault) => (Name, NormalizedName, IsDefault) = (name, name.ToUpper(), isDefault); |
|
23 | 24 |
|
24 | 25 |
private List<ApplicationPermission> _permissions = new List<ApplicationPermission>(); |
25 | 26 |
|
... | ... | |
57 | 58 |
/// </summary> |
58 | 59 |
/// <param name="name"></param> |
59 | 60 |
public void ChangeName(string name) => (Name, NormalizedName) = (name, name.ToUpper()); |
61 |
|
|
62 |
/// <summary> |
|
63 |
/// |
|
64 |
/// </summary> |
|
65 |
public bool IsDefault { get; private set; } |
|
66 |
|
|
60 | 67 |
} |
61 | 68 |
} |
src/Core/Application/Leuze.Core.Application.Identity/ApplicationUser.cs | ||
---|---|---|
36 | 36 |
/// </summary> |
37 | 37 |
public IReadOnlyCollection<ApplicationRole> Roles => _roles.AsReadOnly(); |
38 | 38 |
|
39 |
/// <summary> |
|
40 |
/// |
|
41 |
/// </summary> |
|
42 |
/// <param name="email"></param> |
|
43 |
public void ChangeEmail(string email) => (UserName, Email) = (email, email); |
|
39 | 44 |
} |
40 | 45 |
} |
src/Core/Application/Leuze.Core.Application.UI/App.razor | ||
---|---|---|
3 | 3 |
<CascadingAuthenticationState> |
4 | 4 |
<Router AppAssembly="@typeof(CoreUIExtension).Assembly" AdditionalAssemblies="LoadAdditionalAssemblies"> |
5 | 5 |
<Found Context="routeData"> |
6 |
<AuthorizeRouteView RouteData="@routeData"/> |
|
6 |
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"> |
|
7 |
<NotAuthorized> |
|
8 |
<RedirectToLogin /> |
|
9 |
</NotAuthorized> |
|
10 |
<Authorizing> |
|
11 |
<p>Just checking with the boss you can come in.</p> |
|
12 |
</Authorizing> |
|
13 |
</AuthorizeRouteView> |
|
7 | 14 |
</Found> |
8 | 15 |
<NotFound> |
9 |
<LayoutView Layout="@typeof(MainLayout)">
|
|
16 |
<LayoutView Layout="@typeof(EmptyLayout)">
|
|
10 | 17 |
<p>Sorry, there's nothing at this address.</p> |
11 | 18 |
</LayoutView> |
12 | 19 |
</NotFound> |
src/Core/Application/Leuze.Core.Application.UI/Areas/Identity/IdentityHostingStartup.cs | ||
---|---|---|
1 |
using Microsoft.AspNetCore.Hosting; |
|
2 |
using Microsoft.Extensions.DependencyInjection; |
|
3 |
|
|
4 |
[assembly: HostingStartup(typeof(Leuze.Core.Application.UI.Areas.Identity.IdentityHostingStartup))] |
|
5 |
namespace Leuze.Core.Application.UI.Areas.Identity |
|
6 |
{ |
|
7 |
/// <summary> |
|
8 |
/// |
|
9 |
/// </summary> |
|
10 |
public class IdentityHostingStartup : IHostingStartup |
|
11 |
{ |
|
12 |
/// <summary> |
|
13 |
/// |
|
14 |
/// </summary> |
|
15 |
/// <param name="builder"></param> |
|
16 |
public void Configure(IWebHostBuilder builder) |
|
17 |
{ |
|
18 |
builder.ConfigureServices((context, services) => services.AddLeuzeCore(context.Configuration, context.HostingEnvironment)); |
|
19 |
builder.Configure((app) => app.UseLeuzeCore()); |
|
20 |
} |
|
21 |
} |
|
22 |
} |
src/Core/Application/Leuze.Core.Application.UI/Areas/Identity/Pages/Account/Login.cshtml | ||
---|---|---|
1 |
@page |
|
2 |
@model LoginModel |
|
3 |
|
|
4 |
@{ |
|
5 |
ViewData["Title"] = "Log in"; |
|
6 |
} |
|
7 |
|
|
8 |
<style> |
|
9 |
.login_leuze_logo { |
|
10 |
height: 45px; |
|
11 |
position: absolute; |
|
12 |
top: 60px; |
|
13 |
left: 120px; |
|
14 |
} |
|
15 |
|
|
16 |
.login_leuze_robot { |
|
17 |
height: 450px; |
|
18 |
position: absolute; |
|
19 |
bottom: 0; |
|
20 |
left: 120px; |
|
21 |
} |
|
22 |
|
|
23 |
@@media and (max-width: 1900px) { |
|
24 |
.login_leuze_logo { |
|
25 |
height: 35px; |
|
26 |
} |
|
27 |
.login_leuze_robot { |
|
28 |
height: 275px; |
|
29 |
} |
|
30 |
} |
|
31 |
|
|
32 |
@@media (max-width: 1400px) { |
|
33 |
.login_leuze_logo { |
|
34 |
left: auto; |
|
35 |
position: relative; |
|
36 |
} |
|
37 |
.login_leuze_robot { |
|
38 |
display: none; |
|
39 |
} |
|
40 |
} |
|
41 |
</style> |
|
42 |
<style> |
|
43 |
.login_form { |
|
44 |
width: 300px; |
|
45 |
height: 357px; |
|
46 |
padding-left: 200px; |
|
47 |
border-left: 1px solid #EDEDED; |
|
48 |
position: absolute; |
|
49 |
top: calc(50% - 178.5px); |
|
50 |
right: 200px; |
|
51 |
} |
|
52 |
.login_row { |
|
53 |
width: 100%; |
|
54 |
margin-bottom: 24px; |
|
55 |
} |
|
56 |
.login_label { |
|
57 |
width: 100%; |
|
58 |
color: #9D9D9D; |
|
59 |
font-size: 14px; |
|
60 |
text-transform: uppercase; |
|
61 |
font-weight: 700; |
|
62 |
font-family: 'Arial'; |
|
63 |
} |
|
64 |
.login_input { |
|
65 |
width: calc(100% - 24px); |
|
66 |
padding: 10px 12px; |
|
67 |
color: #111111; |
|
68 |
font-size: 16px; |
|
69 |
font-weight: 700; |
|
70 |
font-family: 'Arial'; |
|
71 |
border-radius: 8px; |
|
72 |
border: 1px solid #DADADA; |
|
73 |
outline: none; |
|
74 |
margin-top: 6px; |
|
75 |
} |
|
76 |
.login_button { |
|
77 |
width: 100%; |
|
78 |
padding: 12px; |
|
79 |
color: #E30613; |
|
80 |
font-size: 14px; |
|
81 |
font-weight: 700; |
|
82 |
font-family: 'Arial'; |
|
83 |
border-radius: 8px; |
|
84 |
border: 2px solid #E30613; |
|
85 |
text-transform: uppercase; |
|
86 |
outline: none; |
|
87 |
background-color: white; |
|
88 |
margin-top: 12px; |
|
89 |
} |
|
90 |
.login_or { |
|
91 |
width: 100%; |
|
92 |
color: #DADADA; |
|
93 |
font-size: 14px; |
|
94 |
font-weight: 700; |
|
95 |
font-family: 'Arial'; |
|
96 |
text-transform: uppercase; |
|
97 |
position: relative; |
|
98 |
text-align: center; |
|
99 |
margin: 24px 0; |
|
100 |
} |
|
101 |
.login_or::before { |
|
102 |
content: ''; |
|
103 |
width: 100%; |
|
104 |
height: 1px; |
|
105 |
background-color: #EDEDED; |
|
106 |
left: 0; |
|
107 |
top: 10px; |
|
108 |
position: absolute; |
|
109 |
} |
|
110 |
.login_orText { |
|
111 |
background-color: white; |
|
112 |
width: fit-content; |
|
113 |
margin: 0 auto; |
|
114 |
position: relative; |
|
115 |
padding: 0 12px; |
|
116 |
} |
|
117 |
.login_adWrapper { |
|
118 |
width: 100%; |
|
119 |
text-align: center; |
|
120 |
} |
|
121 |
.login_microsoft { |
|
122 |
width: 24px; |
|
123 |
height: 24px; |
|
124 |
} |
|
125 |
@@media (max-width: 1400px) { |
|
126 |
.login_form { |
|
127 |
width: 400px; |
|
128 |
right: calc(50% - 200px); |
|
129 |
border-left: none; |
|
130 |
padding-left: 0; |
|
131 |
} |
|
132 |
} |
|
133 |
|
|
134 |
@@media (max-width: 450px) { |
|
135 |
.login_form { |
|
136 |
width: 90%; |
|
137 |
position: relative; |
|
138 |
top: 0; |
|
139 |
right: 5%; |
|
140 |
left: 5%; |
|
141 |
margin-top: 150px; |
|
142 |
padding-bottom: 24px; |
|
143 |
height: auto; |
|
144 |
} |
|
145 |
} |
|
146 |
</style> |
|
147 |
|
|
148 |
<img class="login_leuze_logo" src="/Resources/Icons/logo.svg" /> |
|
149 |
<img class="login_leuze_robot" src="/Resources/Icons/login-robot.svg" /> |
|
150 |
|
|
151 |
<form id="account" method="post" class="login_form"> |
|
152 |
<div class="login_row"> |
|
153 |
<label class="login_label" asp-for="Input.Email">Přihlašovací jméno</label> |
|
154 |
<input class="login_input" type="email" asp-for="Input.Email" /> |
|
155 |
</div> |
|
156 |
<div class="login_row"> |
|
157 |
<label class="login_label" asp-for="Input.Password">Heslo</label> |
|
158 |
<input class="login_input" type="password" asp-for="Input.Password" /> |
|
159 |
</div> |
|
160 |
<p>@Model.ErrorMessage</p> |
|
161 |
<button type="submit" class="login_button">Přihlásit se</button> |
|
162 |
<div class="login_or"> |
|
163 |
<div class="login_orText">nebo</div> |
|
164 |
</div> |
|
165 |
<div class="login_adWrapper"> |
|
166 |
<a href="/MicrosoftIdentity/Account/SignIn"> |
|
167 |
<img class="login_microsoft" src="/Resources/Icons/microsoft-logo.svg" /> |
|
168 |
</a> |
|
169 |
</div> |
|
170 |
</form> |
src/Core/Application/Leuze.Core.Application.UI/Areas/Identity/Pages/Account/Login.cshtml.cs | ||
---|---|---|
1 |
using System; |
|
2 |
using System.Collections.Generic; |
|
3 |
using System.ComponentModel.DataAnnotations; |
|
4 |
using System.Linq; |
|
5 |
using System.Threading.Tasks; |
|
6 |
using Leuze.Core.Application.Identity; |
|
7 |
using Microsoft.AspNetCore.Authentication; |
|
8 |
using Microsoft.AspNetCore.Authorization; |
|
9 |
using Microsoft.AspNetCore.Identity; |
|
10 |
using Microsoft.AspNetCore.Mvc; |
|
11 |
using Microsoft.AspNetCore.Mvc.RazorPages; |
|
12 |
using Microsoft.Extensions.Logging; |
|
13 |
|
|
14 |
namespace Leuze.Core.Application.UI.Areas.Pages.Account |
|
15 |
{ |
|
16 |
/// <summary> |
|
17 |
/// |
|
18 |
/// </summary> |
|
19 |
[AllowAnonymous] |
|
20 |
public class LoginModel : PageModel |
|
21 |
{ |
|
22 |
private readonly UserManager<ApplicationUser> _userManager; |
|
23 |
private readonly SignInManager<ApplicationUser> _signInManager; |
|
24 |
private readonly ILogger<LoginModel> _logger; |
|
25 |
|
|
26 |
/// <summary> |
|
27 |
/// |
|
28 |
/// </summary> |
|
29 |
/// <param name="signInManager"></param> |
|
30 |
/// <param name="logger"></param> |
|
31 |
/// <param name="userManager"></param> |
|
32 |
public LoginModel(SignInManager<ApplicationUser> signInManager, |
|
33 |
ILogger<LoginModel> logger, |
|
34 |
UserManager<ApplicationUser> userManager) |
|
35 |
=> (_userManager, _signInManager, _logger) = (userManager, signInManager, logger); |
|
36 |
|
|
37 |
/// <summary> |
|
38 |
/// |
|
39 |
/// </summary> |
|
40 |
[BindProperty] |
|
41 |
public InputModel Input { get; set; } = new(); |
|
42 |
|
|
43 |
/// <summary> |
|
44 |
/// |
|
45 |
/// </summary> |
|
46 |
public string ReturnUrl { get; set; } = null!; |
|
47 |
|
|
48 |
/// <summary> |
|
49 |
/// |
|
50 |
/// </summary> |
|
51 |
[TempData] |
|
52 |
public string ErrorMessage { get; set; } = null!; |
|
53 |
|
|
54 |
/// <summary> |
|
55 |
/// |
|
56 |
/// </summary> |
|
57 |
public class InputModel |
|
58 |
{ |
|
59 |
/// <summary> |
|
60 |
/// |
|
61 |
/// </summary> |
|
62 |
[Required] |
|
63 |
[EmailAddress] |
|
64 |
public string Email { get; set; } = string.Empty; |
|
65 |
|
|
66 |
/// <summary> |
|
67 |
/// |
|
68 |
/// </summary> |
|
69 |
[Required] |
|
70 |
[DataType(DataType.Password)] |
|
71 |
public string Password { get; set; } = string.Empty; |
|
72 |
} |
|
73 |
|
|
74 |
/// <summary> |
|
75 |
/// |
|
76 |
/// </summary> |
|
77 |
/// <param name="returnUrl"></param> |
|
78 |
/// <returns></returns> |
|
79 |
public async Task OnGetAsync(string returnUrl = null!) |
|
80 |
{ |
|
81 |
if (!string.IsNullOrEmpty(ErrorMessage)) |
|
82 |
{ |
|
83 |
ModelState.AddModelError(string.Empty, ErrorMessage); |
|
84 |
} |
|
85 |
|
|
86 |
returnUrl ??= Url.Content("~/")!; |
|
87 |
|
|
88 |
// Clear the existing external cookie to ensure a clean login process |
|
89 |
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); |
|
90 |
|
|
91 |
ReturnUrl = returnUrl; |
|
92 |
} |
|
93 |
|
|
94 |
/// <summary> |
|
95 |
/// |
|
96 |
/// </summary> |
|
97 |
/// <param name="returnUrl"></param> |
|
98 |
/// <returns></returns> |
|
99 |
public async Task<IActionResult> OnPostAsync(string? returnUrl = null!) |
|
100 |
{ |
|
101 |
returnUrl ??= Url.Content("~/")!; |
|
102 |
|
|
103 |
if (ModelState.IsValid) |
|
104 |
{ |
|
105 |
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, true, false); |
|
106 |
if (result.Succeeded) |
|
107 |
{ |
|
108 |
_logger.LogInformation("User logged in."); |
|
109 |
return LocalRedirect(returnUrl); |
|
110 |
} |
|
111 |
else |
|
112 |
{ |
|
113 |
ModelState.AddModelError(string.Empty, "Invalid login attempt."); |
|
114 |
ErrorMessage = "Invalid email or password"; |
|
115 |
return Page(); |
|
116 |
} |
|
117 |
} |
|
118 |
|
|
119 |
// If we got this far, something failed, redisplay form |
|
120 |
return Page(); |
|
121 |
} |
|
122 |
} |
|
123 |
} |
src/Core/Application/Leuze.Core.Application.UI/Areas/Identity/Pages/Shared/_Layout.cshtml | ||
---|---|---|
1 |
<!DOCTYPE html> |
|
2 |
<html> |
|
3 |
<head> |
|
4 |
<meta charset="utf-8" /> |
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|
6 |
<title>@ViewData["Title"] - BlazorLogin</title> |
|
7 |
</head> |
|
8 |
<body> |
|
9 |
<div class="container"> |
|
10 |
<main role="main" class="pb-3"> |
|
11 |
@RenderBody() |
|
12 |
</main> |
|
13 |
</div> |
|
14 |
@RenderSection("Scripts", required: false) |
|
15 |
</body> |
|
16 |
</html> |
src/Core/Application/Leuze.Core.Application.UI/Areas/Identity/Pages/_ViewImports.cshtml | ||
---|---|---|
1 |
@using Microsoft.AspNetCore.Identity |
|
2 |
@using Leuze.Core.Application.UI.Areas.Pages.Account |
|
3 |
@using Leuze.Core.Application.UI.Areas.Pages |
|
4 |
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers |
src/Core/Application/Leuze.Core.Application.UI/Areas/Identity/Pages/_ViewStart.cshtml | ||
---|---|---|
1 |
|
|
2 |
@{ |
|
3 |
Layout = "/Areas/Identity/Pages/Shared/_Layout.cshtml"; |
|
4 |
} |
src/Core/Application/Leuze.Core.Application.UI/Components/Login/Form.razor | ||
---|---|---|
1 |
|
|
1 |
@inject IMediator _mediator |
|
2 |
@inject NavigationManager _navManager |
|
3 |
|
|
2 | 4 |
<Styled @bind-Classname="@_form"> |
3 |
width: 504px;
|
|
5 |
width: 300px;
|
|
4 | 6 |
height: 357px; |
5 | 7 |
padding-left: 200px; |
6 | 8 |
border-left: 1px solid #EDEDED; |
... | ... | |
37 | 39 |
font-family: 'Arial'; |
38 | 40 |
</Styled> |
39 | 41 |
<Styled @bind-Classname="@_input"> |
40 |
width: 100%;
|
|
42 |
width: calc(100% - 24px);
|
|
41 | 43 |
padding: 10px 12px; |
42 | 44 |
color: #111111; |
43 | 45 |
font-size: 16px; |
... | ... | |
46 | 48 |
border-radius: 8px; |
47 | 49 |
border: 1px solid #DADADA; |
48 | 50 |
outline: none; |
51 |
margin-top: 6px; |
|
49 | 52 |
</Styled> |
50 | 53 |
<Styled @bind-Classname="@_button"> |
51 | 54 |
width: 100%; |
... | ... | |
97 | 100 |
height: 24px; |
98 | 101 |
</Styled> |
99 | 102 |
|
100 |
<form class="@_form"> |
|
103 |
<form @onsubmit="Login" class="@_form">
|
|
101 | 104 |
<div class="@_row"> |
102 | 105 |
<label class="@_label">Přihlašovací jméno</label> |
103 |
<input class="@_input" /> |
|
106 |
<input class="@_input" type="email" @bind-value="Email" />
|
|
104 | 107 |
</div> |
105 | 108 |
<div class="@_row"> |
106 | 109 |
<label class="@_label">Heslo</label> |
107 |
<input class="@_input" /> |
|
110 |
<input class="@_input" type="password" @bind-value="Password" /> |
|
111 |
</div> |
|
112 |
<p>@Error</p> |
|
113 |
<button @onclick="Login" class="@_button" @onkeypress:preventDefault>Přihlásit se</button> |
|
114 |
<div class="@_or"> |
|
115 |
<div class="@_orText">nebo</div> |
|
116 |
</div> |
|
117 |
<div class="@_adWrapper"> |
|
118 |
<a href="MicrosoftIdentity/Account/SignIn"> |
|
119 |
<img class="@_microsoft" src="/Resources/Icons/microsoft-logo.svg" /> |
|
120 |
</a> |
|
108 | 121 |
</div> |
109 |
<button class="@_button">Přihlásit se</button> |
|
110 |
|
|
111 |
|
|
112 |
<div class="@_or"> |
|
113 |
<div class="@_orText">nebo</div> |
|
114 |
</div> |
|
115 |
<div class="@_adWrapper"> |
|
116 |
<a href="MicrosoftIdentity/Account/SignIn"> |
|
117 |
<img class="@_microsoft" src="/Resources/Icons/microsoft-logo.svg" /> |
|
118 |
</a> |
|
119 |
</div> |
|
120 |
|
|
121 | 122 |
</form> |
122 | 123 |
|
123 | 124 |
@code { |
... | ... | |
131 | 132 |
private string _adWrapper = null!; |
132 | 133 |
private string _microsoft = null!; |
133 | 134 |
|
134 |
/// <summary>
|
|
135 |
///
|
|
136 |
/// </summary> |
|
137 |
//public List<AuthenticationScheme> ExternalLogins { get; set; } = new();
|
|
135 |
private string Email { get; set; } = string.Empty;
|
|
136 |
private string Password { get; set; } = string.Empty;
|
|
137 |
|
|
138 |
private string Error { get; set; } = string.Empty;
|
|
138 | 139 |
|
139 |
/// <summary> |
|
140 |
/// |
|
141 |
/// </summary> |
|
142 |
/// <returns></returns> |
|
143 |
/* protected override async Task OnInitializedAsync() |
|
140 |
private async Task Login() |
|
144 | 141 |
{ |
145 |
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); |
|
146 |
}*/ |
|
142 |
var result = await _mediator.Send(new VerifyLocalUser.Command(Email, Password)); |
|
143 |
|
|
144 |
if (!result.IsSuccess) Error = result.Errors.First(); |
|
145 |
else _navManager.NavigateTo("/"); |
|
146 |
|
|
147 |
} |
|
148 |
|
|
147 | 149 |
} |
src/Core/Application/Leuze.Core.Application.UI/Leuze.Core.Application.UI.csproj | ||
---|---|---|
22 | 22 |
</PropertyGroup> |
23 | 23 |
|
24 | 24 |
<ItemGroup> |
25 |
<PackageReference Include="Blazored.Toast" Version="3.1.2" /> |
|
25 | 26 |
<PackageReference Include="BlazorStyled" Version="3.1.0" /> |
26 | 27 |
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.2.0" /> |
28 |
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="5.0.2" /> |
|
27 | 29 |
</ItemGroup> |
28 | 30 |
|
29 | 31 |
<ItemGroup> |
src/Core/Application/Leuze.Core.Application.UI/Pages/Login.razor | ||
---|---|---|
1 |
@page "/Account/Login"
|
|
1 |
@page "/Identity/Account/ResetPassword"
|
|
2 | 2 |
@attribute [AllowAnonymous] |
3 | 3 |
@layout EmptyLayout |
4 | 4 |
<Styled> |
src/Core/Application/Leuze.Core.Application.UI/Pages/Testing/User.razor | ||
---|---|---|
1 |
@page "/Testing" |
|
2 |
@attribute [AllowAnonymous] |
|
3 |
@layout EmptyLayout |
|
4 |
@inject IMediator _mediator |
|
5 |
<h3>Testovací GUI pro tvorbu lokálních uživatelů</h3> |
|
6 |
|
|
7 |
<button @onclick="CreateUser">Vytvořit uživatele test@test.cz</button> |
|
8 |
<br/> |
|
9 |
Token: @Token |
|
10 |
<br/> |
|
11 |
<button @onclick="SetPassword">Nastavit heslo Test123*</button> |
|
12 |
|
|
13 |
@code { |
|
14 |
|
|
15 |
protected string Token { get; set; } |
|
16 |
|
|
17 |
protected async Task CreateUser() |
|
18 |
{ |
|
19 |
var result = await _mediator.Send(new CreateLocalUser.Command("Testovací Uživatel", "test@test.cz", null!, new List<Guid>())); |
|
20 |
Token = result.Result!.Token; |
|
21 |
} |
|
22 |
|
|
23 |
protected async Task SetPassword() |
|
24 |
{ |
|
25 |
await _mediator.Send(new ResetLocalUserPassword.Command("test@test.cz", Token, "Test123*")); |
|
26 |
} |
|
27 |
|
|
28 |
} |
src/Core/Application/Leuze.Core.Application.UI/Pages/Users/Components/UserAside.razor | ||
---|---|---|
1 |
@inject IMediator _mediator |
|
2 |
@inject IToastService toastService |
|
3 |
|
|
4 |
<aside class="@_aside side_dialog"> |
|
5 |
<div class="dialog_header"> |
|
6 |
<div>Nový uživatel</div> |
|
7 |
<button @onclick="Close" type="button">X</button> |
|
8 |
</div> |
|
9 |
<div class="dialog_body"> |
|
10 |
@if (UserId != default(Guid) && Loading) |
|
11 |
{ |
|
12 |
<div>Načítám uživatele</div> |
|
13 |
} |
|
14 |
else |
|
15 |
{ |
|
16 |
<div class="input_row"> |
|
17 |
<label class="input_label">Jméno a příjmení<span style="color:#ff0000">*</span></label> |
|
18 |
<input type="text" placeholder="Jméno a příjmení" disabled="@IsAdUser" @bind-value="Name" /> |
|
19 |
</div> |
|
20 |
<div class="input_row"> |
|
21 |
<label class="input_label">E-mailová adresa<span style="color:#ff0000">*</span></label> |
|
22 |
<input type="email" placeholder="E-mail" @bind-value="Email" /> |
|
23 |
</div> |
|
24 |
<div class="section_title">Role<span style="color:#ff0000">*</span></div> |
|
25 |
<div class="input_row"> |
|
26 |
@if (Roles is null) |
|
27 |
{ |
|
28 |
<div>Načítám role</div> |
|
29 |
} |
|
30 |
else |
|
31 |
{ |
|
32 |
@foreach (var role in Roles) |
|
33 |
{ |
|
34 |
<label class="container" @onclick="() => SetRole(role.Id)"> |
|
35 |
@role.Name |
|
36 |
<input type="checkbox" checked="@(Role == role.Id)"> |
|
37 |
<span class="checkmark"></span> |
|
38 |
</label> |
|
39 |
} |
|
40 |
@if (Roles.Count == 0) |
|
41 |
{ |
|
42 |
<div>Nenalezeny žádné role</div> |
|
43 |
} |
|
44 |
} |
|
45 |
</div> |
|
46 |
<div class="section_title">Nadřízený</div> |
|
47 |
<div class="input_row"> |
|
48 |
@if (Users is null) |
|
49 |
{ |
|
50 |
<div>Načítám uživatele</div> |
|
51 |
} |
|
52 |
else |
|
53 |
{ |
|
54 |
<select @onchange="(e) => SetUser((e.Value != null && (string)e.Value != string.Empty) ? Guid.Parse((string)e.Value) : default(Guid))"> |
|
55 |
<option value="" selected="@(Senior == default(Guid))">Vyberte uživatele</option> |
|
56 |
|
|
57 |
@foreach (var user in Users) |
|
58 |
{ |
|
59 |
<option value="@user.Id" selected="@(Senior == user.Id)">@user.Name</option> |
|
60 |
} |
|
61 |
</select> |
|
62 |
} |
|
63 |
</div> |
|
64 |
} |
|
65 |
</div> |
|
66 |
<div class="button_container"> |
|
67 |
<button @onclick="FireAction" disabled="@(IsValidModel() || Sent)">@(UserId == default(Guid) ? "Vytvořit" : "Uložit")</button> |
|
68 |
<button @onclick="Close">Zrušit</button> |
|
69 |
</div> |
|
70 |
</aside> |
|
71 |
|
|
72 |
<Styled @bind-Classname="@_aside"> |
|
73 |
right: @(Show ? "0" : "-525")px; |
|
74 |
</Styled> |
|
75 |
<Styled> |
|
76 |
.side_dialog { |
|
77 |
width: 500px; |
|
78 |
height: 100%; |
|
79 |
position: fixed; |
|
80 |
background-color: #FFFFFF; |
|
81 |
z-index: 1; |
|
82 |
top: 0; |
|
83 |
right: -525px; |
|
84 |
transition: .25s right; |
|
85 |
-webkit-box-shadow: 0 0 6px 0 rgba(0,0,0,0.1); |
|
86 |
-moz-box-shadow: 0 0 6px 0 rgba(0,0,0,0.1); |
|
87 |
box-shadow: 0 0 6px 0 rgba(0,0,0,0.1); |
|
88 |
} |
|
89 |
.dialog_body { |
|
90 |
background-color: #FFFFFF; |
|
91 |
width: calc(100% - 72px); |
|
92 |
height: calc(100% - 216px); |
|
93 |
padding: 36px; |
|
94 |
} |
|
95 |
.side_dialog .button_container { |
|
96 |
width: calc(100% - 72px); |
|
97 |
padding: 12px 36px; |
|
98 |
height: 60px; |
|
99 |
background-color: #F8F8F8; |
|
100 |
display: grid; |
|
101 |
grid-template-columns: repeat(2, 1fr); |
|
102 |
column-gap: 24px; |
|
103 |
align-items: center; |
|
104 |
} |
|
105 |
.side_dialog .button_container button { |
|
106 |
border: 1px solid #111111; |
|
107 |
border-radius: 4px; |
|
108 |
color: #111111; |
|
109 |
padding: 12px 16px; |
|
110 |
background-color: #F8F8F8; |
|
111 |
font-size: 14px; |
|
112 |
font-weight: 400; |
|
113 |
cursor: pointer; |
|
114 |
transition: .25s background-color, .25s color, .25s border; |
|
115 |
} |
|
116 |
.side_dialog .button_container button:hover { |
|
117 |
background-color: #EEEEEE; |
|
118 |
border: 1px solid #EEEEEE; |
|
119 |
color: #111111; |
|
120 |
} |
|
121 |
.side_dialog .button_container button:first-child { |
|
122 |
border: 1px solid #E30613; |
|
123 |
color: #E30613; |
|
124 |
} |
|
125 |
.side_dialog .button_container button:first-child:hover { |
|
126 |
background-color: #E30613; |
|
127 |
color: #FFFFFF; |
|
128 |
} |
|
129 |
.side_dialog .dialog_header { |
|
130 |
height: 36px; |
|
131 |
padding: 12px 36px; |
|
132 |
width: calc(100% - 72px); |
|
133 |
border-bottom: 1px solid #F8F8F8; |
|
134 |
display: flex; |
|
135 |
justify-content: space-between; |
|
136 |
align-items: center; |
|
137 |
} |
|
138 |
.side_dialog .dialog_header div { |
|
139 |
font-size: 20px; |
|
140 |
} |
|
141 |
.side_dialog .dialog_header button { |
|
142 |
border: none; |
|
143 |
background: none; |
|
144 |
cursor: pointer; |
|
145 |
} |
|
146 |
|
|
147 |
.input_row { |
|
148 |
margin-bottom: 24px; |
|
149 |
} |
|
150 |
.input_row input:not([type="checkbox"]) { |
|
151 |
width: calc(100% - 34px); |
|
152 |
padding: 12px 16px; |
|
153 |
border-radius: 4px; |
|
154 |
border: 1px solid #C8C8C8; |
|
155 |
font-size: 14px; |
|
156 |
} |
|
157 |
.input_row label.input_label { |
|
158 |
font-size: 16px; |
|
159 |
font-weight: 700; |
|
160 |
color: #111111; |
|
161 |
padding-bottom: 4px; |
|
162 |
display: block; |
|
163 |
} |
|
164 |
.section_title { |
|
165 |
font-size: 16px; |
|
166 |
font-weight: 700; |
|
167 |
color: #111111; |
|
168 |
padding-bottom: 4px; |
|
169 |
display: block; |
|
170 |
} |
|
171 |
.container { |
|
172 |
display: block; |
|
173 |
position: relative; |
|
174 |
padding-left: 25px; |
|
175 |
margin-bottom: 12px; |
|
176 |
cursor: pointer; |
|
177 |
font-size: 16px; |
|
178 |
-webkit-user-select: none; |
|
179 |
-moz-user-select: none; |
|
180 |
-ms-user-select: none; |
|
181 |
user-select: none; |
|
182 |
} |
|
183 |
.container input { |
|
184 |
position: absolute; |
|
185 |
opacity: 0; |
|
186 |
cursor: pointer; |
|
187 |
height: 0; |
|
188 |
width: 0; |
|
189 |
} |
|
190 |
.checkmark { |
|
191 |
position: absolute; |
|
192 |
top: 2px; |
|
193 |
left: 0; |
|
194 |
height: 15px; |
|
195 |
width: 15px; |
|
196 |
background-color: #eee; |
|
197 |
border-radius: 50%; |
|
198 |
} |
|
199 |
.container:hover input ~ .checkmark { |
|
200 |
background-color: #ccc; |
|
201 |
} |
|
202 |
.container input:checked ~ .checkmark { |
|
203 |
background-color: #E30613; |
|
204 |
} |
|
205 |
.checkmark:after { |
|
206 |
content: ""; |
|
207 |
position: absolute; |
|
208 |
display: none; |
|
209 |
} |
|
210 |
.container input:checked ~ .checkmark:after { |
|
211 |
display: block; |
|
212 |
} |
|
213 |
.container .checkmark:after { |
|
214 |
top: 5px; |
|
215 |
left: 5px; |
|
216 |
width: 5px; |
|
217 |
height: 5px; |
|
218 |
border-radius: 50%; |
|
219 |
background: white; |
|
220 |
} |
|
221 |
</Styled> |
|
222 |
|
|
223 |
@code { |
|
224 |
private string _aside; |
|
225 |
|
|
226 |
private bool Sent { get; set; } = false; |
|
227 |
|
|
228 |
private bool Loading { get; set; } = true; |
|
229 |
|
|
230 |
private List<RoleItem> Roles { get; set; } = null!; |
|
231 |
|
|
232 |
private List<UserShortDto> Users { get; set; } = null!; |
|
233 |
|
|
234 |
private string Name { get; set; } = string.Empty; |
|
235 |
|
|
236 |
private string Email { get; set; } = string.Empty; |
|
237 |
|
|
238 |
private Guid Role { get; set; } = default(Guid); |
|
239 |
|
|
240 |
private Guid Senior { get; set; } = default(Guid); |
|
241 |
|
|
242 |
private bool IsAdUser { get; set; } = false; |
|
243 |
|
|
244 |
[Parameter] |
|
245 |
public Guid UserId { get; set; } = default(Guid); |
|
246 |
|
|
247 |
[Parameter] |
|
248 |
public bool Show { get; set; } = false; |
|
249 |
|
|
250 |
[Parameter] |
|
251 |
public EventCallback ToggleUserAside { get; set; } |
|
252 |
|
|
253 |
[Parameter] |
|
254 |
public EventCallback RefetchUsers { get; set; } |
|
255 |
|
|
256 |
protected override async Task OnInitializedAsync() |
|
257 |
{ |
|
258 |
await base.OnInitializedAsync(); |
|
259 |
await FetchRoles(); |
|
260 |
await FetchUsers(); |
|
261 |
} |
|
262 |
|
|
263 |
protected override async Task OnParametersSetAsync() |
|
264 |
{ |
|
265 |
await base.OnParametersSetAsync(); |
|
266 |
await FetchUser(); |
|
267 |
} |
|
268 |
|
|
269 |
private async Task Close() |
|
270 |
{ |
|
271 |
ResetValues(); |
|
272 |
await ToggleUserAside.InvokeAsync(); |
|
273 |
} |
|
274 |
|
|
275 |
private void ResetValues() |
|
276 |
{ |
|
277 |
Name = string.Empty; |
|
278 |
Email = string.Empty; |
|
279 |
Role = default(Guid); |
|
280 |
} |
|
281 |
|
|
282 |
private async Task FetchUser() |
|
283 |
{ |
|
284 |
var result = await _mediator.Send(new GetUserDetail.Query(UserId)); |
|
285 |
if (result.IsSuccess && result.Result is not null) |
|
286 |
{ |
|
287 |
Name = result.Result.User.Name; |
|
288 |
Email = result.Result.User.Email; |
|
289 |
Role = result.Result.User.Role.Id; |
|
290 |
Loading = false; |
|
291 |
} |
|
292 |
} |
|
293 |
|
|
294 |
private async Task FetchUsers() |
|
295 |
{ |
|
296 |
var result = await _mediator.Send(new GetUsersList.Query(false)); |
|
297 |
if (result.IsSuccess && result.Result is not null) |
|
298 |
{ |
|
299 |
if (UserId == default(Guid)) Users = result.Result.List; |
|
300 |
else Users = result.Result.List.Where(o => o.Id != UserId).ToList(); |
|
301 |
} |
|
302 |
} |
|
303 |
|
|
304 |
private async Task FetchRoles() |
|
305 |
{ |
|
306 |
var result = await _mediator.Send(new GetRolesList.Query()); |
|
307 |
if (result.IsSuccess && result.Result is not null) Roles = result.Result.List; |
|
308 |
} |
|
309 |
|
|
310 |
private void SetRole(Guid id) => Role = id; |
|
311 |
|
|
312 |
private void SetUser(Guid id) => Senior = id; |
|
313 |
|
|
314 |
private bool IsValidModel() => Email == string.Empty || Role == default(Guid) || Name == string.Empty; |
|
315 |
|
|
316 |
private async Task FireAction() |
|
317 |
{ |
|
318 |
if (UserId == default(Guid)) |
|
319 |
{ |
|
320 |
var result = await _mediator.Send(new CreateLocalUser.Command(Name, Email, Senior == default(Guid) ? null! : Senior, new List<Guid>() { Role })); |
|
321 |
if (result.IsSuccess) |
|
322 |
{ |
|
323 |
toastService.ShowSuccess("Uživatel úspěšně vytvořen"); |
|
324 |
} |
|
325 |
else toastService.ShowError("Uživatele se nepodařilo vytvořit"); |
|
326 |
} |
|
327 |
else |
|
328 |
{ |
|
329 |
var result = await _mediator.Send(new UpdateUser.Command(UserId, Name, Email, Senior == default(Guid) ? null! : Senior, new List<Guid>() { Role })); |
|
330 |
if (result.IsSuccess) |
|
331 |
{ |
|
332 |
toastService.ShowSuccess("Uživatel úspěšně vytvořen"); |
|
333 |
} |
|
334 |
else toastService.ShowError("Uživatele se nepodařilo uložit"); |
|
335 |
} |
|
336 |
ResetValues(); |
|
337 |
await ToggleUserAside.InvokeAsync(); |
|
338 |
await RefetchUsers.InvokeAsync(); |
|
339 |
} |
|
340 |
|
|
341 |
} |
src/Core/Application/Leuze.Core.Application.UI/Pages/Users/List.razor | ||
---|---|---|
1 |
@page "/Users" |
|
2 |
@attribute [Authorize] |
|
3 |
@layout MainLayout |
|
4 |
@inject IMediator _mediator |
|
5 |
<div class="section_header"> |
|
6 |
<h2>Správa uživatelů</h2> |
|
7 |
<button type="button" @onclick="() => SetUserAsideId(default(Guid))"> |
|
8 |
<img src="/Resources/Icons/plus.svg" class="plus_icon" /> |
|
9 |
<span>Přidat uživatele</span> |
|
10 |
</button> |
|
11 |
</div> |
|
12 |
@if (Users is not null) |
|
13 |
{ |
|
14 |
<table class="actions" style="width:100%"> |
|
15 |
<tr> |
|
16 |
<th>Jméno</th> |
|
17 |
<th>Email</th> |
|
18 |
<th>Role</th> |
|
19 |
<th>Typ</th> |
|
20 |
<th>Akce</th> |
|
21 |
</tr> |
|
22 |
@foreach (var user in Users) |
|
23 |
{ |
|
24 |
<tr> |
|
25 |
<td>@user.Name</td> |
|
26 |
<td>@user.Email</td> |
|
27 |
<td>@user.Role</td> |
|
28 |
<td>@(user.IsAdUser ? "AD" : "Lokální")</td> |
|
29 |
<td> |
|
30 |
<div @onclick="() => SetUserAsideId(user.Id)">Upravit</div> |
|
31 |
</td> |
|
32 |
</tr> |
|
33 |
} |
|
34 |
</table> |
|
35 |
@if (Users.Count > 0) |
|
36 |
{ |
|
37 |
<div class="pagination"> |
|
38 |
<div class="left"> |
|
39 |
<button class="pagination_button"><img class="pagination_icon" src="/Resources/Icons/chevron-double-left.svg"/></button> |
|
40 |
<button class="pagination_button"><img class="pagination_icon" src="/Resources/Icons/chevron-left.svg" /></button> |
|
41 |
<input type="number" @bind-value="PageNumber" /> |
|
42 |
<button class="pagination_button"><img class="pagination_icon" src="/Resources/Icons/chevron-right.svg" /></button> |
|
43 |
<button class="pagination_button"><img class="pagination_icon" src="/Resources/Icons/chevron-double-right.svg" /></button> |
|
44 |
</div> |
|
45 |
<div class="right">Zobrazeno <b>@Users.Count z @Total</b></div> |
|
46 |
</div> |
|
47 |
} |
|
48 |
} |
|
49 |
else |
|
50 |
{ |
|
51 |
<div>Loading...</div> |
|
52 |
} |
|
53 |
<UserAside Show="ShowUserAside" ToggleUserAside="@ToggleUserAside" UserId="UserAsideId" RefetchUsers="FetchUsers"/> |
|
54 |
|
|
55 |
<Styled> |
|
56 |
.section_header { |
|
57 |
display: flex; |
|
58 |
justify-content: space-between; |
|
59 |
align-items: center; |
|
60 |
} |
|
61 |
.section_header h2 { |
|
62 |
color: #111111; |
|
63 |
font-size: 36px; |
|
64 |
margin: 0; |
|
65 |
} |
|
66 |
.section_header button { |
|
67 |
border-radius: 4px; |
|
68 |
border: none; |
|
69 |
background-color: #E30613; |
|
70 |
padding: 12px 16px; |
|
71 |
color: #FFFFFF; |
|
72 |
display: flex; |
|
73 |
align-items: center; |
|
74 |
cursor: pointer; |
|
75 |
transition: .25s opacity; |
|
76 |
outline: none; |
|
77 |
} |
|
78 |
.section_header button:hover { |
|
79 |
opacity: .6; |
|
80 |
} |
|
81 |
.section_header button .plus_icon { |
|
82 |
width: 18px; |
|
83 |
height: 18px; |
|
84 |
margin-right: 16px; |
|
85 |
} |
|
86 |
.section_header button span { |
|
87 |
position: relative; |
|
88 |
top: 1px; |
|
89 |
} |
|
90 |
</Styled> |
|
91 |
<Styled> |
|
92 |
table { |
|
93 |
border-collapse: collapse; |
|
94 |
margin-top: 60px !important; |
|
95 |
} |
|
96 |
table tr th { text-align: left; } |
|
97 |
table.actions tr th:last-child, table.actions tr td:last-child { text-align: right; } |
|
98 |
table tr { |
|
99 |
border-bottom: 1px solid #EDEDED; |
|
100 |
} |
|
101 |
table tr:first-child { border-bottom: 1px solid #9D9D9D55; } |
|
102 |
table tr th, table tr td { |
|
103 |
padding: 12px 8px; |
|
104 |
} |
|
105 |
table tr:not(:first-child):hover { |
|
106 |
background-color: #EDEDED55; |
|
107 |
} |
|
108 |
</Styled> |
|
109 |
<Styled> |
|
110 |
input::-webkit-outer-spin-button, |
|
111 |
input::-webkit-inner-spin-button { |
|
112 |
-webkit-appearance: none; |
|
113 |
margin: 0; |
|
114 |
} |
|
115 |
input[type=number] { |
|
116 |
-moz-appearance: textfield; |
|
117 |
} |
|
118 |
.pagination { |
|
119 |
width: 100%; |
|
120 |
display: flex; |
|
121 |
justify-content: space-between; |
|
122 |
align-items: center; |
|
123 |
margin-top: 36px; |
|
124 |
} |
|
125 |
.pagination .left { |
|
126 |
display: flex; |
|
127 |
align-items: center; |
|
128 |
} |
|
129 |
.pagination button { |
|
130 |
width: 34px; |
|
131 |
height: 34px; |
|
132 |
border: 1px solid #EEEEEE; |
|
133 |
border-radius: 4px; |
|
134 |
background-color: #FFFFFF; |
|
135 |
cursor: pointer; |
|
136 |
outline: none; |
|
137 |
margin-right: 6px; |
|
138 |
display: flex; |
|
139 |
justify-content: center; |
|
140 |
align-items: center; |
|
141 |
} |
|
142 |
.pagination input { |
|
143 |
width: 30px; |
|
144 |
height: 30px; |
|
145 |
padding: 1px 12px; |
|
146 |
outline: none; |
|
147 |
margin-right: 6px; |
|
148 |
border: 1px solid #EEEEEE; |
|
149 |
border-radius: 4px; |
|
150 |
} |
|
151 |
.pagination_icon { |
|
152 |
width: 18px; |
|
153 |
height: 18px; |
|
154 |
} |
|
155 |
</Styled> |
|
156 |
|
|
157 |
@code { |
|
158 |
|
|
159 |
private Guid UserAsideId { get; set; } = default(Guid); |
|
160 |
|
|
161 |
private List<UserDto> Users { get; set; } = null!; |
|
162 |
|
|
163 |
private int PageNumber { get; set; } = 1; |
|
164 |
|
|
165 |
private int PageSize { get; set; } = 12; |
|
166 |
|
|
167 |
private int Total { get; set; } = 0; |
|
168 |
|
|
169 |
private bool ShowUserAside { get; set; } = false; |
|
170 |
|
|
171 |
protected override async Task OnInitializedAsync() |
|
172 |
{ |
|
173 |
await base.OnInitializedAsync(); |
|
174 |
await FetchUsers(); |
|
175 |
} |
|
176 |
|
|
177 |
private void ToggleUserAside() => ShowUserAside = !ShowUserAside; |
|
178 |
|
|
179 |
private void SetUserAsideId(Guid id) |
|
180 |
{ |
|
181 |
UserAsideId = id; |
|
182 |
ToggleUserAside(); |
|
183 |
StateHasChanged(); |
|
184 |
} |
|
185 |
|
|
186 |
private async Task FetchUsers() |
|
187 |
{ |
|
188 |
var result = await _mediator.Send(new GetFilteredUsers.Query(PageNumber, PageSize)); |
|
189 |
|
|
190 |
if (result.IsSuccess && result.Result is not null) |
|
191 |
{ |
|
192 |
Users = result.Result.List; |
|
193 |
Total = result.Result.Total; |
|
194 |
} |
|
195 |
} |
|
196 |
|
|
197 |
} |
src/Core/Application/Leuze.Core.Application.UI/Pages/_Host.cshtml | ||
---|---|---|
6 | 6 |
} |
7 | 7 |
|
8 | 8 |
<!DOCTYPE html> |
9 |
<html lang="en">
|
|
9 |
<html lang="cs">
|
|
10 | 10 |
<head> |
11 | 11 |
<meta charset="utf-8" /> |
12 | 12 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
13 |
<title>Leuze.App</title>
|
|
13 |
<title>Leuze</title> |
|
14 | 14 |
<base href="~/" /> |
15 |
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" /> |
|
16 | 15 |
<link href="css/site.css" rel="stylesheet" /> |
16 |
<link href="_content/Blazored.Toast/blazored-toast.min.css" rel="stylesheet" /> |
|
17 | 17 |
<link href="Leuze.App.styles.css" rel="stylesheet" /> |
18 | 18 |
</head> |
19 | 19 |
<body> |
20 | 20 |
<component type="typeof(App)" render-mode="ServerPrerendered" /> |
21 |
|
|
22 |
<div id="blazor-error-ui"> |
|
23 |
<environment include="Staging,Production"> |
|
24 |
An error has occurred. This application may no longer respond until reloaded. |
|
25 |
</environment> |
|
26 |
<environment include="Development"> |
|
27 |
An unhandled exception has occurred. See browser dev tools for details. |
|
28 |
</environment> |
|
29 |
<a href="" class="reload">Reload</a> |
|
30 |
<a class="dismiss">🗙</a> |
|
31 |
</div> |
|
32 |
|
|
33 | 21 |
<script src="_framework/blazor.server.js"></script> |
34 | 22 |
</body> |
35 | 23 |
</html> |
src/Core/Application/Leuze.Core.Application.UI/Shared/Components/RedirectToLogin.cs | ||
---|---|---|
1 |
using Microsoft.AspNetCore.Components; |
|
2 |
|
|
3 |
namespace Leuze.Core.Application.UI.Shared.Components |
|
4 |
{ |
|
5 |
public class RedirectToLogin : ComponentBase |
|
6 |
{ |
|
7 |
[Inject] |
|
8 |
protected NavigationManager NavigationManager { get; set; } |
|
9 |
|
|
10 |
protected override void OnInitialized() |
|
11 |
{ |
|
12 |
NavigationManager.NavigateTo("/Identity/Account/Login"); |
|
13 |
} |
|
14 |
} |
|
15 |
} |
src/Core/Application/Leuze.Core.Application.UI/Shared/MainLayout.razor | ||
---|---|---|
1 | 1 |
@inherits LayoutComponentBase |
2 | 2 |
@inject NavigationManager _navManager |
3 | 3 |
@inject AuthenticationStateProvider AuthenticationStateProvider |
4 |
<div class="top-row px-4"> |
|
5 |
<img src="Resources/Icons/logo.svg" alt="logo" style="width:111px;height:28px;" /> |
|
4 |
@using Blazored.Toast.Configuration |
|
5 |
|
|
6 |
<BlazoredToasts Position="ToastPosition.BottomRight" |
|
7 |
Timeout="10" |
|
8 |
IconType="IconType.FontAwesome" |
|
9 |
SuccessIcon="fa fa-thumbs-up" |
|
10 |
ErrorIcon="fa fa-bug" /> |
|
11 |
<header class="@_header"> |
|
12 |
<img class="leuze_logo" src="/Resources/Icons/logo.svg" /> |
|
6 | 13 |
<div> |
7 |
@Name |
|
14 |
<span class="username">@Name</span> |
|
15 |
<img class="user_icon" src="/Resources/Icons/user-circle.svg" /> |
|
8 | 16 |
<a href="/MicrosoftIdentity/Account/SignOut"> |
9 |
<img src="Resources/Icons/sign-out.svg" style="width:18px;height:20px;" />
|
|
17 |
<img class="signout_icon" src="/Resources/Icons/sign-out.svg" />
|
|
10 | 18 |
</a> |
11 | 19 |
</div> |
12 |
</div> |
|
13 |
|
|
20 |
</header> |
|
21 |
<NavMenu /> |
|
22 |
<main class="@_main">@Body</main> |
|
14 | 23 |
|
15 |
<div class="page"> |
|
16 |
<div class="sidebar"> |
|
17 |
<NavMenu /> |
|
18 |
</div> |
|
19 |
|
|
20 |
<div class="main"> |
|
21 |
<div class="content px-4"> |
|
22 |
@Body |
|
23 |
</div> |
|
24 |
</div> |
|
25 |
</div> |
|
24 |
<Styled @bind-Classname="@_header"> |
|
25 |
width: calc(100% - 84px); |
|
26 |
height: 71px; |
|
27 |
background-color: #FFFFFF; |
|
28 |
border-bottom: 1px solid #EDEDED; |
|
29 |
padding: 0 48px 0 36px; |
|
30 |
position: fixed; |
|
31 |
top: 0; |
|
32 |
left: 0; |
|
33 |
display: flex; |
|
34 |
justify-content: space-between; |
|
35 |
align-items: center; |
|
36 |
& > div { |
|
37 |
display: flex; |
|
38 |
justify-content: space-between; |
|
39 |
align-items: center; |
|
40 |
} |
|
41 |
& .leuze_logo { |
|
42 |
height: 28.24px; |
|
43 |
} |
|
44 |
& .username { |
|
45 |
color: #111111; |
|
46 |
font-size: 18px; |
|
47 |
} |
|
48 |
& .user_icon { |
|
49 |
width: 48px; |
|
50 |
height: 48px; |
|
51 |
margin: 0 48px 0 16px; |
|
52 |
} |
|
53 |
& .signout_icon { |
|
54 |
width: 26px; |
|
55 |
height: 22px; |
|
56 |
} |
|
57 |
</Styled> |
|
58 |
<Styled @bind-Classname="@_main"> |
|
59 |
width: calc(100% - 420px); |
|
60 |
min-height: calc(100% - 168px); |
|
61 |
background-color: transparent; |
|
62 |
position: absolute; |
|
63 |
top: 72px; |
|
64 |
left: 276px; |
|
65 |
padding: 48px 72px; |
|
66 |
</Styled> |
|
26 | 67 |
|
27 |
@code { |
|
68 |
@code { |
|
69 |
private string _main = null!; |
|
70 |
private string _header = null!; |
|
28 | 71 |
|
29 | 72 |
private string Name { get; set; } = string.Empty; |
30 | 73 |
|
src/Core/Application/Leuze.Core.Application.UI/Shared/NavMenu.razor | ||
---|---|---|
1 |
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu"> |
|
2 |
<ul class="nav flex-column"> |
|
3 |
<li class="nav-item"> |
|
4 |
<NavLink class="nav-link" href="" Match="NavLinkMatch.All"> |
|
5 |
<img src="/Resources/Icons/analytics.svg" width="13" height="11"/> Přehled |
|
6 |
</NavLink> |
|
7 |
</li> |
|
8 |
<li class="nav-item"> |
|
9 |
<NavLink class="nav-link" href="goals"> |
|
10 |
<img src="/Resources/Icons/clipboard-list-check.svg" width="13" height="11" /> Správa cílů |
|
11 |
</NavLink> |
|
12 |
</li> |
|
13 |
<li class="nav-item"> |
|
14 |
<NavLink class="nav-link" href="benefits"> |
|
15 |
<img src="/Resources/Icons/award.svg" width="13" height="11" /> Benefity |
|
16 |
</NavLink> |
|
17 |
</li> |
|
18 |
<li class="nav-item"> |
|
19 |
<NavLink class="nav-link" href="users"> |
|
20 |
<img src="/Resources/Icons/user.svg" width="13" height="11" /> Uživatelé |
|
21 |
</NavLink> |
|
22 |
</li> |
|
23 |
<li class="nav-item"> |
|
24 |
<NavLink class="nav-link" href="authorization"> |
|
25 |
<img src="/Resources/Icons/balance-scale.svg" width="13" height="11" /> Role a práva |
|
26 |
</NavLink> |
|
27 |
</li> |
|
28 |
</ul> |
|
29 |
</div> |
|
1 |
<aside class="@_aside"> |
|
2 |
<nav class="@_nav"> |
|
3 |
<NavLink href="/dashboard"> |
|
4 |
<div class="item"> |
|
5 |
<img src="/Resources/Icons/analytics.svg" class="item_icon nav_icon_1" /> |
|
6 |
<span>Přehled</span> |
|
7 |
</div> |
|
8 |
</NavLink> |
|
9 |
<NavLink href="/goals"> |
|
10 |
<div class="item"> |
|
11 |
<img src="/Resources/Icons/clipboard-list-check.svg" class="item_icon nav_icon_2" /> |
|
12 |
<span>Správa cílů</span> |
|
13 |
</div> |
|
14 |
</NavLink> |
|
15 |
<NavLink href="/users"> |
|
16 |
<div class="item"> |
|
17 |
<img src="/Resources/Icons/user.svg" class="item_icon nav_icon_3" /> |
|
18 |
<span>Uživatelé</span> |
|
19 |
</div> |
|
20 |
</NavLink> |
|
21 |
</nav> |
|
22 |
</aside> |
|
30 | 23 |
|
31 |
@code { |
|
32 |
private bool collapseNavMenu = true; |
|
33 |
|
|
34 |
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; |
|
35 |
|
|
36 |
private void ToggleNavMenu() |
|
37 |
{ |
|
38 |
collapseNavMenu = !collapseNavMenu; |
|
24 |
<Styled @bind-Classname="@_aside"> |
|
25 |
width: 275px; |
|
26 |
height: calc(100% - 72px); |
|
27 |
position: fixed; |
|
28 |
left: 0; |
|
29 |
top: 72px; |
|
30 |
background-color: #FFFFFF; |
|
31 |
border-right: 1px solid #EDEDED; |
|
32 |
</Styled> |
|
33 |
<Styled @bind-Classname="@_nav"> |
|
34 |
margin-top: 36px; |
|
35 |
& .item { |
|
36 |
padding: 20px 36px; |
|
37 |
width: calc(100% - 76px); |
|
38 |
border-left: 4px solid #FFFFFF; |
|
39 |
display: flex; |
|
40 |
align-items: center; |
|
41 |
background-color: #FFFFFF; |
|
42 |
transition: .25s background-color, .25s border-left-color; |
|
43 |
cursor: pointer; |
|
44 |
} |
|
45 |
& .item:hover { |
|
46 |
background-color: #FFFAFA; |
|
47 |
border-left: 4px solid #E30613; |
|
48 |
} |
|
49 |
& .item_active { |
|
50 |
background-color: #FFFAFA; |
|
51 |
border-left: 4px solid #E30613; |
|
52 |
} |
|
53 |
& .item > span { |
|
54 |
color: #111111; |
|
55 |
font-size: 18px; |
|
56 |
margin-left: 16px; |
|
57 |
position: relative; |
|
58 |
top: 2px; |
|
39 | 59 |
} |
40 |
} |
|
60 |
</Styled> |
|
61 |
<Styled> |
|
62 |
.nav_icon_1 { |
|
63 |
width: 20px; |
|
64 |
height: 18px; |
|
65 |
} |
|
66 |
.nav_icon_2 { |
|
67 |
width: 20px; |
|
68 |
height: 26px; |
|
69 |
} |
|
70 |
.nav_icon_3 { |
|
71 |
width: 20px; |
|
72 |
height: 22px; |
|
73 |
} |
|
74 |
</Styled> |
|
75 |
|
|
76 |
@code { |
|
77 |
private string _aside = null!; |
|
78 |
private string _nav = null!; |
|
79 |
} |
src/Core/Application/Leuze.Core.Application.UI/_Imports.razor | ||
---|---|---|
10 | 10 |
@using BlazorStyled |
11 | 11 |
@using Microsoft.AspNetCore.Authentication |
12 | 12 |
@using Microsoft.AspNetCore.Identity |
13 |
@using Leuze.Core.Application.Identity |
|
14 |
@using Leuze.Core.Application.UI.Components.Login |
|
13 |
@using Leuze.Core.Application.Identity |
|
14 |
@using Leuze.Core.Application.UI.Components.Login |
|
15 |
@using MediatR |
|
16 |
@using Leuze.Core.Application.CQRS.Users.Commands |
|
17 |
@using Leuze.Core.Application.CQRS.Auth.Commands |
|
18 |
@using Leuze.Core.Domain.Domains.Users.DTOs |
|
19 |
@using Leuze.Core.Application.CQRS.Users.Queries |
|
20 |
@using Leuze.Core.Application.UI.Shared.Components |
|
21 |
@using Leuze.Core.Application.UI.Pages.Users.Components |
Také k dispozici: Unified diff
Bug fixes, creating and updating user, role seed
refs #8763 @0.5h