This commit is contained in:
2025-06-24 10:56:02 +02:00
parent a97df74ef4
commit 10c1435dba
35 changed files with 509 additions and 185 deletions

View File

@@ -22,7 +22,7 @@
{
@if (ShowFilter)
{
<MudIconButton OnClick="OnFilterToggle" Icon="@Icons.Material.Outlined.FilterAlt" Color="Color.Dark"/>
<MudIconButton OnClick="OnFilterToggle" Icon="@Icons.Material.Outlined.FilterAlt"/>
}
@* @if (ShowCalendarToggle)
@@ -32,7 +32,7 @@
@if (ShowProfile)
{
<MudIconButton Class="user" OnClick="OpenPersonalInfo" Icon="@Icons.Material.Filled.Person" Color="Color.Dark"/>
<MudIconButton Class="user" OnClick="OpenPersonalInfo" Icon="@Icons.Material.Filled.Person"/>
}
}
else

View File

@@ -14,8 +14,6 @@
font-size: larger;
}
.right-section { min-height: 45px; }
.left-section ::deep button, .right-section ::deep button { font-size: 1.1rem; }
.left-section ::deep .mud-button-icon-start { margin-right: 3px !important; }

View File

@@ -4,13 +4,13 @@
@inject IJSRuntime JS
@inject BackNavigationService BackService
<MudThemeProvider Theme="_currentTheme" />
<MudPopoverProvider />
<MudDialogProvider />
<MudSnackbarProvider />
<MudThemeProvider Theme="_currentTheme" @ref="@_mudThemeProvider" @bind-IsDarkMode="@IsDarkMode" />
<MudPopoverProvider/>
<MudDialogProvider/>
<MudSnackbarProvider/>
<div class="page">
<NavMenu />
<NavMenu/>
<main>
<article>
@@ -21,22 +21,65 @@
</div>
@code {
private MudThemeProvider? _mudThemeProvider;
private bool IsDarkMode { get; set; }
private string _mainContentClass = "";
private readonly MudTheme _currentTheme = new()
{
PaletteLight = new PaletteLight()
{
Primary = "#ABA9BF",
Secondary = "#BEB7DF",
Tertiary = "#B2FDAD"
Primary = "#00a0de",
Secondary = "#002339",
Tertiary = "#dff2ff",
TextPrimary = "#000"
},
PaletteDark = new PaletteDark
{
Primary = "#00a0de",
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()
{
BackService.OnHardwareBack += async () =>
{
await JS.InvokeVoidAsync("goBack");
};
BackService.OnHardwareBack += async () => { await JS.InvokeVoidAsync("goBack"); };
var culture = new CultureInfo("it-IT", false);

View File

@@ -83,7 +83,9 @@
.nav-item ::deep a.active > div > span { font-weight: 800; }
.nav-item ::deep a:not(.active) > div { color: var(--mud-palette-text-primary); }
.nav-item ::deep a:not(.active) > div {
color: var(--mud-palette-text-primary);
}
.nav-item ::deep a i { font-size: 1.65rem; }

View File

@@ -350,13 +350,24 @@
if (Filter.ClearFilter)
return activities;
return activities.Where(x =>
(!Filter.Text.IsNullOrEmpty() && x.ActivityDescription != null && x.ActivityDescription.ContainsIgnoreCase(Filter.Text!)) &&
(x.ActivityTypeId != null && !Filter.Type.IsNullOrEmpty() && x.ActivityTypeId.Equals(Filter.Type)) &&
(x.ActivityResultId != null && !Filter.Result.IsNullOrEmpty() && x.ActivityResultId.Equals(Filter.Result)) &&
(x.UserName != null && !Filter.User.IsNullOrEmpty() && Filter.User!.Contains(x.UserName)) &&
(Filter.Category != null && x.Category.Equals(Filter.Category))
).ToList();
var filteredActivity = activities.AsQueryable();
filteredActivity = filteredActivity
.Where(x => Filter.Text.IsNullOrEmpty() || (x.ActivityDescription != null && x.ActivityDescription.ContainsIgnoreCase(Filter.Text!)));
filteredActivity = filteredActivity
.Where(x => Filter.Type.IsNullOrEmpty() || (x.ActivityTypeId != null && x.ActivityTypeId.Equals(Filter.Type)));
filteredActivity = filteredActivity
.Where(x => Filter.Result.IsNullOrEmpty() || (x.ActivityResultId != null && x.ActivityResultId.Equals(Filter.Result)));
filteredActivity = filteredActivity
.Where(x => Filter.User.IsNullOrEmpty() || (x.UserName != null && Filter.User!.Contains(x.UserName)));
filteredActivity = filteredActivity
.Where(x => Filter.Category == null || x.Category.Equals(Filter.Category));
return filteredActivity.ToList();
}
[JSInvokable]
@@ -511,7 +522,7 @@
}
private List<ActivityDTO> GetEventsForDay(DateTime day)
=> _eventsCache.TryGetValue(day.Date, out var events) ? events : new List<ActivityDTO>();
=> _eventsCache.TryGetValue(day.Date, out var events) ? events : [];
public void Dispose()
{

View File

@@ -71,12 +71,12 @@
gap: 0.2rem;
}
.week-day > div:first-child {
font-size: 0.8rem;
color: var(--mud-palette-primary);
margin-bottom: 0.2rem;
font-weight: 500;
}
.week-day > div:first-child {
font-size: 0.8rem;
color: var(--mud-palette-text-primary);
margin-bottom: 0.2rem;
font-weight: 500;
}
.day {
background: var(--mud-palette-surface);
@@ -85,13 +85,14 @@
cursor: pointer;
transition: background 0.3s ease, transform 0.2s ease;
font-size: 0.95rem;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
box-shadow: var(--custom-box-shadow);
width: 38px;
height: 38px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: var(--mud-palette-text-primary);
border: 1px solid var(--mud-palette-surface);
margin: 0 auto;
}
@@ -107,9 +108,9 @@
}
.day.selected {
background: var(--mud-palette-secondary);
border: 1px solid var(--mud-palette-secondary);
color: white;
background: var(--mud-palette-tertiary);
border: 1px solid var(--mud-palette-tertiary);
color: var(--mud-palette-secondary);
}
.day.today { border: 1px solid var(--mud-palette-primary); }
@@ -134,13 +135,13 @@
border-radius: 8px;
padding: 0.8rem;
margin-bottom: 0.5rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
box-shadow: var(--custom-box-shadow);
}
.toggle-month {
background: none;
border: none;
color: var(--mud-palette-primary);
color: var(--mud-palette-text-primary);
font-size: 1rem;
cursor: pointer;
}

