Prima configurazione e struttura

This commit is contained in:
2026-02-04 17:31:00 +01:00
parent 1a949051ca
commit ecafebae7f
66 changed files with 1153 additions and 645 deletions

View File

@@ -1,16 +1,170 @@
@inherits LayoutComponentBase
@using System.Globalization
@using SteUp.Shared.Core.Interface.IntegryApi
@using SteUp.Shared.Core.Interface.System.Network
@inherits LayoutComponentBase
@inject INetworkService NetworkService
@inject IIntegryApiService IntegryApiService
<MudThemeProvider Theme="_currentTheme" @ref="@_mudThemeProvider" @bind-IsDarkMode="@IsDarkMode" />
<MudPopoverProvider/>
<MudDialogProvider/>
<MudSnackbarProvider/>
<div class="page">
@*<div class="sidebar">
<NavMenu />
</div>*@
<NavMenu/>
<main>
<article class="content px-4">
<article>
@Body
</article>
</main>
</div>
</div>
@code {
private MudThemeProvider _mudThemeProvider = null!;
private bool IsDarkMode { get; set; }
private string _mainContentClass = "";
//Connection state
private bool _isNetworkAvailable;
private bool _servicesIsDown;
private bool _showWarning;
private DateTime _lastApiCheck = DateTime.MinValue;
private const int DelaySeconds = 90;
private CancellationTokenSource? _cts;
private bool ServicesIsDown
{
get => _servicesIsDown;
set
{
if (_servicesIsDown == value) return;
_servicesIsDown = value;
StateHasChanged();
}
}
private bool IsNetworkAvailable
{
get => _isNetworkAvailable;
set
{
if (_isNetworkAvailable == value) return;
_isNetworkAvailable = value;
StateHasChanged();
}
}
private bool ShowWarning
{
get => _showWarning;
set
{
if (_showWarning == value) return;
_showWarning = value;
StateHasChanged();
}
}
private readonly MudTheme _currentTheme = new()
{
PaletteLight = new PaletteLight()
{
Primary = "#ec4c41",
Secondary = "#002339",
Tertiary = "#dff2ff",
TextPrimary = "#000"
},
PaletteDark = new PaletteDark
{
Primary = "#ec4c41",
Secondary = "#002339",
Tertiary = "#dff2ff",
Surface = "#000406",
Background = "#000406",
TextPrimary = "#fff",
GrayDark = "#E0E0E0"
}
};
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// if (firstRender)
// {
// var isDarkMode = LocalStorage.GetString("isDarkMode");
// if (isDarkMode == null && _mudThemeProvider != null)
// {
// IsDarkMode = await _mudThemeProvider.GetSystemPreference();
// await _mudThemeProvider.WatchSystemPreference(OnSystemPreferenceChanged);
// LocalStorage.SetString("isDarkMode", IsDarkMode.ToString());
// StateHasChanged();
// }
// else
// {
// IsDarkMode = bool.Parse(isDarkMode!);
// }
// if (IsDarkMode)
// {
// _mainContentClass += "is-dark";
// StateHasChanged();
// }
// }
}
private async Task OnSystemPreferenceChanged(bool newValue)
{
IsDarkMode = newValue;
}
protected override void OnInitialized()
{
_cts = new CancellationTokenSource();
_ = CheckConnectionState(_cts.Token);
var culture = new CultureInfo("it-IT", false);
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
}
private Task CheckConnectionState(CancellationToken token)
{
return Task.Run(async () =>
{
while (!token.IsCancellationRequested)
{
var isNetworkAvailable = NetworkService.IsNetworkAvailable();
var servicesDown = ServicesIsDown;
if (isNetworkAvailable && (DateTime.UtcNow - _lastApiCheck).TotalSeconds >= DelaySeconds)
{
servicesDown = !await IntegryApiService.SystemOk();
_lastApiCheck = DateTime.UtcNow;
}
await InvokeAsync(async () =>
{
IsNetworkAvailable = isNetworkAvailable;
ServicesIsDown = servicesDown;
await Task.Delay(1500, token);
ShowWarning = !(IsNetworkAvailable && !ServicesIsDown);
NetworkService.ConnectionAvailable = !ShowWarning;
});
await Task.Delay(500, token);
}
}, token);
}
public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
}

