Primo sviluppo sincronizzazione e migliorie ui

This commit is contained in:
2025-05-19 16:47:21 +02:00
parent 6f08ba87be
commit 626408412b
66 changed files with 1824 additions and 91 deletions

View File

@@ -1,14 +1,43 @@
@inject IJSRuntime JS
<div class="header">
<div class="header-content">
<h3 class="page-title">@Title</h3>
@if (ShowFilter)
<div class="header-content @(Back ? "with-back" : "no-back")">
@if (Back)
{
<MudIconButton Icon="@Icons.Material.Outlined.FilterAlt" Color="Color.Dark" />
<div class="left-section">
<MudButton StartIcon="@Icons.Material.Outlined.ArrowBackIosNew"
OnClick="GoBack"
Color="Color.Info"
Style="text-transform: none"
Variant="Variant.Text">@BackTo</MudButton>
</div>
}
<h3 class="page-title">@Title</h3>
<div class="right-section">
@if (ShowFilter)
{
<MudIconButton Icon="@Icons.Material.Outlined.FilterAlt" Color="Color.Dark" />
}
@if (ShowNotifications)
{
<MudIconButton Icon="@Icons.Material.Filled.Notifications" Color="Color.Dark" />
}
</div>
</div>
</div>
@code{
[Parameter] public string? Title { get; set; }
[Parameter] public bool ShowFilter { get; set; }
[Parameter] public bool ShowNotifications { get; set; } = true;
[Parameter] public bool Back { get; set; }
[Parameter] public string BackTo { get; set; } = "";
private async Task GoBack()
{
await JS.InvokeVoidAsync("goBack");
}
}

View File

@@ -1,7 +1,29 @@
.header-content {
line-height: normal;
display: flex;
justify-content: space-between;
color: var(--lighter-color);
align-items: center;
padding-top: .5rem;
position: relative;
}
.header-content.with-back { margin: .6rem 0 0 0; }
.header-content.with-back .page-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
margin: 0;
font-size: larger;
}
.left-section ::deep button {
font-size: 1rem;
}
.left-section ::deep .mud-button-icon-start {
margin-right: 3px !important;
}
.header-content.no-back .page-title {
margin: 0;
}

View File

@@ -1,4 +1,7 @@
@inherits LayoutComponentBase
@using Template.Shared.Core.Messages
@inherits LayoutComponentBase
@inject IJSRuntime JS
@inject BackNavigationService BackService
<MudThemeProvider Theme="_currentTheme" />
<MudPopoverProvider />
@@ -23,8 +26,16 @@
{
Primary = "#ABA9BF",
Secondary = "#BEB7DF",
Tertiary = "#D4F2D2"
Tertiary = "#B2FDAD"
}
};
protected override void OnInitialized()
{
BackService.OnHardwareBack += async () =>
{
await JS.InvokeVoidAsync("goBack");
};
}
}

View File

@@ -3,6 +3,8 @@
position: fixed;
bottom: 0;
width: 100%;
z-index: 1001;
border-top: 1px solid var(--card-border-color);
}
.navbar-expand { padding: 0 !important; }

View File

@@ -0,0 +1,20 @@
@if (Elements is not null)
{
<div class="container-loader">
<span>Download risorse in corso</span>
<div>
@foreach (var element in Elements)
{
<div class="progress-content">
<span>@element.Key</span>
<MudProgressLinear Indeterminate="@(!element.Value)" Value="100" Rounded="true" Color="@(element.Value ? Color.Tertiary : Color.Secondary)" Size="Size.Large" />
</div>
}
</div>
</div>
}
@code
{
[Parameter] public Dictionary<string, bool>? Elements { get; set; }
}

View File

@@ -0,0 +1,27 @@
.container-loader {
display: flex;
height: 95vh;
flex-direction: column;
justify-content: center;
padding: 0 1rem;
align-items: center;
gap: 5vh;
}
.container-loader > div {
width: 100%;
}
.container-loader > span {
font-weight: 900;
font-size: large;
color: var(--mud-palette-primary);
}
.progress-content > span {
font-weight: 700;
}
.progress-content:nth-last-child(2) {
margin: 10px 0;
}