View File

@@ -12,7 +12,7 @@ else
{
<div class="login-page">
<div class="container container-top-logo">
<span>SalesBook</span>
<img src="_content/Template.Shared/images/salesbook-marchio_vers.positiva.svg" class="logo" alt="sales book">
</div>
<div class="container container-login">
<div class="login-form-container">

View File

@@ -5,10 +5,8 @@
background: var(--mud-palette-surface);
}
.container-top-logo > span {
font-size: x-large;
font-weight: 900;
color: var(--mud-palette-primary)
.container-top-logo > .logo {
width: 50%;
}
.container-login > span {

View File

@@ -72,7 +72,15 @@ else
{
<div class="container-pers-rif">
<Virtualize Items="PersRif" Context="person">
<ContactCard Contact="person"/>
@{
var index = PersRif.IndexOf(person);
var isLast = index == PersRif.Count - 1;
}
<ContactCard Contact="person" />
@if (!isLast)
{
<div class="divider"></div>
}
</Virtualize>
</div>
}

View File

@@ -103,21 +103,19 @@
min-height: 25px;
}
.container-button ::deep .button-settings.infoText {
color: var(--mud-palette-info);
.container-button ::deep .button-settings.infoText { color: var(--mud-palette-info); }
.container-button ::deep .button-settings.green-icon .mud-icon-root {
border: 1px solid var(--mud-palette-success);
background: hsl(from var(--mud-palette-success-lighten) h s 95%);
color: var(--mud-palette-success-darken);
}
.container-button ::deep .button-settings.green-icon .mud-icon-root {
border: 1px solid var(--mud-palette-success);
background: hsl(from var(--mud-palette-success-lighten) h s 95%);
color: var(--mud-palette-success-darken);
}
.container-button ::deep .button-settings.red-icon .mud-icon-root {
border: 1px solid var(--mud-palette-error);
background: hsl(from var(--mud-palette-error-lighten) h s 95%);
color: var(--mud-palette-error-darken);
}
.container-button ::deep .button-settings.red-icon .mud-icon-root {
border: 1px solid var(--mud-palette-error);
background: hsl(from var(--mud-palette-error-lighten) h s 95%);
color: var(--mud-palette-error-darken);
}
.container-button ::deep .button-settings .mud-button-label {
justify-content: flex-start;
@@ -139,4 +137,11 @@
flex-direction: column;
gap: 1rem;
margin-bottom: 1rem;
box-shadow: var(--custom-box-shadow);
border-radius: 16px;
}
.container-pers-rif .divider {
margin: 0 0 0 3.5rem;
width: unset;
}

View File

@@ -7,15 +7,20 @@
<HeaderLayout Title="Contatti"/>
<div class="container search-box">
<div class="input-card clearButton">
<MudTextField T="string?" Placeholder="Cerca..." Variant="Variant.Text" @bind-Value="TextToFilter" OnDebounceIntervalElapsed="FilterUsers" DebounceInterval="500"/>
@if (!TextToFilter.IsNullOrEmpty())
{
<MudIconButton Class="closeIcon" Icon="@Icons.Material.Filled.Close" OnClick="() => FilterUsers(true)"/>
}
</div>
</div>
<div class="container users">
@if (GroupedUserList?.Count > 0)
{
<div class="input-card clearButton">
<MudTextField T="string?" Placeholder="Cerca..." Variant="Variant.Text" @bind-Value="TextToFilter" OnDebounceIntervalElapsed="FilterUsers" DebounceInterval="500"/>
<MudIconButton Class="closeIcon" Icon="@Icons.Material.Filled.Close" OnClick="() => FilterUsers(true)"/>
</div>
<Virtualize Items="FilteredGroupedUserList" Context="item">
@if (item.ShowHeader)
{
@@ -46,7 +51,7 @@
var sortedUsers = users
.Where(u => !string.IsNullOrWhiteSpace(u.RagSoc))
.OrderBy(u =>
.OrderBy(u =>
{
var firstChar = char.ToUpper(u.RagSoc[0]);
return char.IsLetter(firstChar) ? firstChar.ToString() : "ZZZ";
@@ -98,7 +103,14 @@
if (TextToFilter == null) return;
FilteredGroupedUserList = GroupedUserList.FindAll(x => x.User.RagSoc.Contains(TextToFilter, StringComparison.OrdinalIgnoreCase));
FilteredGroupedUserList = GroupedUserList.FindAll(x =>
x.User.RagSoc.Contains(TextToFilter, StringComparison.OrdinalIgnoreCase) ||
x.User.Indirizzo.Contains(TextToFilter, StringComparison.OrdinalIgnoreCase) ||
(x.User.Telefono != null && x.User.Telefono.Contains(TextToFilter, StringComparison.OrdinalIgnoreCase)) ||
(x.User.EMail != null && x.User.EMail.Contains(TextToFilter, StringComparison.OrdinalIgnoreCase)) ||
x.User.PartIva.Contains(TextToFilter, StringComparison.OrdinalIgnoreCase)
);
StateHasChanged();
}

View File

@@ -19,4 +19,15 @@
.letter-header {
border-bottom: 1px solid var(--card-border-color);
font-weight: bolder;
}
.search-box {
position: sticky;
top: 0;
background: var(--mud-palette-surface);
padding-bottom: .5rem;
}
.search-box .input-card {
margin: 0 !important;
}

View File

@@ -0,0 +1,151 @@
@using Template.Shared.Core.Dto
@using Template.Shared.Core.Entity
@using Template.Shared.Core.Helpers.Enum
@using Template.Shared.Core.Interface
@inject IManageDataService manageData
<div class="bottom-sheet-backdrop @(IsSheetVisible ? "show" : "")" @onclick="CloseBottomSheet"></div>
<div class="bottom-sheet-container @(IsSheetVisible ? "show" : "")">
<div class="bottom-sheet pb-safe-area">
<div class="title">
<MudText Typo="Typo.h6">
<b>Filtri</b>
</MudText>
<MudIconButton Icon="@Icons.Material.Filled.Close" OnClick="CloseBottomSheet"/>
</div>
<div class="input-card clearButton">
<MudTextField T="string?" Placeholder="Cerca..." Variant="Variant.Text" @bind-Value="Filter.Text" DebounceInterval="500"/>
<MudIconButton Class="closeIcon" Icon="@Icons.Material.Filled.Close" OnClick="() => Filter.Text = null"/>
</div>
<div class="input-card">
<div class="form-container">
<span class="disable-full-width">Assegnata a</span>
<MudSelectExtended SearchBox="true"
ItemCollection="Users.Select(x => x.UserName).ToList()"
SelectAllPosition="SelectAllPosition.NextToSearchBox"
SelectAll="true"
NoWrap="true"
MultiSelection="true"
MultiSelectionTextFunc="@(new Func<List<string>, string>(GetMultiSelectionUser))"
FullWidth="true" T="string"
Variant="Variant.Text"
Virtualize="true"
@bind-SelectedValues="Filter.User"
Class="customIcon-select"
AdornmentIcon="@Icons.Material.Filled.Code"/>
</div>
<div class="divider"></div>
<div class="form-container">
<span class="disable-full-width">Tipo</span>
<MudSelectExtended FullWidth="true"
T="string?"
Variant="Variant.Text"
@bind-Value="Filter.Type"
Class="customIcon-select"
AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var type in ActivityType)
{
<MudSelectItemExtended Class="custom-item-select" Value="@type.ActivityTypeId">@type.ActivityTypeId</MudSelectItemExtended>
}
</MudSelectExtended>
</div>
<div class="divider"></div>
<div class="form-container">
<span class="disable-full-width">Esito</span>
<MudSelectExtended FullWidth="true"
T="string?"
Variant="Variant.Text"
@bind-Value="Filter.Result"
Class="customIcon-select"
AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var result in ActivityResult)
{
<MudSelectItemExtended Class="custom-item-select" Value="@result.ActivityResultId">@result.ActivityResultId</MudSelectItemExtended>
}
</MudSelectExtended>
</div>
<div class="divider"></div>
<div class="form-container">
<span class="disable-full-width">Categoria</span>
<MudSelectExtended FullWidth="true"
T="ActivityCategoryEnum?"
Variant="Variant.Text"
@bind-Value="Filter.Category"
Class="customIcon-select"
AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var category in CategoryList)
{
<MudSelectItemExtended T="ActivityCategoryEnum?" Class="custom-item-select" Value="@category">@category.ConvertToHumanReadable()</MudSelectItemExtended>
}
</MudSelectExtended>
</div>
</div>
<div class="button-section">
<MudButton OnClick="() => Filter = new FilterActivityDTO()" Variant="Variant.Outlined" Color="Color.Error">Pulisci</MudButton>
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="OnFilterButton">Filtra</MudButton>
</div>
</div>
</div>
@code {
[Parameter] public bool IsSheetVisible { get; set; }
[Parameter] public EventCallback<bool> IsSheetVisibleChanged { get; set; }
[Parameter] public FilterActivityDTO Filter { get; set; }
[Parameter] public EventCallback<FilterActivityDTO> FilterChanged { get; set; }
private List<StbActivityResult> ActivityResult { get; set; } = [];
private List<StbActivityType> ActivityType { get; set; } = [];
private List<StbUser> Users { get; set; } = [];
private List<ActivityCategoryEnum> CategoryList { get; set; } = [];
protected override async Task OnParametersSetAsync()
{
if (IsSheetVisible)
await LoadData();
}
private string GetMultiSelectionUser(List<string> selectedValues)
{
return $"{selectedValues.Count} Utent{(selectedValues.Count != 1 ? "i selezionati" : "e selezionato")}";
}
private async Task LoadData()
{
Users = await manageData.GetTable<StbUser>();
ActivityResult = await manageData.GetTable<StbActivityResult>();
ActivityType = await manageData.GetTable<StbActivityType>(x => x.FlagTipologia.Equals("A"));
CategoryList = ActivityCategoryHelper.AllActivityCategory;
StateHasChanged();
}
private void CloseBottomSheet()
{
IsSheetVisible = false;
IsSheetVisibleChanged.InvokeAsync(IsSheetVisible);
}
private void OnFilterButton()
{
FilterChanged.InvokeAsync(Filter);
CloseBottomSheet();
}
}

View File

@@ -27,7 +27,10 @@
width: 100%;
}
.activity-hours { font-weight: 700; }
.activity-hours {
font-weight: 700;
color: var(--mud-palette-text-primary);
}
.activity-hours-section ::deep .mud-chip { margin: 5px 0 0 !important; }
@@ -41,6 +44,7 @@
font-weight: 800 !important;
margin: 0 !important;
line-height: normal !important;
color: var(--mud-palette-text-primary);
}
.activity-body-section ::deep > .activity-subtitle {

View File

@@ -2,20 +2,28 @@
<div class="contact-card">
<div class="contact-left-section">
<MudIcon Color="Color.Default" Icon="@Icons.Material.Filled.PersonOutline" Size="Size.Large" />
<div class="contact-body-section">
<div class="title-section">
<MudText Class="contact-title" Typo="Typo.body1" HtmlTag="h3">@Contact.PersonaRif</MudText>
<MudText Class="contact-title" Typo="Typo.body1" HtmlTag="h3">@UtilityString.FormatString(Contact.PersonaRif).TitleCase</MudText>
</div>
@if (Contact.Mansione is not null)
{
<MudText Class="contact-subtitle" Typo="Typo.body1" HtmlTag="p">@Contact.Mansione</MudText>
<MudText Class="contact-subtitle" Typo="Typo.body1" HtmlTag="p">@UtilityString.FormatString(Contact.Mansione).SentenceCase</MudText>
}
</div>
</div>
<div class="contact-right-section">
<MudIcon Color="Color.Success" Disabled="@(Contact.NumCellulare.IsNullOrEmpty())" Size="Size.Large" Icon="@Icons.Material.Outlined.Phone" />
<MudIcon Color="Color.Info" Disabled="@(Contact.EMail.IsNullOrEmpty())" Size="Size.Large" Icon="@Icons.Material.Filled.MailOutline" />
@if (!Contact.NumCellulare.IsNullOrEmpty())
{
<MudIcon Color="Color.Success" Size="Size.Large" Icon="@Icons.Material.Outlined.Phone" />
}
@if (!Contact.EMail.IsNullOrEmpty()){
<MudIcon Color="Color.Info" Size="Size.Large" Icon="@Icons.Material.Filled.MailOutline" />
}
</div>
</div>

View File

@@ -2,10 +2,9 @@
width: 100%;
display: flex;
flex-direction: row;
padding: .5rem .5rem;
padding: 0 .75rem;
border-radius: 16px;
line-height: normal;
box-shadow: var(--custom-box-shadow);
justify-content: space-between;
align-items: center;
}
@@ -19,7 +18,7 @@
.contact-left-section {
display: flex;
align-items: center;
margin-left: 4px;
gap: 1rem;
}
.title-section {
@@ -40,7 +39,7 @@
}
.title-section ::deep > .contact-title {
font-weight: 800 !important;
font-weight: 700 !important;
margin: 0 !important;
line-height: normal !important;
}

View File

@@ -27,14 +27,16 @@
@foreach (var commessa in Commesse!)
{
<div class="commessa">
<span>@($"{commessa.CodAnag} - {commessa.Descrizione}")</span>
<span>@($"{commessa.CodJcom} - {commessa.Descrizione}")</span>
</div>
}
</div>
}
else
{
<span>Nessuna commessa presente</span>
<div class="commessa">
<span>Nessuna commessa presente</span>
</div>
}
}
</div>