View File

@@ -21,29 +21,29 @@ main {
align-items: center;
}
.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}
.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
}
@media (min-width: 641px) {
@@ -64,14 +64,14 @@ main {
z-index: 1;
}
.top-row.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}
.top-row.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}
.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}
}

View File

@@ -1,40 +0,0 @@
<div class="container">
<nav>
<ul>
<li>
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span>Home</span>
<i class="ri-home-5-line"/>
</NavLink>
</li>
</ul>
<ul>
<li>
<a href="#" id="workout">
<span>Workout</span>
<i class="ri-empathize-line"/>
</a>
</li>
</ul>
<ul>
<li>
<a href="#" id="logbook">
<span>Logbook</span>
<i class="ri-health-book-line"></i>
</a>
</li>
</ul>
<ul>
<li>
<a href="#" id="settings">
<span>Impostazioni</span>
<i class="ri-settings-5-line"/>
</a>
</li>
</ul>
</nav>
</div>
@code {
}

View File

@@ -1,81 +0,0 @@
a {
text-decoration: none;
color: inherit;
}
ul {
list-style-type: none;
}
.container {
max-width: 100%;
margin: 0 auto;
padding: 0;
}
nav {
position: fixed;
bottom: 0;
width: 100%;
background-color: var(--ligther-color);
margin: 0;
display: flex;
border-radius: 40px 40px 0px 0px;
box-shadow: rgb(50 50 93 / 25%) 0 50px 100px 10px,
rgb(0 0 0 / 30%) 0 30px 60px -30px;
}
nav ul {
display: inline-flex;
align-items: center;
padding: 0;
flex: 0 0 25%;
justify-content: center;
}
nav :where(li a) {
position: relative;
}
nav ul li a {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column-reverse;
padding: 1em;
line-height: 1.4;
-webkit-transition: all .3s ease-out;
transition: all .3s ease-out;
}
nav ul li a:hover {
color: var(--primary-color);
}
nav ul li a i {
font-size: 1.5rem;
}
nav ul li a span {
font-size: 0.9rem;
}
/* animations */
nav li.active a::before, nav li.active a::after {
content: "";
position: absolute;
background-color: var(--primary-color);
z-index: -1;
}
nav li.active a::before {
top: 5%;
width: calc(100% - 0px);
height: 100%;
border-radius: 25px;
}
nav li.active a {
color: var(--ligther-color);
}

View File