View File

@@ -47,14 +47,15 @@
<div class="card-container">
@if (FilterByDay)
{
<DayView/>
<DayView @bind-Date="DateFilter"/>
}
else if (FilterByWeek)
{
<WeekView @bind-Date="DateRangeFilter" />
}
else if (FilterByMonth)
{
<MonthView @bind-Date="DateTimeForMonthView" />
<MonthView @bind-Date="DateTimeForMonthView"/>
}
</div>

View File

@@ -1,12 +1,13 @@
.activity-filter { margin-top: .5rem; }
.activity-filter { margin-top: .2rem; }
.card-container {
margin-top: .5rem;
margin-top: .2rem;
width: 100%;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
gap: 1rem;
overflow: hidden;
}
.date-controller {
@@ -14,9 +15,13 @@
align-items: center;
}
.content ::deep > .custom-mudButtonGroup {
width: 100%;
}
.content ::deep > .custom-mudButtonGroup .mud-button-root {
border-radius: 12px;
padding: .5rem 1.5rem;
padding: .2rem 1.5rem;
text-transform: none !important;
font-size: .985rem;
border: 1px solid var(--mud-palette-gray-light);

View File

@@ -1,6 +1,5 @@
@page "/"
@using Template.Shared.Core.Interface
@using Template.Shared.Interfaces
@attribute [Authorize]
@inject IFormFactor FormFactor
@inject INetworkService NetworkService
@@ -9,12 +8,11 @@
{
protected override Task OnInitializedAsync()
{
var lastSyncDate = DateOnly.FromDateTime(LocalStorage.Get<DateTime>("last-sync"));
var lastSyncDate = LocalStorage.Get<DateTime>("last-sync");
if (!FormFactor.IsWeb() && NetworkService.IsNetworkAvailable() && lastSyncDate < DateOnly.FromDateTime(DateTime.Now))
if (!FormFactor.IsWeb() && NetworkService.IsNetworkAvailable() && lastSyncDate.Equals(DateTime.MinValue))
{
//NavigationManager.NavigateTo("/sync");
NavigationManager.NavigateTo("/Calendar");
NavigationManager.NavigateTo("/sync");
return base.OnInitializedAsync();
}

View File

@@ -57,4 +57,9 @@
.login-footer img {
height: 15px;
margin-left: 4px;
}
.container > .bg-white {
box-shadow: var(--card-shadow);
border: 1px solid var(--card-border-color);
}

View File

@@ -5,7 +5,6 @@
@using Template.Shared.Core.Interface
@using Template.Shared.Core.Services
@using Template.Shared.Core.Utility
@using Template.Shared.Interfaces
@inject AppAuthenticationStateProvider AuthenticationStateProvider
@inject INetworkService NetworkService
@inject IFormFactor FormFactor
@@ -75,16 +74,21 @@
</div>
</div>
<div class="user-button">
<span>Impostazioni account</span>
</div>
<MudButton Class="user-button"
FullWidth="true"
Size="Size.Medium"
StartIcon="@Icons.Material.Outlined.Settings"
OnClick="OpenSettings"
Variant="Variant.Outlined">Impostazioni</MudButton>
<div class="divider"></div>
<div class="user-button logout" @onclick="Logout">
<span>Esci</span>
<i class="ri-logout-box-line"></i>
</div>
<MudButton FullWidth="true"
StartIcon="@Icons.Material.Outlined.Logout"
Color="Color.Error"
Size="Size.Medium"
OnClick="Logout"
Variant="Variant.Outlined">Esci</MudButton>
</div>
@code {
@@ -96,11 +100,6 @@
await LoadData();
}
private void Logout()
{
AuthenticationStateProvider.SignOut();
}
private async Task LoadData()
{
await Task.Run(() =>
@@ -112,4 +111,10 @@
StateHasChanged();
}
private void OpenSettings() =>
NavigationManager.NavigateTo("/settings/Profilo");
private void Logout() =>
AuthenticationStateProvider.SignOut();
}

View File

@@ -9,7 +9,7 @@
flex-direction: column;
align-items: center;
line-height: normal;
margin: 2rem 0;
margin: .2rem 0 1rem 0;
}
.info-nome {
@@ -59,19 +59,8 @@
font-size: small;
}
.user-button {
border: 2px solid var(--card-border-color);
background: transparent;
text-align: center;
border-radius: 6px;
padding: .45rem 2rem;
width: 100%;
font-weight: 700;
line-height: normal;
}
.user-button.logout {
color: var(--mud-palette-error);
.content ::deep .user-button {
border: 1px solid var(--card-border-color) !important;
}
.user-button > i { font-size: large; }

View File

@@ -0,0 +1,28 @@
@page "/settings"
@page "/settings/{BackTo}"
@using Template.Shared.Components.Layout
<HeaderLayout BackTo="@BackTo" ShowNotifications="false" Back="true" Title="Impostazioni" />
<div class="content">
<MudButton Class="user-button"
FullWidth="true"
Size="Size.Medium"
StartIcon="@Icons.Material.Outlined.Sync"
OnClick="UpdateDb"
Variant="Variant.Outlined">Sincronizza</MudButton>
</div>
@code {
[Parameter] public string BackTo { get; set; } = "";
private void UpdateDb()
{
var absoluteUri = NavigationManager.ToAbsoluteUri(NavigationManager.Uri);
var pathAndQuery = absoluteUri.Segments.Length > 1 ? absoluteUri.PathAndQuery : null;
var path = pathAndQuery == null ? $"/sync/{DateTime.Today:yyyy-MM-dd}" : $"/sync/{DateTime.Today:yyyy-MM-dd}?path=" + System.Web.HttpUtility.UrlEncode(pathAndQuery);
NavigationManager.NavigateTo(path);
}
}

View File

@@ -0,0 +1,57 @@
@page "/sync"
@page "/sync/{DateFilter}"
@using Template.Shared.Components.Layout.Spinner
@using Template.Shared.Core.Interface
@inject ISyncDbService syncDb
<SyncSpinner Elements="@Elements"/>
@code {
[Parameter] public string? DateFilter { get; set; }
private Dictionary<string, bool> Elements { get; set; } = new();
protected override async Task OnInitializedAsync()
{
Elements.Add("Attività", false);
Elements.Add("Clienti", false);
Elements.Add("Commesse", false);
StateHasChanged();
await Task.WhenAll(SetActivity(), SetClienti(), SetCommesse());
LocalStorage.Set("last-sync", DateTime.Now);
var pathQuery = System.Web.HttpUtility.ParseQueryString(new UriBuilder(NavigationManager.Uri).Query);
var originalPath = pathQuery["path"] ?? null;
var path = originalPath ?? "/Calendar";
NavigationManager.NavigateTo(path);
}
private async Task SetActivity()
{
await syncDb.GetAndSaveActivity(DateFilter);
Elements["Attività"] = true;
StateHasChanged();
}
private async Task SetClienti()
{
await syncDb.GetAndSaveClienti(DateFilter);
await syncDb.GetAndSaveProspect(DateFilter);
Elements["Clienti"] = true;
StateHasChanged();
}
private async Task SetCommesse()
{
await syncDb.GetAndSaveCommesse(DateFilter);
Elements["Commesse"] = true;
StateHasChanged();
}
}

View File

@@ -1,6 +1,61 @@
<div class="calendar">
@for (var i = 0; i < 3; i++)
@using ConSegna.Shared.Core.Helpers
@using Template.Shared.Core.Dto
@using Template.Shared.Core.Interface
@inject IManageDataService manageData
<div class="calendar">
@if (!Activities.IsNullOrEmpty())
{
<ActivityCard Type="interna" />
@foreach (var activity in Activities!)
{
<ActivityCard Activity="activity"/>
}
}
</div>
else
{
<NoDataAvailable Text="Nessuna attività trovata" ImageSource="_content/Template.Shared/images/undraw_file-search_cbur.svg"/>
}
</div>
@code
{
[Parameter] public required DateTime? Date { get; set; }
[Parameter] public EventCallback<DateTime?> DateChanged { get; set; }
private List<ActivityDTO>? Activities { get; set; } = null;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await LoadData();
}
}
protected override async Task OnParametersSetAsync()
{
await LoadData();
}
private async Task LoadData()
{
await Task.Delay(1000);
var refreshActivity = await RefreshActivity();
Activities = refreshActivity;
StateHasChanged();
}
private async Task<List<ActivityDTO>> RefreshActivity()
{
var activityDto = await Task.Run(async () =>
{
return (await manageData.GetActivity(x =>
(x.EffectiveDate == null && x.EstimatedDate.Equals(Date)) || x.EffectiveDate.Equals(Date)))
.OrderBy(x => x.EffectiveDate ?? x.EstimatedDate)
.ToList();
});
return activityDto;
}
}

View File

@@ -4,4 +4,15 @@
flex-direction: column;
flex-wrap: nowrap;
gap: 1rem;
overflow-y: auto;
overflow-x: hidden;
padding-bottom: 1rem;
}
.calendar::-webkit-scrollbar { display: none; }
.calendar {
-ms-overflow-style: none;
scrollbar-width: none;
}