View File

@@ -61,7 +61,7 @@
}
.title-section ::deep > .mud-icon-root {
background: var(--gray-for-shadow);
background: var(--mud-palette-gray-lighter);
padding: .25rem;
border-radius: 50%;
}

View File

@@ -14,12 +14,13 @@
}
</div>
<div class="button-container">
<div @onclick="OnLogoutClick" class="card-button">
<span>Logout</span>
</div>
<div @onclick="OnRetryClick" class="card-button">
<span>Riprova</span>
</div>
<div @onclick="OnContinueClick" class="card-button">
<span>Continua</span>
</div>
</div>
</div>
</div>
@@ -53,10 +54,9 @@
await OnRetry.InvokeAsync();
}
private async Task OnLogoutClick()
private async Task OnContinueClick()
{
LocalStorage.Remove("last-sync");
AuthenticationStateProvider.SignOut();
NavigationManager.NavigateTo("/");
await OnRetry.InvokeAsync();
}
}

View File

@@ -33,7 +33,7 @@
<MudSelectExtended FullWidth="true" ReadOnly="@(IsView || Commesse.IsNullOrEmpty())" T="string?" Variant="Variant.Text" @bind-Value="ActivityModel.CodJcom" @bind-Value:after="OnCommessaChanged" Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var com in Commesse)
{
<MudSelectItemExtended Class="custom-item-select" Value="@com.CodJcom">@com.Descrizione</MudSelectItemExtended>
<MudSelectItemExtended Class="custom-item-select" Value="@com.CodJcom">@($"{com.CodJcom} - {com.Descrizione}")</MudSelectItemExtended>
}
</MudSelectExtended>
</div>
@@ -160,6 +160,8 @@
private async Task Save()
{
if (!CheckPreSave()) return;
VisibleOverlay = true;
StateHasChanged();
@@ -180,6 +182,17 @@
MudDialog.Close(newActivity);
}
private bool CheckPreSave()
{
Snackbar.Clear();
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
if (!ActivityModel.ActivityTypeId.IsNullOrEmpty()) return true;
Snackbar.Add("Tipo attività obbligatorio!", Severity.Error);
return false;
}
private async Task LoadData()
{
Users = await ManageData.GetTable<StbUser>();
@@ -187,6 +200,11 @@
Clienti = await ManageData.GetTable<AnagClie>(x => x.FlagStato.Equals("A"));
Pros = await ManageData.GetTable<PtbPros>();
ActivityType = await ManageData.GetTable<StbActivityType>(x => x.FlagTipologia.Equals("A"));
if (IsNew)
{
await LoadCommesse();
}
}
private async Task LoadCommesse() =>
@@ -246,9 +264,10 @@
private void OnAfterChangeValue()
{
LabelSave = !OriginalModel.Equals(ActivityModel) ? "Aggiorna" : null;
StateHasChanged();
if (!IsNew)
{
LabelSave = !OriginalModel.Equals(ActivityModel) ? "Aggiorna" : null;
}
}
private void OpenSelectEsito()