@@ -1,38 +1,72 @@
<nav class="navbar navbar-expand justify-content-center">
<div class="container-fluid">
<ul class="navbar-nav nav-justified w-100 text-center">
<div class="container animated-navbar @(IsVisible ? "show-nav" : "hide-nav") @(IsVisible ? PlusVisible ? "with-plus" : "without-plus" : "with-plus")">
<nav class="navbar @(IsVisible ? PlusVisible ? "with-plus" : "without-plus" : "with-plus")">
<div class="container-navbar">
<ul class="navbar-nav flex-row nav-justified align-items-center w-100 text-center">
<li class="nav-item">
<NavLink class="nav-link" href="Users" Match="NavLinkMatch.All">
<div class="d-flex flex-column">
<i class="ri-group-line"></i>
<span>Test</span>
</div>
</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="Calendar" Match="NavLinkMatch.All">
<div class="d-flex flex-column">
<i class="ri-calendar-todo-line"></i>
<span>Home</span>
</div>
</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="Calendar" Match="NavLinkMatch.All">
<div class="d-flex flex-column">
<i class="ri-calendar-todo-line"></i>
<span>Altro</span>
</div>
</NavLink>
</li>
</ul>
</div>
<li class="nav-item">
<NavLink class="nav-link " href="workout" Match="NavLinkMatch.All">
<div class="d-flex flex-column">
<i class="ri-empathize-line"/>
@* <span>Workout</span> *@
</div>
</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<div class="d-flex flex-column">
<i class="ri-home-5-line"/>
@* <span>Home</span> *@
</div>
</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="logbook" Match="NavLinkMatch.All">
<div class="d-flex flex-column">
<i class="ri-health-book-line"></i>
@* <span>Log Book</span> *@
</div>
</NavLink>
</li>
@* <li class="nav-item"> *@
@* <NavLink class="nav-link d-flex flex-column" href="settings" Match="NavLinkMatch.All"> *@
@* <i class="ri-settings-5-line"/> *@
@* <span>Impostazioni</span> *@
@* </NavLink> *@
@* </li> *@
</ul>
</div>
@if (PlusVisible)
{
@* <MudMenu PopoverClass="custom_popover" AnchorOrigin="Origin.TopLeft" TransformOrigin="Origin.BottomRight"> *@
@* <ActivatorContent> *@
@* <MudFab Class="custom-plus-button" Color="Color.Surface" Size="Size.Medium" IconSize="Size.Medium" IconColor="Color.Primary" StartIcon="@Icons.Material.Filled.Add"/> *@
@* </ActivatorContent> *@
@* <ChildContent> *@
@* <MudMenuItem Disabled="!NetworkService.IsNetworkAvailable()" OnClick="() => CreateUser()">Nuovo contatto</MudMenuItem> *@
@* <MudMenuItem Disabled="!NetworkService.IsNetworkAvailable()" OnClick="() => CreateActivity()">Nuova attivit<69></MudMenuItem> *@
@* </ChildContent> *@
@* </MudMenu> *@
}
</nav>
</div>
</nav>
@code
{
private bool IsVisible { get; set; } = true;
private bool PlusVisible { get; set; } = true;
protected override Task OnInitializedAsync()
{
NavigationManager.LocationChanged += (_, args) =>
{
var location = args.Location.Remove(0, NavigationManager.BaseUri.Length);
var newIsVisible = new List<string> { "Home" }
.Contains(location);
var newPlusVisible = new List<string> { "Home" }
.Contains(location);
if (IsVisible == newIsVisible && PlusVisible == newPlusVisible) return;
IsVisible = newIsVisible;
PlusVisible = newPlusVisible;
StateHasChanged();
};
return Task.CompletedTask;
}
}

View File

@@ -0,0 +1,8 @@
<div class="spinner-container @(FullScreen ? "" : "not-fullScreen")">
<span class="loader"></span>
</div>
@code
{
[Parameter] public bool FullScreen { get; set; } = true;
}

View File

@@ -0,0 +1,43 @@
.spinner-container {
display: flex;
justify-content: center;
height: calc(100vh - 10.1rem);
align-items: center;
color: var(--mud-palette-primary);
}
.not-fullScreen {
height: auto !important;
padding: 2rem 0 !important;
}
.loader {
width: 50px;
aspect-ratio: 1;
border-radius: 50%;
border: 8px solid #0000;
border-right-color: var(--mud-palette-secondary);
position: relative;
animation: l24 1s infinite linear;
}
.loader:before,
.loader:after {
content: "";
position: absolute;
inset: -8px;
border-radius: 50%;
border: inherit;
animation: inherit;
animation-duration: 2s;
}
.loader:after {
animation-duration: 4s;
}
@keyframes l24 {
100% {
transform: rotate(1turn)
}
}

View File

@@ -1,16 +0,0 @@
@page "/counter"
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}

View File

@@ -1,23 +0,0 @@
@page "/device-form-factor"
@using Template.Shared.Interfaces
@inject IFormFactor FormFactor
<PageTitle>Form Factor</PageTitle>
<h1>Device Form Factor</h1>
<p>You are running on:</p>
<ul>
<li>Form Factor: @factor</li>
<li>Platform: @platform</li>
</ul>
<p>
<em>This component is defined in the Template.Shared library.</em>
</p>
@code {
private string factor => FormFactor.GetFormFactor();
private string platform => FormFactor.GetPlatform();
}

View File

@@ -1,15 +1,4 @@
@page "/"
@page "/home"
@attribute [Authorize]
<h1>Hello, world!</h1>
Welcome to your new app.
<Modal @ref="modal" title="Full screen" Fullscreen="ModalFullscreen.Always">
<BodyTemplate>...</BodyTemplate>
</Modal>
<Button Color="ButtonColor.Primary" @onclick="() => modal.ShowAsync()">Full screen</Button>
@code {
private Modal modal = default!;
}
<h3>Home</h3>