View File

@@ -42,7 +42,6 @@
@code
{
[Parameter] public required DateTime Date { get; set; }
[Parameter] public EventCallback<DateTime> DateChanged { get; set; }
private List<CalendarEvent> Events { get; set; }

View File

@@ -0,0 +1,76 @@
@using ConSegna.Shared.Core.Helpers
@using Template.Shared.Core.Dto
@using Template.Shared.Core.Interface
@inject IManageDataService manageData
<div class="calendar">
@{
DateTime? currentDate = null;
}
@if (!Activities.IsNullOrEmpty())
{
foreach (var activity in Activities!)
{
var dateToShow = activity.EffectiveDate ?? activity.EstimatedDate;
if (currentDate != dateToShow?.Date)
{
currentDate = dateToShow?.Date;
<div class="week-info">
<span>@($"{currentDate:D}")</span>
</div>
}
<ActivityCard Activity="activity"/>
}
}
else
{
<NoDataAvailable Text="Nessuna attività trovata" ImageSource="_content/Template.Shared/images/undraw_file-search_cbur.svg"/>
}
</div>
@code
{
[Parameter] public required DateRange Date { get; set; }
[Parameter] public EventCallback<DateRange> DateChanged { get; set; }
private List<ActivityDTO>? Activities { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await LoadData();
}
}
protected override async Task OnParametersSetAsync()
{
await LoadData();
}
private async Task LoadData()
{
await Task.Delay(1000);
var refreshActivity = await RefreshActivity();
Activities = refreshActivity;
StateHasChanged();
}
private async Task<List<ActivityDTO>> RefreshActivity()
{
var activityDto = await Task.Run(async () =>
{
return Activities = (await manageData.GetActivity(x =>
(x.EffectiveDate == null && x.EstimatedDate >= Date.Start && x.EstimatedDate <= Date.End) ||
(x.EffectiveDate >= Date.Start && x.EffectiveDate <= Date.End)
))
.OrderBy(x => x.EffectiveDate ?? x.EstimatedDate)
.ToList();
});
return activityDto;
}
}

