Api controller using cookie auth
This commit is contained in:
parent
05925a1d96
commit
a45c212592
|
@ -4,9 +4,9 @@
|
|||
[submodule "include/Math-Expression-Evaluator"]
|
||||
path = include/Math-Expression-Evaluator
|
||||
url = git@git.codeliturgy.com:P0/Math-Expression-Evaluator.git
|
||||
[submodule "BlueWest.Frontend"]
|
||||
path = BlueWest.Frontend
|
||||
url = git@git.codeliturgy.com:P0/BlueWest.Frontend.git
|
||||
[submodule "CodeLiturgy.Dashboard.Frontend"]
|
||||
path = CodeLiturgy.Dashboard.Frontend
|
||||
url = git@git.codeliturgy.com:P0/CodeLiturgy.Dashboard.Frontend.git
|
||||
[submodule "include/BlueWest.EfMethods"]
|
||||
path = include/BlueWest.EfMethods
|
||||
url = git@git.codeliturgy.com:P0/BlueWest.EfGenerator.git
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 8b2e541cfe92c7bfaada6a95e427a3d6c92ce768
|
|
@ -0,0 +1 @@
|
|||
Subproject commit d8e4df2c5d247b02accbbd5c668c4427145806d3
|
|
@ -12,7 +12,8 @@ namespace BlueWest.Data.Auth
|
|||
/// <summary>
|
||||
/// API User policy Key
|
||||
/// </summary>
|
||||
public const string ApiNamePolicy = "ApiUser";
|
||||
public const string ApiNamePolicy = "Bearer";
|
||||
public const string CookieNamePolicy = "Cookies";
|
||||
public const string SessionTokenHeaderName = "x-bw2-auth";
|
||||
public const string CookieDomain = "https://localhost:7022";
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ namespace BlueWest.Data.Auth.Context.Users;
|
|||
public static class Constants
|
||||
{
|
||||
|
||||
public const string CorsPolicyName = "_myAllowSpecificOrigins";
|
||||
|
||||
/// <summary>
|
||||
/// JwtClaimIdentifiers
|
||||
/// </summary>
|
||||
|
@ -21,4 +23,14 @@ public static class Constants
|
|||
/// </summary>
|
||||
public const string ApiAccess = "api_access";
|
||||
}
|
||||
|
||||
public static class CookieClaims
|
||||
{
|
||||
/// <summary>
|
||||
/// JwtClaims.ApiAccess
|
||||
/// </summary>
|
||||
public const string CookieAccess = "cookie_access";
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
namespace CodeLiturgy.Data.Capital;
|
||||
|
||||
public enum EnvironmentType
|
||||
{
|
||||
Development,
|
||||
Staging,
|
||||
Production
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using MapTo;
|
||||
|
||||
namespace CodeLiturgy.Data.Capital
|
||||
{
|
||||
|
||||
[MapFrom(new []
|
||||
{
|
||||
typeof(SiteUnique),
|
||||
typeof(SiteCreate)
|
||||
})]
|
||||
public partial class Site
|
||||
{
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Domain { get; set; }
|
||||
|
||||
public string Ip { get; set; }
|
||||
|
||||
public DateTime CreatedDate { get; set; }
|
||||
|
||||
public DateTime LastChanged { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
using MapTo;
|
||||
|
||||
namespace CodeLiturgy.Data.Capital
|
||||
{
|
||||
[MapFrom(typeof(Site))]
|
||||
public partial class SiteCreate
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Domain { get; set; }
|
||||
|
||||
public string Ip { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using MapTo;
|
||||
|
||||
namespace CodeLiturgy.Data.Capital
|
||||
{
|
||||
|
||||
[MapFrom(typeof(Site))]
|
||||
public partial class SiteUnique
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Domain { get; set; }
|
||||
|
||||
public string Ip { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -53,7 +53,6 @@ namespace CodeLiturgy.Domain
|
|||
{
|
||||
base.OnModelCreating(builder);
|
||||
|
||||
|
||||
builder.ConfigureCurrentDbModel();
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
using CodeLiturgy.Data.Capital;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using BlueWest.EfMethods;
|
||||
|
||||
namespace CodeLiturgy.Domain;
|
||||
|
||||
public class SiteDbContext: DbContext
|
||||
{
|
||||
[EfGetOneBy(nameof(Site.Id), typeof(SiteUnique))]
|
||||
[EfGetOne(typeof(SiteUnique))]
|
||||
[EfAddMethods(createType: typeof(SiteCreate), returnType: typeof(SiteUnique))]
|
||||
[EfGetMany(typeof(SiteUnique))]
|
||||
|
||||
public DbSet<Site> Sites { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SiteDbContext Constructor.
|
||||
/// </summary>
|
||||
/// <param name="options"></param>
|
||||
public SiteDbContext(DbContextOptions<SiteDbContext> options) : base(options)
|
||||
{
|
||||
Database.EnsureCreated();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<Nullable>disable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
@ -79,10 +79,10 @@
|
|||
<_ContentIncludedByDefault Remove="wwwroot\lib\jquery\dist\jquery.min.map" />
|
||||
<_ContentIncludedByDefault Remove="wwwroot\lib\jquery\LICENSE.txt" />
|
||||
<_ContentIncludedByDefault Remove="Views\Data\Roles\Index.cshtml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="Views\Account\Index.cshtml" />
|
||||
<_ContentIncludedByDefault Remove="Views\Account\ChangePassword.cshtml" />
|
||||
<_ContentIncludedByDefault Remove="Views\Account\Curriculums\Index.cshtml" />
|
||||
<_ContentIncludedByDefault Remove="Views\Account\Curriculums\Upload.cshtml" />
|
||||
<_ContentIncludedByDefault Remove="Views\Account\Index.cshtml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -109,4 +109,9 @@
|
|||
<Folder Include="wwwroot\static\profile" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="Views\Auth\Account.cshtml" />
|
||||
<AdditionalFiles Include="Views\EnvironmentsPage\Index.cshtml" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -12,7 +12,6 @@ public class AccountController : UserController
|
|||
{
|
||||
private readonly ILogger<AccountController> _logger;
|
||||
|
||||
|
||||
public AccountController(ApplicationUserManager userManager, ILogger<AccountController> logger) : base(userManager, logger)
|
||||
{
|
||||
_userManager = userManager;
|
||||
|
@ -27,19 +26,9 @@ public class AccountController : UserController
|
|||
|
||||
public override void OnInitialization()
|
||||
{
|
||||
SetFooterMenu(LayoutCache.AccountRouteRecord.Children);
|
||||
SetFooterMenu(LayoutCache.AccountRouteRecord.ChildrenToUrl());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<IActionResult> Curriculums()
|
||||
{
|
||||
await OnEveryAction();
|
||||
|
||||
return View("Curriculums/Index");
|
||||
}
|
||||
|
||||
|
||||
public async Task<IActionResult> Change()
|
||||
{
|
||||
await OnEveryAction();
|
||||
|
@ -47,14 +36,6 @@ public class AccountController : UserController
|
|||
}
|
||||
|
||||
|
||||
[Microsoft.AspNetCore.Mvc.Route("[controller]/upload")]
|
||||
public async Task<IActionResult> UploadCurriculum()
|
||||
{
|
||||
await OnEveryAction();
|
||||
return View("Curriculums/Upload");
|
||||
}
|
||||
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public async Task<IActionResult> Error()
|
||||
{
|
||||
|
|
|
@ -62,6 +62,13 @@ namespace CodeLiturgy.Views.Controllers
|
|||
return View();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Account()
|
||||
{
|
||||
await OnEveryAction();
|
||||
this.HandleGlobalization();
|
||||
return View();
|
||||
}
|
||||
|
||||
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
using BlueWest.Data.Auth.Context.Users;
|
||||
using CodeLiturgy.Data.Capital;
|
||||
using CodeLiturgy.Domain;
|
||||
using CodeLiturgy.Views.Utils;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
|
||||
namespace CodeLiturgy.Views.Controllers
|
||||
{
|
||||
[Route("api/envs")]
|
||||
[ApiController]
|
||||
[Authorize]
|
||||
|
||||
public class EnvironmentsController : ControllerBase
|
||||
{
|
||||
private ApplicationUserManager _userManager;
|
||||
private ILogger<UserController> _logger;
|
||||
private SiteDbContext _siteDbContext;
|
||||
|
||||
public EnvironmentsController(ApplicationUserManager userManager, ILogger<UserController> logger, SiteDbContext siteDbContext)
|
||||
{
|
||||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
_siteDbContext = siteDbContext;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<Site>> GetSites()
|
||||
{
|
||||
return Ok(new Site());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
using BlueWest.Data.Auth.Context.Users;
|
||||
using CodeLiturgy.Views.Utils;
|
||||
using Duende.IdentityServer.Extensions;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace CodeLiturgy.Views.Controllers;
|
||||
|
||||
|
||||
public class EnvironmentsPageController : UserController
|
||||
{
|
||||
public EnvironmentsPageController(ApplicationUserManager userManager, ILogger<UserController> logger) : base(userManager, logger) { }
|
||||
|
||||
public override void OnInitialization()
|
||||
{
|
||||
base.OnInitialization();
|
||||
SetHeaderMenu(new List<Url>()
|
||||
{
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
await OnEveryAction();
|
||||
|
||||
|
||||
if (!User.IsAuthenticated())
|
||||
{
|
||||
return Redirect("/auth/login");
|
||||
}
|
||||
|
||||
|
||||
return View();
|
||||
}
|
||||
}
|
|
@ -18,6 +18,12 @@ public class HomeController : UserController
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public override void OnInitialization()
|
||||
{
|
||||
var menu = LayoutCache.Root.ChildrenToUrl();
|
||||
SetHeaderMenu(menu);
|
||||
}
|
||||
|
||||
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> Index()
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace CodeLiturgy.Views.Controllers
|
|||
|
||||
public override void OnInitialization()
|
||||
{
|
||||
SetFooterMenu(LayoutCache.SystemRoute.Children);
|
||||
SetFooterMenu(LayoutCache.SystemRoute.ChildrenToUrl());
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Users()
|
||||
|
|
|
@ -15,22 +15,24 @@ public class UserController : Controller
|
|||
protected ILogger<UserController> _logger;
|
||||
protected ApplicationUserManager _userManager;
|
||||
|
||||
private List<RouteRecord> _footerMenu;
|
||||
private List<Url> _footerMenu;
|
||||
private List<Url> _headerMenu;
|
||||
|
||||
public UserController(ApplicationUserManager userManager, ILogger<UserController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_userManager = userManager;
|
||||
_footerMenu = new List<RouteRecord>();
|
||||
_footerMenu = new List<Url>();
|
||||
_headerMenu = new List<Url>();
|
||||
}
|
||||
|
||||
public async Task OnEveryAction()
|
||||
{
|
||||
HandleGlobalization();
|
||||
SetFooterMenuViewData();
|
||||
await SetUserProfileViewData();
|
||||
OnInitialization();
|
||||
SetFooterMenuViewData();
|
||||
SetHeaderMenuViewData();
|
||||
}
|
||||
|
||||
public virtual void OnInitialization()
|
||||
|
@ -42,9 +44,21 @@ public class UserController : Controller
|
|||
ViewData[FooterMenuViewDataId] = _footerMenu;
|
||||
}
|
||||
|
||||
public void SetFooterMenu(List<RouteRecord> routeRecords)
|
||||
protected void SetHeaderMenuViewData()
|
||||
{
|
||||
_footerMenu = routeRecords;
|
||||
ViewData[HeaderMenuId] = _headerMenu;
|
||||
}
|
||||
|
||||
|
||||
public void SetFooterMenu(List<Url> urls)
|
||||
{
|
||||
_footerMenu = urls;
|
||||
}
|
||||
|
||||
|
||||
public void SetHeaderMenu(List<Url> urls)
|
||||
{
|
||||
_headerMenu = urls;
|
||||
}
|
||||
|
||||
public async Task SetUserProfileViewData()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
namespace CodeLiturgy.Views.Languages;
|
||||
|
||||
public static class SiteContent
|
||||
public static class Translations
|
||||
{
|
||||
internal static readonly Dictionary<string, Dictionary<string, string>> RouteTitle =
|
||||
new Dictionary<string, Dictionary<string, string>>
|
||||
|
@ -21,14 +21,6 @@ public static class SiteContent
|
|||
{"en-gb", "System"},
|
||||
}
|
||||
},
|
||||
{
|
||||
DataKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Dados"},
|
||||
{"eng", "Data"},
|
||||
{"en-gb", "Data"}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
RolesKeyName, new Dictionary<string, string>()
|
||||
|
@ -62,68 +54,23 @@ public static class SiteContent
|
|||
{"en-gb", "Settings"},
|
||||
}
|
||||
},
|
||||
{
|
||||
CompaniesKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Empresas"},
|
||||
{"eng", "Companies"},
|
||||
{"en-gb", "Companies"},
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
IndustriesKeyName, new Dictionary<string, string>()
|
||||
AuthAccountKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Indústrias"},
|
||||
{"en-gb", "Industries"},
|
||||
{"eng", "Industries"},
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
CurrenciesKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Moedas"},
|
||||
{"en-gb", "Currencies"},
|
||||
{"eng", "Currencies"},
|
||||
}
|
||||
},
|
||||
{
|
||||
CountriesKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Países"},
|
||||
{"eng", "Countries"},
|
||||
{"en-gb", "Countries"}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
BanksKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Bancos"},
|
||||
{"eng", "Banks"},
|
||||
{"en-gb", "Banks"},
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
DataUsersKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Utilizadores"},
|
||||
{"eng", "Users"},
|
||||
{"en-gb", "Users"},
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
AccountKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Perfil"},
|
||||
{"pt", "Conta"},
|
||||
{"eng", "Account"},
|
||||
{"en-gb", "Account"},
|
||||
}
|
||||
},
|
||||
{
|
||||
EnvironmentsKeyName, new Dictionary<string, string>()
|
||||
{
|
||||
{"pt", "Ambientes"},
|
||||
{"eng", "Environments"},
|
||||
{"en-gb", "Environments"},
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
ChangePasswordKeyName, new Dictionary<string, string>()
|
|
@ -112,6 +112,24 @@ public static class StartupExtensions
|
|||
{
|
||||
options.LoginPath = Routes.AuthLoginRoute;
|
||||
options.LogoutPath = Routes.AuthLogoutRoute;
|
||||
})
|
||||
.AddJwtBearer(configureOptions =>
|
||||
{
|
||||
configureOptions.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
|
||||
configureOptions.TokenValidationParameters = tokenValidationParameters;
|
||||
configureOptions.SaveToken = true;
|
||||
configureOptions.Events = new JwtBearerEvents
|
||||
{
|
||||
OnAuthenticationFailed = context =>
|
||||
{
|
||||
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
|
||||
{
|
||||
context.Response.Headers.Add("Token-Expired", "true");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
|
@ -121,6 +139,10 @@ public static class StartupExtensions
|
|||
options.AddPolicy(SessionConstants.ApiNamePolicy,
|
||||
policy => policy.RequireClaim(Constants.JwtClaimIdentifiers.Rol,
|
||||
Constants.JwtClaims.ApiAccess));
|
||||
options.AddPolicy(SessionConstants.CookieNamePolicy, policy =>
|
||||
{
|
||||
policy.RequireClaim(Constants.CookieClaims.CookieAccess);
|
||||
});
|
||||
});
|
||||
|
||||
// add identity
|
||||
|
@ -168,6 +190,7 @@ public static class StartupExtensions
|
|||
{
|
||||
return serviceCollection
|
||||
.AddDbContextPool<UserDbContext>(options => options.UsePsqlConfiguration(configuration))
|
||||
.AddDbContextPool<SiteDbContext>(options => options.UsePsqlConfiguration(configuration))
|
||||
.AddDbContextPool<CountryDbContext>(options => options.UsePsqlConfiguration(configuration))
|
||||
.AddDbContextPool<ApplicationUserDbContext>(options =>
|
||||
options.UsePsqlConfiguration(configuration));
|
||||
|
|
|
@ -10,16 +10,22 @@ internal class LayoutCache
|
|||
#region Route Tree
|
||||
|
||||
private static readonly RouteRecord NonLoggedInRoot = new RouteRecord(
|
||||
"Login",
|
||||
AuthLoginRoute,
|
||||
AuthLoginKeyName,
|
||||
nameof(AuthController), new List<RouteRecord>());
|
||||
|
||||
|
||||
private static readonly RouteRecord Root = new RouteRecord(
|
||||
internal static readonly RouteRecord Root = new RouteRecord(
|
||||
"Code Liturgy - Dashboard",
|
||||
RootKeyName,
|
||||
RootLocation,
|
||||
nameof(HomeController),
|
||||
new List<RouteRecord>(), ViewType.Root);
|
||||
new List<RouteRecord>()
|
||||
{
|
||||
new RouteRecord("Environments", EnvironmentsKeyName, EnvironmentsRouteLocation, nameof(EnvironmentsPageController), new List<RouteRecord>(), ViewType.Environments)
|
||||
|
||||
}, ViewType.Root);
|
||||
|
||||
#endregion Route Tree
|
||||
|
||||
|
@ -28,9 +34,6 @@ internal class LayoutCache
|
|||
internal static readonly RouteRecord SystemRoute =
|
||||
Root.Children.FirstOrDefault(x => x.ViewType == ViewType.System)!;
|
||||
|
||||
internal static readonly RouteRecord DataRouteRecord =
|
||||
Root.Children.FirstOrDefault(x => x.ViewType == ViewType.Data)!;
|
||||
|
||||
internal static readonly RouteRecord AccountRouteRecord =
|
||||
Root.Children.FirstOrDefault(x => x.ViewType == ViewType.Account)!;
|
||||
|
||||
|
@ -38,72 +41,50 @@ internal class LayoutCache
|
|||
|
||||
#region Internal Menus
|
||||
|
||||
internal static List<RouteView> GetDefaultFooterMenu(ViewDataDictionary dictionary)
|
||||
|
||||
internal static List<Url> GetDefaultHeaderMenu(ViewDataDictionary dictionary, bool userAuthenticated = false)
|
||||
{
|
||||
var location = GetUserLanguage(dictionary);
|
||||
var menuToShow = new List<Url>();
|
||||
|
||||
var menu = LayoutCache
|
||||
.Root
|
||||
.Children;
|
||||
menuToShow.Add(Root.ToUrl());
|
||||
|
||||
if (dictionary[FooterMenuViewDataId] is List<RouteRecord> footerMenu)
|
||||
{
|
||||
menu = footerMenu;
|
||||
}
|
||||
|
||||
return menu
|
||||
.Select(x =>
|
||||
{
|
||||
if (SiteContent.RouteTitle[x.routeKey].ContainsKey(location))
|
||||
{
|
||||
return new RouteView(SiteContent.RouteTitle[x.routeKey][location],
|
||||
location);
|
||||
}
|
||||
return new RouteView(SiteContent.RouteTitle[x.routeKey][DefaultCultureName],
|
||||
x.location);
|
||||
|
||||
})
|
||||
|
||||
.ToList();
|
||||
|
||||
}
|
||||
|
||||
internal static List<RouteView> GetDefaultHeaderMenu(ViewDataDictionary dictionary, bool userAuthenticated = false)
|
||||
{
|
||||
if (!userAuthenticated)
|
||||
{
|
||||
var menuToShow = new List<RouteView>();
|
||||
menuToShow.Add(new RouteView("CodeLiturgy Dashboard", "/"));
|
||||
return menuToShow;
|
||||
}
|
||||
|
||||
var location = GetUserLanguage(dictionary);
|
||||
|
||||
var menu = LayoutCache
|
||||
.Root
|
||||
.Children;
|
||||
|
||||
if (dictionary[HeaderMenuId] is List<RouteRecord> footerMenu)
|
||||
if (dictionary[HeaderMenuId] is List<Url> headerMenu)
|
||||
{
|
||||
menu = footerMenu;
|
||||
}
|
||||
|
||||
return menu
|
||||
.Select(x =>
|
||||
{
|
||||
if (SiteContent.RouteTitle[x.routeKey].ContainsKey(location))
|
||||
{
|
||||
return new RouteView(SiteContent.RouteTitle[x.routeKey][location],
|
||||
x.Location);
|
||||
}
|
||||
return new RouteView(SiteContent.RouteTitle[x.routeKey][DefaultCultureName],
|
||||
x.location);
|
||||
|
||||
})
|
||||
|
||||
menuToShow = menuToShow
|
||||
.Concat(headerMenu)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
return menuToShow;
|
||||
}
|
||||
|
||||
|
||||
internal static List<Url> GetDefaultFooterMenu(ViewDataDictionary dictionary, bool userAuthenticated)
|
||||
{
|
||||
var menuToShow = new List<Url>();
|
||||
|
||||
menuToShow.Add(Root.ToUrl());
|
||||
|
||||
if (!userAuthenticated)
|
||||
{
|
||||
return menuToShow;
|
||||
}
|
||||
|
||||
if (dictionary[FooterMenuViewDataId] is List<Url> footerMenu)
|
||||
{
|
||||
menuToShow = menuToShow
|
||||
.Concat(footerMenu)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
return menuToShow;
|
||||
}
|
||||
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal static void IsDevMode(ref bool isDebug)
|
||||
|
|
|
@ -1,14 +1,33 @@
|
|||
namespace CodeLiturgy.Views.Utils
|
||||
{
|
||||
public record RouteRecord(string routeKey, string location, string controllerName, List<RouteRecord> children, ViewType viewType = ViewType.Undefined)
|
||||
public class Url
|
||||
{
|
||||
public string RouteKey = routeKey;
|
||||
public string Location = location;
|
||||
public string ControllerName;
|
||||
public List<RouteRecord> Children = children;
|
||||
public ViewType ViewType = viewType;
|
||||
public string Name;
|
||||
public string Location;
|
||||
|
||||
public Url(string name, string location)
|
||||
{
|
||||
Name = name;
|
||||
Location = location;
|
||||
}
|
||||
}
|
||||
public record RouteRecord(string Name, string RouteKey, string Location, string ControllerName, List<RouteRecord> Children, ViewType ViewType = ViewType.Undefined)
|
||||
{
|
||||
|
||||
public List<Url> ChildrenToUrl()
|
||||
{
|
||||
return Children.Select(x => x.ToUrl()).ToList();
|
||||
}
|
||||
public Url ToUrl()
|
||||
{
|
||||
return new Url(Name, Location);
|
||||
}
|
||||
public Url ToUrl(string name)
|
||||
{
|
||||
return new Url(name, Location);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public record RouteView(string Name, string Location);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,47 +46,22 @@ namespace CodeLiturgy.Views.Utils
|
|||
internal const string SettingsRouteLocation = $"{SystemRouteLocation}/settings";
|
||||
internal const string SettingsKeyName = "settings";
|
||||
|
||||
internal const string DataLocation = $"/data";
|
||||
internal const string DataKeyName = "data";
|
||||
|
||||
|
||||
internal const string DataUsersLocation = "/data/users";
|
||||
internal const string DataUsersKeyName = "users";
|
||||
|
||||
internal const string SystemRouteLocation = $"/system";
|
||||
internal const string SystemKeyName = "system";
|
||||
|
||||
internal const string BanksKeyName = "banks";
|
||||
internal const string BanksLocation = $"{DataLocation}/banks";
|
||||
|
||||
internal const string CountriesLocation = $"{DataLocation}/countries";
|
||||
internal const string CountriesKeyName = "countries";
|
||||
|
||||
internal const string CurrenciesLocation = $"{DataLocation}/currencies";
|
||||
internal const string CurrenciesKeyName = "currencies";
|
||||
|
||||
internal const string IndustriesKeyName = "industries";
|
||||
internal const string IndustriesLocation = $"{DataLocation}/industries";
|
||||
|
||||
internal const string CompaniesLocation = $"{DataLocation}/companies";
|
||||
internal const string CompaniesKeyName = "companies";
|
||||
|
||||
|
||||
// Jobs
|
||||
internal const string Curriculums = "curriculums";
|
||||
internal const string Proposals = "proposals";
|
||||
internal const string Offers = "offers";
|
||||
internal const string Processes = "processes";
|
||||
internal const string AuthLogoutRoute = "/auth/logout";
|
||||
internal const string AuthAccountRoute = "/auth/account";
|
||||
internal const string AuthAccountKeyName = "account";
|
||||
|
||||
// Account
|
||||
internal const string AccountRouteLocation = "/account";
|
||||
internal const string AccountKeyName = "profile";
|
||||
|
||||
internal const string ChangePasswordKeyName = "changepwd";
|
||||
internal const string ChangePasswordRouteLocation = $"{AccountRouteLocation}/change";
|
||||
|
||||
|
||||
internal const string ChangePasswordRouteLocation = $"{AuthAccountRoute}/change";
|
||||
|
||||
// Environments
|
||||
internal const string EnvironmentsRouteLocation = "/environments";
|
||||
internal const string EnvironmentsKeyName = "envs";
|
||||
|
||||
}
|
||||
}
|
|
@ -5,8 +5,8 @@ namespace CodeLiturgy.Views
|
|||
public enum ViewType
|
||||
{
|
||||
System,
|
||||
Data,
|
||||
Account,
|
||||
Environments,
|
||||
Root,
|
||||
Undefined
|
||||
|
||||
|
|
|
@ -1,111 +0,0 @@
|
|||
@using CodeLiturgy.Views.Utils
|
||||
@model BlueWest.Data.Application.Users.ApplicationUserUnique
|
||||
@{
|
||||
var user = @Model;
|
||||
}
|
||||
|
||||
<div class="q-ma-lg">
|
||||
<form method="POST" action="EditProfile" class="q-form q-gutter-md">
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.Id" disabled="disabled">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Id</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" name="email" aria-label="Email" type="email" value="@user.Email">
|
||||
</div>
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Email address</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.PhoneNumber">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Phone Number</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.NormalizedEmail" disabled="disabled">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Normalized Email</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.UserName">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Username</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Password Hash" name="name" type="name" value="@Model.PasswordHash" disabled="disabled">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Username</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<div>
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--standard q-btn--rectangle bg-primary text-white q-btn--actionable q-focusable q-hoverable">
|
||||
<span class="q-focus-helper"></span>
|
||||
<span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
|
||||
<span class="block">Save</span>
|
||||
</span>
|
||||
</button>
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--flat q-btn--rectangle text-primary q-btn--actionable q-focusable q-hoverable q-ml-sm" type="reset">
|
||||
<span class="q-focus-helper"></span>
|
||||
<span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
|
||||
<span class="block">Reset</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
@await Html.PartialAsync("_FooterMenu")
|
|
@ -1,10 +0,0 @@
|
|||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Curriculums Module</h1>
|
||||
</div>
|
||||
|
||||
|
||||
@Html.Partial("_FooterMenu");
|
|
@ -1,111 +0,0 @@
|
|||
@using CodeLiturgy.Views.Utils
|
||||
@model BlueWest.Data.Application.Users.ApplicationUserUnique
|
||||
@{
|
||||
var user = @Model;
|
||||
}
|
||||
|
||||
<div class="q-ma-lg">
|
||||
<form method="POST" action="EditProfile" class="q-form q-gutter-md">
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.Id" disabled="disabled">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Id</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" name="email" aria-label="Email" type="email" value="@user.Email">
|
||||
</div>
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Email address</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.PhoneNumber">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Phone Number</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.NormalizedEmail" disabled="disabled">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Normalized Email</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Phone Number" name="name" type="name" value="@Model.UserName">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Username</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<label class="q-field row no-wrap items-start q-field--filled q-input q-field--labeled q-field--dark q-field--with-bottom">
|
||||
<div class="q-field__inner relative-position col self-stretch">
|
||||
<div class="q-field__control relative-position row no-wrap" tabindex="-1">
|
||||
<div class="q-field__control-container col relative-position row no-wrap q-anchor--skip">
|
||||
<input class="q-field__native q-placeholder" tabindex="0" aria-label="Password Hash" name="name" type="name" value="@Model.PasswordHash" disabled="disabled">
|
||||
</div>
|
||||
<div class="q-field__bottom row items-start q-field__bottom--animated">
|
||||
<div class="q-field__messages col">
|
||||
<div>Username</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<div>
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--standard q-btn--rectangle bg-primary text-white q-btn--actionable q-focusable q-hoverable">
|
||||
<span class="q-focus-helper"></span>
|
||||
<span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
|
||||
<span class="block">Save</span>
|
||||
</span>
|
||||
</button>
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--flat q-btn--rectangle text-primary q-btn--actionable q-focusable q-hoverable q-ml-sm" type="reset">
|
||||
<span class="q-focus-helper"></span>
|
||||
<span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
|
||||
<span class="block">Reset</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
@await Html.PartialAsync("_FooterMenu")
|
|
@ -0,0 +1 @@
|
|||
<h1>Account</h1>
|
|
@ -1,5 +0,0 @@
|
|||
@model BlueWest.Data.Auth.Context.Users.LoginRequest
|
||||
@using (Html.BeginForm()){
|
||||
@Html.LabelFor(x => x.Password, "Password")
|
||||
@Html.PasswordFor(x => x.Password);
|
||||
}
|
|
@ -101,7 +101,7 @@
|
|||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--standard q-btn--rectangle bg-primary text-white q-btn--actionable q-focusable q-hoverable">
|
||||
<span class="q-focus-helper"></span>
|
||||
<span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
|
||||
<span class="block">Login</span>
|
||||
<span class="block">Signup</span>
|
||||
</span>
|
||||
</button>
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--flat q-btn--rectangle text-primary q-btn--actionable q-focusable q-hoverable q-ml-sm" type="reset">
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
@model dynamic
|
||||
|
||||
@{
|
||||
Layout = null;
|
||||
}
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>title</title>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,5 @@
|
|||
<div class="text-center">
|
||||
|
||||
</div>
|
||||
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
@using CodeLiturgy.Views.Utils
|
||||
@using Duende.IdentityServer.Extensions
|
||||
@{
|
||||
Layout = null;
|
||||
var menu = LayoutCache.GetDefaultFooterMenu(ViewData);
|
||||
var menu = LayoutCache.GetDefaultFooterMenu(ViewData, User.IsAuthenticated());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -4,10 +4,12 @@
|
|||
@{
|
||||
Layout = null;
|
||||
var userAuthenticated = User.IsAuthenticated();
|
||||
var menu = LayoutCache.GetDefaultHeaderMenu(ViewData, userAuthenticated);
|
||||
var user = ViewData.GetUserViewData();
|
||||
var rootUrl = SessionConstants.CookieDomain;
|
||||
|
||||
|
||||
var menu = LayoutCache.GetDefaultHeaderMenu(ViewData, User.IsAuthenticated());
|
||||
|
||||
}
|
||||
|
||||
<div class="q-tabs items-center row no-wrap items-center q-tabs--not-scrollable q-tabs--horizontal q-tabs__arrows--inside">
|
||||
|
@ -26,7 +28,7 @@
|
|||
}
|
||||
@if (userAuthenticated && user != null)
|
||||
{
|
||||
<div class="q-chip row inline no-wrap items-center q-chip--dark q-dark">
|
||||
<div class="q-chip row inline no-wrap items-center q-chip--dark q-dark" id="profile-wrap">
|
||||
<div class="q-chip__content col row no-wrap items-center q-anchor--skip">
|
||||
<div class="q-avatar">
|
||||
<div class="q-avatar__content row flex-center overflow-hidden">
|
||||
|
@ -35,8 +37,46 @@
|
|||
</div> @user.Email
|
||||
</div>
|
||||
</div>
|
||||
<style lang="css">
|
||||
|
||||
</style>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@if (userAuthenticated && user != null)
|
||||
{
|
||||
<div class="profileMenuContainer" id="profile-menu" style="display: none">
|
||||
<div>
|
||||
<a href="@AuthAccountRoute" class="profile-link">
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--standard q-btn--rectangle bg-black text-white q-btn--actionable q-focusable q-hoverable q-btn--active" type="button">
|
||||
<span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
|
||||
<span class="block">Profile</span>
|
||||
</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="@AuthLogoutRoute" class="profile-link">
|
||||
<button class="q-btn q-btn-item non-selectable no-outline q-btn--standard q-btn--rectangle bg-black text-white q-btn--actionable q-focusable q-hoverable q-btn--active" type="button">
|
||||
<span class="q-btn__content text-center col items-center q-anchor--skip justify-center row">
|
||||
<span class="block">Logout</span>
|
||||
</span>
|
||||
</button>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<script >
|
||||
(() => {
|
||||
const wrapMenu = document.getElementById("profile-wrap");
|
||||
const profileMenu = document.getElementById("profile-menu");
|
||||
wrapMenu.onclick = () => {
|
||||
if (profileMenu.style.display === "none") {
|
||||
profileMenu.style.display = "block";
|
||||
} else {
|
||||
profileMenu.style.display = "none";
|
||||
}
|
||||
}
|
||||
})();
|
||||
</script>
|
|
@ -10,10 +10,9 @@
|
|||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>@ViewData["Title"] - CodeLiturgy.Views</title>
|
||||
<link rel="stylesheet" href="@rootUrl/static/main.css"/>
|
||||
@*
|
||||
<link rel="stylesheet" href="~/CodeLiturgy.Views.styles.css" asp-append-version="true"/>
|
||||
*@
|
||||
<link rel="stylesheet" href="@rootUrl/static/main.css" asp-append-version="true"/>
|
||||
<link rel="stylesheet" href="@rootUrl/static/site.css" asp-append-version="true"/>
|
||||
|
||||
<link rel="preload" href="@rootUrl/static/material-icons/web-font/flUhRq6tzZclQEJ-Vdg-IuiaDsNa.woff" as="font" type="font/woff" crossorigin>
|
||||
<link rel="preload" href="@rootUrl/static/material-icons/web-font/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<style>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
.router-link-active {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
|
||||
.material-icons {
|
||||
font-family: 'Material Icons', "Roboto", "-apple-system", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-weight: normal;
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
#profile-wrap {
|
||||
cursor: pointer;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.profile-link {
|
||||
text-decoration: none;
|
||||
}
|
||||
.profileMenuContainer {
|
||||
position: absolute;
|
||||
background-color: #505050;
|
||||
padding: 12px 10px 12px;
|
||||
right: 0;
|
||||
border: 2px solid #0f0f0f;
|
||||
border-radius: 10px;
|
||||
}
|
Loading…
Reference in New Issue