View File

@@ -0,0 +1 @@

View File

@@ -1,6 +0,0 @@
@page "/settings"
<h3>Impostazioni</h3>
@code {
}

View File

@@ -0,0 +1,13 @@
@page "/"
@using SteUp.Shared.Components.Layout.Spinner
@attribute [Authorize]
<SpinnerLayout FullScreen="true"/>
@code {
protected override void OnInitialized()
{
NavigationManager.NavigateTo("/home");
}
}

View File

@@ -1,10 +0,0 @@
@page "/logbook"
@using Template.Shared.Components.SingleElements
<h3 class="page-title">Log book</h3>
<NoDataAvailable ImageSource="_content/Template.Shared/images/log-book.svg"
Text="Nessun log book memorizzato"/>
@code {
}

View File

@@ -0,0 +1,6 @@
@page "/login"
<h3>Login</h3>
@code {
}

View File

@@ -0,0 +1 @@

View File

@@ -1,61 +0,0 @@
@page "/weather"
<h1>Weather</h1>
<p>This component demonstrates showing data.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
// Simulate asynchronous loading to demonstrate a loading indicator
await Task.Delay(500);
var startDate = DateOnly.FromDateTime(DateTime.Now);
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
}
private class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
}

View File

@@ -1,12 +0,0 @@
@page "/workout"
@using Template.Shared.Components.SingleElements
<h3 class="page-title">Workout</h3>
<NoDataAvailable ImageSource="_content/Template.Shared/images/man-doing-squats.svg"
Text="Nessun workout disponibile"/>
@code {
}

View File

@@ -1,6 +1,48 @@
<Router AppAssembly="@typeof(Routes).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
</Router>
@using SteUp.Shared.Components.SingleElements.Modal.ExceptionModal
@inject NavigationManager NavigationManager
<ErrorBoundary @ref="ErrorBoundary">
<ChildContent>
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Routes).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)">
<Authorizing>
<p>Authorizing page</p>
</Authorizing>
<NotAuthorized>
@if (context.User.Identity?.IsAuthenticated != true)
{
NavigationManager.NavigateTo("/login");
}
else
{
<p role="alert">You are not authorized to access this resource.</p>
}
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="@routeData" Selector="h1"/>
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView>
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
</ChildContent>
<ErrorContent>
<ExceptionModal @ref="ExceptionModal"
Exception="@context"
ErrorBoundary="@ErrorBoundary"
OnRetry="() => ErrorBoundary?.Recover()"/>
</ErrorContent>
</ErrorBoundary>
@code {
private ErrorBoundary? ErrorBoundary { get; set; }
private ExceptionModal ExceptionModal { get; set; }
}

View File

@@ -0,0 +1,59 @@
<div class="container container-modal">
<div class="c-modal">
<div class="exception-header mb-2">
<i class="ri-emotion-unhappy-line"></i>
<span>Ops</span>
</div>
<div class="text">
@if (Exception != null)
{
<code>@Message</code>
}
</div>
<div class="button-container">
<div @onclick="OnRetryClick" class="card-button">
<span>Riprova</span>
</div>
<div @onclick="OnContinueClick" class="card-button">
<span>Continua</span>
</div>
</div>
</div>
</div>
@code {
[Parameter] public Exception? Exception { get; set; }
[Parameter] public EventCallback OnRetry { get; set; }
[Parameter] public ErrorBoundary? ErrorBoundary { get; set; }
private string Message { get; set; } = "";
protected override void OnInitialized()
{
if (Exception == null) return;
if (Exception.Message.Contains("Failed to connect to"))
{
var ipPort = Exception.Message.Split("to /")[1];
Message = $"Impossibile collegarsi al server ({ipPort})";
}
else
{
Message = Exception.Message;
}
StateHasChanged();
}
private async Task OnRetryClick()
{
await OnRetry.InvokeAsync();
}
private async Task OnContinueClick()
{
NavigationManager.NavigateTo("/");
await OnRetry.InvokeAsync();
}
}

View File