View File

@@ -0,0 +1,26 @@
.calendar {
width: 100%;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
gap: 1rem;
overflow-y: auto;
overflow-x: hidden;
padding-bottom: 1rem;
}
.calendar::-webkit-scrollbar { display: none; }
.calendar {
-ms-overflow-style: none;
scrollbar-width: none;
}
.week-info {
background: var(--mud-palette-action-disabled-background);
width: 100%;
padding: .3rem .5rem;
border-radius: 8px;
text-transform: capitalize;
font-weight: 700;
}

View File

@@ -1,22 +1,52 @@
<div class="activity-card @Type">
@using Template.Shared.Core.Dto
<div class="activity-card @Activity.Category.ConvertToHumanReadable()">
<div class="activity-left-section">
<div class="activity-hours-section">
<span class="activity-hours">14:00</span>
<MudChip T="string" Icon="@IconConstants.Chip.Time" Color="Color.Dark" Size="Size.Small">1h</MudChip>
<span class="activity-hours">
@if (Activity.EffectiveTime is null)
{
@($"{Activity.EstimatedTime:t}")
}
else
{
@($"{Activity.EffectiveTime:t}")
}
</span>
@if (Durata != null)
{
<MudChip T="string" Icon="@IconConstants.Chip.Time" Color="Color.Dark" Size="Size.Small">@($"{Durata.Value.TotalHours:####}h")</MudChip>
}
</div>
<div class="activity-body-section">
<span class="activity-title">Format</span>
<span class="activity-subtitle">Preparazione preventivo</span>
<MudText Class="activity-title" Typo="Typo.button" HtmlTag="h3">@Activity.Commessa</MudText>
<MudText Class="activity-subtitle" Typo="Typo.caption">@Activity.ActivityDescription</MudText>
</div>
</div>
<div class="activity-info-section">
<MudChip T="string" Icon="@IconConstants.Chip.Stato" Size="Size.Small" Color="Color.Success">Completata</MudChip>
<MudChip T="string" Icon="@IconConstants.Chip.User" Size="Size.Small">GMANCINI</MudChip>
@if (Activity.ActivityResultId != null)
{
<MudChip T="string" Icon="@IconConstants.Chip.Stato" Size="Size.Small" Color="Color.Success">@Activity.ActivityResultId</MudChip>
}
<MudChip T="string" Icon="@IconConstants.Chip.User" Size="Size.Small">@Activity.UserName</MudChip>
</div>
</div>
@code {
[Parameter] public string Type { get; set; } = "";
[Parameter] public ActivityDTO Activity { get; set; } = new();
private TimeSpan? Durata { get; set; }
private Color ColorStatus { get; set; }
protected override void OnInitialized()
{
Durata = Activity switch
{
{ EffectiveTime: not null, EffectiveEndtime: not null } => Activity.EffectiveEndtime.Value - Activity.EffectiveTime.Value,
{ EstimatedTime: not null, EstimatedEndtime: not null } => Activity.EstimatedEndtime.Value - Activity.EstimatedTime.Value,
_ => Durata
};
}
}

View File

@@ -40,14 +40,16 @@
flex-direction: column;
}
.activity-title {
font-weight: 800;
font-size: medium;
.activity-body-section ::deep > .activity-title {
font-weight: 800 !important;
margin: 0 0 .2rem 0 !important;
line-height: normal !important;
}
.activity-subtitle {
.activity-body-section ::deep > .activity-subtitle {
font-size: smaller;
color: var(--mud-palette-gray-darker)
color: var(--mud-palette-gray-darker);
line-height: normal !important;
}
.activity-info-section { width: min-content; }

View File

@@ -1,6 +1,5 @@
<div class="no-data opacity-75 d-flex flex-column align-items-center">
<img
src="@ImageSource"/>
<img src="@ImageSource"/>
<p class="mt-3">@Text</p>
</div>

View File

@@ -1,7 +1,7 @@
.no-data {
position: fixed;
top: 35%;
width: calc(100% - 3rem); /* remove page padding */
width: calc(100vw - (var(--bs-gutter-x) * .5) * 2);
}
.no-data img {