@@ -0,0 +1,61 @@
.container-modal {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
.c-modal {
border-radius: 16px;
box-shadow: var(--exception-box-shadow);
padding: 16px;
}
.button-container {
display: flex;
gap: 1rem;
flex-direction: row;
align-items: center;
width: 100%;
justify-content: space-between;
margin: 1.5rem 0 0 0;
}
.text {
font-size: medium;
font-weight: 500;
display: flex;
text-align: center;
}
.card-button {
text-align: center;
background-color: transparent;
padding: .3rem 1rem;
font-weight: 700;
color: var(--bs-primary-text-emphasis);
}
.exception-header {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.exception-header > i {
font-size: 3rem;
line-height: normal;
color: var(--bs-danger);
}
.exception-header > span {
font-size: x-large;
font-weight: 700;
}
code {
width: 100%;
height: auto;
color: var(--bs-gray-dark);
}

View File

@@ -0,0 +1,13 @@
namespace SteUp.Shared.Core.Authorization.Enum;
public enum KeyGroupEnum
{
UtenteAziendale = 2,
Cliente = 3,
Agenti = 5,
AmministratoreAziendale = 9,
Tecnico = 22,
ResponsabileDiReparto = 23,
ResponsabileAmministrativo = 29,
Programmatore = 30
}

View File

@@ -1,4 +1,4 @@
namespace Template.Shared.Interfaces;
namespace SteUp.Shared.Core.Interface;
public interface IFormFactor
{

View File

@@ -0,0 +1,6 @@
namespace SteUp.Shared.Core.Interface.IntegryApi;
public interface IIntegryApiService
{
Task<bool> SystemOk();
}

View File

@@ -0,0 +1,8 @@
namespace SteUp.Shared.Core.Interface.System.Network;
public interface INetworkService
{
public bool ConnectionAvailable { get; set; }
public bool IsNetworkAvailable();
}

View File

@@ -0,0 +1,56 @@
using System.Security.Claims;
using IntegryApiClient.Core.Domain.Abstraction.Contracts.Account;
using Microsoft.AspNetCore.Components.Authorization;
namespace SteUp.Shared.Core.Services;
public class AppAuthenticationStateProvider : AuthenticationStateProvider
{
private readonly IUserSession _userSession;
private readonly IUserAccountService _userAccountService;
public AppAuthenticationStateProvider(IUserSession userSession, IUserAccountService userAccountService)
{
_userSession = userSession;
_userAccountService = userAccountService;
userAccountService.ExpiredUserSession += (_, _) =>
NotifyAuthenticationStateChanged(LoadAuthenticationState());
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
return await LoadAuthenticationState();
}
public async Task SignOut()
{
await _userAccountService.Logout();
NotifyAuthenticationState();
}
public void NotifyAuthenticationState()
{
NotifyAuthenticationStateChanged(LoadAuthenticationState());
}
private async Task<AuthenticationState> LoadAuthenticationState()
{
if (!await _userSession.IsLoggedIn() || !await _userSession.IsRefreshTokenValid())
{
return new AuthenticationState(
new ClaimsPrincipal(
new ClaimsIdentity()
)
);
}
var claimIdentity = new ClaimsIdentity(_userSession.JwtToken!.Claims, "jwt");
var user = new ClaimsPrincipal(claimIdentity);
var authenticationState = new AuthenticationState(user);
return authenticationState;
}
}

View File

@@ -0,0 +1,22 @@
using IntegryApiClient.Core.Domain.Abstraction.Contracts.Account;
using IntegryApiClient.Core.Domain.RestClient.Contacts;
using SteUp.Shared.Core.Interface.IntegryApi;
namespace SteUp.Shared.Core.Services;
public class IntegryApiService(IIntegryApiRestClient integryApiRestClient, IUserSession userSession)
: IIntegryApiService
{
public async Task<bool> SystemOk()
{
try
{
await integryApiRestClient.Get<object>("system/ok");
return true;
}
catch (Exception e)
{
return false;
}
}
}

View File

@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
namespace Template.Shared;
namespace SteUp.Shared;
public static class InteractiveRenderSettings
{

View File

@@ -1,23 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<SupportedPlatform Include="browser"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Blazor.Bootstrap" Version="3.0.0" />
<PackageReference Include="IntegryApiClient.Core" Version="1.0.7" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.8" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="IntegryApiClient.Core" Version="1.2.3"/>
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="9.0.12"/>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0"/>
<PackageReference Include="MudBlazor" Version="8.15.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.14.0"/>
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.12"/>
<PackageReference Include="Microsoft.Maui.Essentials" Version="9.0.120"/>
</ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\js\bootstrap\" />
</ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\js\bootstrap\"/>
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Components\Layout\Overlay\SpinnerOverlay.razor" />
</ItemGroup>
</Project>

View File

@@ -1,10 +1,19 @@
@using System.Net.Http
@using System.Net.Http.Json
@using IntegryApiClient.Core.Domain.Abstraction.Contracts.Account
@using IntegryApiClient.Core.Domain.Abstraction.Contracts.Storage
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using Template.Shared.Components
@using BlazorBootstrap;
@using SteUp.Shared.Components
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using MudBlazor
@using static InteractiveRenderSettings
@inject NavigationManager NavigationManager
@inject IUserSession UserSession
@inject ILocalStorage LocalStorage
@inject ISnackbar Snackbar

View File

@@ -1,46 +1,98 @@
html, body {
font-family: 'Poppins', 'Helvetica Neue', Helvetica, Arial, sans-serif;
html { overflow: hidden; }
.page, article, main { height: 100% !important; overflow: hidden; }
#app { height: 100vh; }
html, body {
font-family: "Nunito", sans-serif;
font-size: 14px;
font-weight: 400;
line-height: 1.8;
color: black;
}
* { font-family: "Nunito", sans-serif !important; }
.mud-button-label { font-weight: 700 !important; }
a, .btn-link {
/*color: #006bb7;*/
text-decoration: none;
color: inherit;
}
article {
display: flex;
flex-direction: column;
}
/*ServicesIsDown" : "SystemOk" : "NetworkKo*/
.Connection {
padding: 0 .75rem;
font-weight: 700;
display: flex;
flex-direction: row;
gap: 1rem;
font-size: larger;
transition: all 0.5s ease;
opacity: 0;
transform: translateY(-35px);
min-height: 35px;
align-items: center;
}
.Connection.ServicesIsDown, .Connection.NetworkKo {
background-color: var(--mud-palette-error);
color: white;
}
.Connection.SystemOk {
background-color: var(--mud-palette-success);
color: white;
}
.Connection.Show {
opacity: 1;
transform: translateY(0);
}
.Connection.Hide ~ .page {
transition: all 0.5s ease;
transform: translateY(-35px);
}
.btn-primary {
color: #fff;
background-color: var(--primary-color);
border-color: var(--darker-color);
}
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; }
.content {
padding-top: 1.1rem;
padding-top: 1rem;
display: flex;
align-items: center;
flex-direction: column;
height: 90vh;
}
h1:focus {
outline: none;
.content::-webkit-scrollbar { width: 3px; }
.content::-webkit-scrollbar-thumb {
background: #bbb;
border-radius: 2px;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
h1:focus { outline: none; }
.invalid {
outline: 1px solid #e50000;
}
.valid.modified:not([type=checkbox]) { outline: 1px solid #26b050; }
.validation-message {
color: #e50000;
}
.invalid { outline: 1px solid #e50000; }
.validation-message { color: #e50000; }
#blazor-error-ui {
background: lightyellow;
@@ -67,18 +119,134 @@ h1:focus {
color: white;
}
.blazor-error-boundary::after {
content: "An error has occurred."
}
.blazor-error-boundary::after { content: "An error has occurred." }
.status-bar-safe-area {
display: none;
}
.status-bar-safe-area { display: none; }
.page-title {
/*text-align: center;*/
font-size: x-large;
color: var(--darker-color);
font-weight: 800;
margin: 0;
line-height: normal;
color: var(--mud-palette-text-primary);
}
.custom-mudfab {
position: fixed !important;
bottom: 4rem;
margin-bottom: 16px;
right: 16px;
}
.custom_popover {
border-radius: 5px !important;
background-color: var(--mud-palette-drawer-background) !important;
box-shadow: 4px 4px 20px 0px rgba(0, 0, 0, 0.26), 0px 0px 0px 1px rgb(255 255 255 / 25%) !important;
color: var(--mud-palette-text-primary) !important;
}
.custom_popover .mud-divider { border-color: var(--mud-palette-text-primary) !important; }
.custom_popover .mud-list-padding { padding: 3px 0px 3px 0px !important; }
.custom_popover .mud-list-item { padding: 5px 12px 5px 12px; }
.custom_popover .mud-menu-item-text { font-weight: 600; }
.custom_popover .mud-list-item-icon {
min-width: fit-content !important;
padding-right: 12px !important;
}
.divider {
display: block;
width: 100%;
border: .05rem solid var(--card-border-color);
margin: 1rem 0;
}
/*Spinner*/
.spinner-container {
display: flex;
justify-content: center;
height: 100vh;
align-items: center;
color: var(--mud-palette-primary);
}
.not-fullScreen {
height: auto !important;
padding: 2rem 0 !important;
}
.loader {
width: 50px;
aspect-ratio: 1;
border-radius: 50%;
border: 8px solid #0000;
border-right-color: var(--mud-palette-secondary);
position: relative;
animation: l24 1s infinite linear;
}
.loader:before,
.loader:after {
content: "";
position: absolute;
inset: -8px;
border-radius: 50%;
border: inherit;
animation: inherit;
animation-duration: 2s;
}
.loader:after { animation-duration: 4s; }
@keyframes l24 {
100% { transform: rotate(1turn) }
}
/*MudBlazor Personalization*/
.mud-button-group-horizontal:not(.mud-button-group-rtl) > .mud-button-root:not(:last-child), .mud-button-group-horizontal:not(.mud-button-group-rtl) > :not(:last-child) .mud-button-root {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
.mud-button-group-horizontal:not(.mud-button-group-rtl) > .mud-button-root:not(:first-child), .mud-button-group-horizontal:not(.mud-button-group-rtl) > :not(:first-child) .mud-button-root {
border-top-left-radius: 0 !important;
border-bottom-left-radius: 0 !important;
}
.customDialog-form .mud-dialog-content {
padding: 0 .75rem;
margin: 0;
overflow: hidden;
}
.custom-item-select { padding: 6px 16px; }
.custom-item-select .mud-typography-body1 {
font-weight: 600;
font-size: .9rem;
}
.container {
padding-right: var(--m-page-x) !important;
padding-left: var(--m-page-x) !important;
}
.lm-container {
padding-right: calc(var(--m-page-x) * 0.5) !important;
padding-left: calc(var(--m-page-x) * 0.5) !important;
}
.mud-message-box > .mud-dialog-title > h6 { font-weight: 800 !important; }
.mud-dialog-actions button {
margin-left: .5rem !important;
margin-right: .5rem !important;
}
@supports (-webkit-touch-callout: none) {
@@ -87,18 +255,23 @@ h1:focus {
position: fixed;
top: 0;
height: env(safe-area-inset-top);
background-color: #f7f7f7;
width: 100%;
z-index: 1;
background-color: var(--mud-palette-surface);
}
.modal { padding-top: env(safe-area-inset-top); }
.safe-area-bottom { margin-bottom: env(safe-area-inset-bottom) !important; }
.pb-safe-area { padding-bottom: env(safe-area-inset-bottom) !important; }
#app {
padding-top: env(safe-area-inset-top);
height: 100vh;
margin-top: env(safe-area-inset-top);
height: calc(100vh - env(safe-area-inset-top));
}
.flex-column, .navbar-brand {
padding-left: env(safe-area-inset-left);
}
}
.flex-column, .navbar-brand { padding-left: env(safe-area-inset-left); }
.customDialog-form .mud-dialog-content { margin-top: env(safe-area-inset-top); }
}

View File

@@ -1,5 +1,5 @@
:root {
--primary-color: #5352ed;
--primary-color: #ec4c41;
--ligther-color: #f7f7ff;
--darker-color: hsl(240, 81%, 30%);
--darker-color: #d02315;
}