2 Commits

Author SHA1 Message Date
83264731f3 Migliorato modal activity 2025-09-15 17:11:17 +02:00
0f3047a2b6 Gestito notificationData nelle notifiche push 2025-09-12 17:37:36 +02:00
11 changed files with 234 additions and 133 deletions

View File

@@ -1,8 +1,11 @@
using CommunityToolkit.Mvvm.Messaging;
using salesbook.Shared.Core.Dto;
using salesbook.Shared.Core.Entity;
using salesbook.Shared.Core.Helpers;
using salesbook.Shared.Core.Interface.IntegryApi;
using salesbook.Shared.Core.Messages.Notification.NewPush;
using Shiny.Push;
using System.Text.Json;
namespace salesbook.Maui.Core.System.Notification.Push;
@@ -20,10 +23,25 @@ public class PushNotificationDelegate(
public Task OnReceived(PushNotification notification)
{
if (notification.Notification is null) return Task.CompletedTask;
var data = notification.Data;
NotificationDataDTO? notificationDataDto = null;
if (!data.IsNullOrEmpty())
{
var json = JsonSerializer.Serialize(data);
notificationDataDto = JsonSerializer.Deserialize<NotificationDataDTO>(json);
}
if (notificationDataDto?.NotificationId == null) return Task.CompletedTask;
var notificationId = long.Parse(notificationDataDto.NotificationId);
var pushNotification = new WtbNotification
{
Id = notificationId,
Title = notification.Notification.Title,
Body = notification.Notification.Message
Body = notification.Notification.Message,
NotificationData = notificationDataDto
};
messenger.Send(new NewPushNotificationMessage(pushNotification));

View File

@@ -77,11 +77,11 @@
[JSInvokable]
public async Task Delete(string id)
{
Loading = true;
_ = InvokeAsync(StateHasChanged);
if (!long.TryParse(id, out var notificationId)) return;
Loading = true;
StateHasChanged();
var removed = false;
if (Notification.ReceivedNotifications.RemoveAll(x => x.Id == notificationId) > 0)
@@ -91,16 +91,18 @@
else if (Notification.NotificationsRead.RemoveAll(x => x.Id == notificationId) > 0)
removed = true;
if (!removed) return;
if (!removed)
{
Loading = false;
StateHasChanged();
return;
}
await IntegryNotificationRestClient.Delete(notificationId);
NotificationService.OrderNotificationList();
Loading = false;
_ = InvokeAsync(StateHasChanged);
_ = Task.Run(() =>
{
_ = IntegryNotificationRestClient.Delete(notificationId);
});
StateHasChanged();
Messenger.Send(new NotificationsLoadedMessage());
}
@@ -109,7 +111,7 @@
public async Task MarkAsRead(string id)
{
Loading = true;
_ = InvokeAsync(StateHasChanged);
StateHasChanged();
var notificationId = long.Parse(id);

View File

@@ -47,11 +47,11 @@
Notification.StartDate < DateTime.Today && Notification.Body != null && Notification.Body.Contains("Oggi")
)
{
<MudText Typo="Typo.caption" Class="subtitle">@Notification.Body.Replace("Oggi", $"{Notification.StartDate:d}")</MudText>
<div class="subtitle">@Notification.Body.Replace("Oggi", $"{Notification.StartDate:d}")</div>
}
else
{
<MudText Typo="Typo.caption" Class="subtitle">@Notification.Body</MudText>
<div class="subtitle">@Notification.Body</div>
}
</div>
</div>

View File

@@ -105,4 +105,6 @@
.notification-body ::deep > .subtitle {
font-size: 12px;
color: var(--mud-palette-drawer-text);
line-height: inherit;
margin-top: .5rem;
}

View File

@@ -40,19 +40,18 @@
<div class="form-container">
<span class="disable-full-width">Commessa</span>
@if (Commesse.IsNullOrEmpty())
{
<span class="warning-text">Nessuna commessa presente</span>
}
else
{
<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.CodJcom} - {com.Descrizione}")</MudSelectItemExtended>
}
</MudSelectExtended>
}
<MudAutocomplete
Disabled="ActivityModel.Cliente.IsNullOrEmpty()"
T="JtbComt?"
@bind-Value="SelectedComessa"
@bind-Value:after="OnCommessaSelectedAfter"
SearchFunc="SearchCommesseAsync"
ToStringFunc="@(c => c == null ? string.Empty : $"{c.CodJcom} - {c.Descrizione}")"
Clearable="true"
ShowProgressIndicator="true"
DebounceInterval="300"
MaxItems="50"
Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code"/>
</div>
</div>
@@ -91,13 +90,19 @@
<div class="input-card">
<div class="form-container">
<span class="disable-full-width">Assegnata a</span>
<MudSelectExtended FullWidth="true" ReadOnly="IsView" T="string?" Variant="Variant.Text" @bind-Value="ActivityModel.UserName" @bind-Value:after="OnUserChanged" Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var user in Users)
{
<MudSelectItemExtended Class="custom-item-select" Value="@user.UserName">@user.FullName</MudSelectItemExtended>
}
</MudSelectExtended>
<MudAutocomplete
Disabled="Users.IsNullOrEmpty()"
T="StbUser"
@bind-Value="SelectedUser"
@bind-Value:after="OnUserSelectedAfter"
SearchFunc="SearchUtentiAsync"
ToStringFunc="@(u => u == null ? string.Empty : u.FullName)"
Clearable="true"
ShowProgressIndicator="true"
DebounceInterval="300"
MaxItems="50"
Class="customIcon-select"
AdornmentIcon="@Icons.Material.Filled.Code"/>
</div>
<div class="divider"></div>
@@ -213,7 +218,7 @@
<MudMessageBox @ref="AddNamePosition" Class="c-messageBox" Title="Nome della posizione" CancelText="Annulla">
<MessageContent>
<MudTextField @bind-Value="NamePosition" Variant="Variant.Outlined" />
<MudTextField @bind-Value="NamePosition" Variant="Variant.Outlined"/>
</MessageContent>
<YesButton>
<MudButton Size="Size.Small" Variant="Variant.Filled" Color="Color.Primary"
@@ -282,6 +287,15 @@
private string? NamePosition { get; set; }
private bool CanAddPosition { get; set; } = true;
// cache per commesse
private string? _lastLoadedCodAnag;
//Commessa
private JtbComt? SelectedComessa { get; set; }
//User
private StbUser? SelectedUser { get; set; }
protected override async Task OnInitializedAsync()
{
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
@@ -291,24 +305,24 @@
ActivityModel = ActivityCopied.Clone();
}
else if (!Id.IsNullOrEmpty())
{
ActivityModel = (await ManageData.GetActivity(new WhereCondActivity { ActivityId = Id }, true)).Last();
}
if (Id.IsNullOrEmpty()) Id = ActivityModel.ActivityId;
IsNew = Id.IsNullOrEmpty();
LabelSave = IsNew ? "Aggiungi" : null;
_ = LoadData();
await LoadCommesse();
if (IsNew)
{
ActivityModel.EstimatedTime = DateTime.Today.Add(TimeSpan.FromHours(DateTime.Now.Hour));
ActivityModel.EstimatedEndtime = DateTime.Today.Add(TimeSpan.FromHours(DateTime.Now.Hour) + TimeSpan.FromHours(1));
ActivityModel.EstimatedTime = DateTime.Today.AddHours(DateTime.Now.Hour);
ActivityModel.EstimatedEndtime = ActivityModel.EstimatedTime?.AddHours(1);
ActivityModel.UserName = UserSession.User.Username;
}
await LoadActivityType();
_ = LoadData();
await LoadActivityType();
OriginalModel = ActivityModel.Clone();
}
@@ -320,67 +334,59 @@
StateHasChanged();
await SavePosition();
var response = await IntegryApiService.SaveActivity(ActivityModel);
if (response == null)
return;
var newActivity = response.Last();
await ManageData.InsertOrUpdate(newActivity);
await SaveAttached(newActivity.ActivityId!);
SuccessAnimation = true;
StateHasChanged();
await Task.Delay(1250);
MudDialog.Close(newActivity);
}
private async Task SavePosition()
{
if (AttachedList != null)
{
foreach (var attached in AttachedList)
{
if (attached.Type != AttachedDTO.TypeAttached.Position) continue;
if (AttachedList is null) return;
var positionTasks = AttachedList
.Where(a => a.Type == AttachedDTO.TypeAttached.Position)
.Select(async attached =>
{
var position = new PositionDTO
{
Description = attached.Description,
Lat = attached.Lat,
Lng = attached.Lng
};
ActivityModel.Position = await IntegryApiService.SavePosition(position);
}
}
});
await Task.WhenAll(positionTasks);
}
private async Task SaveAttached(string activityId)
{
if (AttachedList != null)
{
foreach (var attached in AttachedList)
{
if (attached.FileContent is not null && attached.Type != AttachedDTO.TypeAttached.Position)
{
await IntegryApiService.UploadFile(activityId, attached.FileBytes, attached.Name);
}
}
}
if (AttachedList is null) return;
var uploadTasks = AttachedList
.Where(a => a.FileContent is not null && a.Type != AttachedDTO.TypeAttached.Position)
.Select(a => IntegryApiService.UploadFile(activityId, a.FileBytes, a.Name));
await Task.WhenAll(uploadTasks);
}
private bool CheckPreSave()
{
Snackbar.Clear();
if (!ActivityModel.ActivityTypeId.IsNullOrEmpty()) return true;
Snackbar.Add("Tipo attività obbligatorio!", Severity.Error);
Snackbar.Add("Tipo attività obbligatorio!", Severity.Error);
return false;
}
@@ -389,13 +395,14 @@
return Task.Run(async () =>
{
if (!IsNew && Id != null)
{
ActivityFileList = await IntegryApiService.GetActivityFile(Id);
}
Users = await ManageData.GetTable<StbUser>();
if (!ActivityModel.UserName.IsNullOrEmpty())
SelectedUser = Users.FindLast(x => x.UserName.Equals(ActivityModel.UserName));
ActivityResult = await ManageData.GetTable<StbActivityResult>();
Clienti = await ManageData.GetClienti(new WhereCondContact {FlagStato = "A"});
Clienti = await ManageData.GetClienti(new WhereCondContact { FlagStato = "A" });
Pros = await ManageData.GetProspect();
await InvokeAsync(StateHasChanged);
@@ -404,76 +411,137 @@
private async Task LoadActivityType()
{
if (ActivityModel.UserName is null) ActivityType = [];
if (ActivityModel.UserName is null)
{
ActivityType = [];
return;
}
ActivityType = await ManageData.GetTable<SrlActivityTypeUser>(x =>
x.UserName != null && x.UserName.Equals(ActivityModel.UserName)
ActivityType = await ManageData.GetTable<SrlActivityTypeUser>(x => x.UserName != null && x.UserName.Equals(ActivityModel.UserName)
);
}
private async Task LoadCommesse() =>
Commesse = await ManageData.GetTable<JtbComt>(x => x.CodAnag != null && x.CodAnag.Equals(ActivityModel.CodAnag));
private async Task<IEnumerable<string>?> SearchCliente(string value, CancellationToken token)
private async Task LoadCommesse()
{
if (string.IsNullOrEmpty(value))
return null;
if (_lastLoadedCodAnag == ActivityModel.CodAnag) return;
var listToReturn = new List<string>();
listToReturn.AddRange(
Clienti.Where(x => x.RagSoc.Contains(value, StringComparison.OrdinalIgnoreCase)).Select(x => $"{x.CodAnag} - {x.RagSoc}")
);
listToReturn.AddRange(
Pros.Where(x => x.RagSoc.Contains(value, StringComparison.OrdinalIgnoreCase)).Select(x => $"{x.CodPpro} - {x.RagSoc}")
);
return listToReturn;
Commesse = await ManageData.GetTable<JtbComt>(x => x.CodAnag == ActivityModel.CodAnag);
Commesse = Commesse.OrderByDescending(x => x.CodJcom).ToList();
_lastLoadedCodAnag = ActivityModel.CodAnag;
}
private async Task OnClienteChanged()
private async Task<IEnumerable<JtbComt>> SearchCommesseAsync(string value, CancellationToken token)
{
await LoadCommesse();
if (Commesse.IsNullOrEmpty()) return [];
IEnumerable<JtbComt> list;
if (string.IsNullOrWhiteSpace(value))
{
list = Commesse.OrderByDescending(x => x.CodJcom).Take(10);
}
else
{
list = Commesse
.Where(x => (x.CodJcom.Contains(value, StringComparison.OrdinalIgnoreCase)
|| x.Descrizione.Contains(value, StringComparison.OrdinalIgnoreCase))
&& (x.CodAnag == ActivityModel.CodAnag || ActivityModel.CodAnag == null))
.OrderByDescending(x => x.CodJcom)
.Take(50);
}
return token.IsCancellationRequested ? [] : list;
}
private Task<IEnumerable<StbUser>> SearchUtentiAsync(string value, CancellationToken token)
{
IEnumerable<StbUser> list;
if (string.IsNullOrWhiteSpace(value))
{
list = Users.OrderBy(u => u.FullName).Take(50);
}
else
{
list = Users
.Where(x => x.UserName.Contains(value, StringComparison.OrdinalIgnoreCase)
|| x.FullName.Contains(value, StringComparison.OrdinalIgnoreCase))
.OrderBy(u => u.FullName)
.Take(50);
}
return Task.FromResult(token.IsCancellationRequested ? [] : list);
}
private Task<IEnumerable<string>?> SearchCliente(string value, CancellationToken token)
{
if (string.IsNullOrWhiteSpace(value))
return Task.FromResult<IEnumerable<string>?>(null);
var results = new List<string>();
results.AddRange(Clienti
.Where(x => x.RagSoc.Contains(value, StringComparison.OrdinalIgnoreCase))
.Select(x => $"{x.CodAnag} - {x.RagSoc}"));
results.AddRange(Pros
.Where(x => x.RagSoc.Contains(value, StringComparison.OrdinalIgnoreCase))
.Select(x => $"{x.CodPpro} - {x.RagSoc}"));
return Task.FromResult<IEnumerable<string>?>(results);
}
private Task OnClienteChanged()
{
ActivityModel.CodJcom = null;
if (string.IsNullOrWhiteSpace(ActivityModel.Cliente)) return Task.CompletedTask;
if (ActivityModel.Cliente.IsNullOrEmpty()) return;
var parts = ActivityModel.Cliente.Split('-', 2, StringSplitOptions.TrimEntries);
if (parts.Length == 2)
{
ActivityModel.CodAnag = parts[0];
ActivityModel.Cliente = parts[1];
}
var match = Regex.Match(ActivityModel.Cliente!, @"^\s*(\S+)\s*-\s*(.*)$");
if (!match.Success)
return;
OnAfterChangeValue();
return Task.CompletedTask;
}
ActivityModel.CodAnag = match.Groups[1].Value;
ActivityModel.Cliente = match.Groups[2].Value;
private async Task OnCommessaSelectedAfter()
{
var com = SelectedComessa;
if (com != null)
{
ActivityModel.CodJcom = com.CodJcom;
ActivityModel.Commessa = com.Descrizione;
}
else
{
ActivityModel.CodJcom = null;
ActivityModel.Commessa = null;
}
await LoadCommesse();
OnAfterChangeValue();
}
private async Task OnCommessaChanged()
{
ActivityModel.Commessa = (await ManageData.GetTable<JtbComt>(x => x.CodJcom.Equals(ActivityModel.CodJcom))).Last().Descrizione;
OnAfterChangeValue();
}
private async Task OnUserChanged()
private async Task OnUserSelectedAfter()
{
ActivityModel.UserName = SelectedUser?.UserName;
await LoadActivityType();
OnAfterChangeValue();
}
private void OnAfterChangeTimeBefore()
{
if (ActivityModel.EstimatedTime != null)
if (ActivityModel.EstimatedTime is not null)
{
if (ActivityModel.MinuteBefore != -1)
ActivityModel.NotificationDate = ActivityModel.MinuteBefore switch
{
ActivityModel.NotificationDate = ActivityModel.MinuteBefore == 0 ?
ActivityModel.EstimatedTime : ActivityModel.EstimatedTime.Value.AddMinutes(ActivityModel.MinuteBefore * -1);
}
else
{
ActivityModel.NotificationDate = null;
}
-1 => null,
0 => ActivityModel.EstimatedTime,
_ => ActivityModel.EstimatedTime.Value.AddMinutes(ActivityModel.MinuteBefore * -1)
};
}
OnAfterChangeValue();
@@ -482,24 +550,21 @@
private void OnAfterChangeValue()
{
if (!IsNew)
{
LabelSave = !OriginalModel.Equals(ActivityModel) ? "Aggiorna" : null;
}
if (ActivityModel.EstimatedEndtime <= ActivityModel.EstimatedTime)
if (ActivityModel.EstimatedTime is not null &&
(ActivityModel.EstimatedEndtime is null || ActivityModel.EstimatedEndtime <= ActivityModel.EstimatedTime))
{
ActivityModel.EstimatedEndtime = ActivityModel.EstimatedTime.Value.AddHours(1);
}
StateHasChanged();
}
private async Task OnAfterChangeEsito()
private Task OnAfterChangeEsito()
{
OnAfterChangeValue();
// var result = await ConfirmMemo.ShowAsync();
// if (result is true)
// OpenAddMemo = !OpenAddMemo;
return Task.CompletedTask;
}
private void OpenSelectEsito()

View File

@@ -4,6 +4,9 @@ namespace salesbook.Shared.Core.Dto;
public class NotificationDataDTO
{
[JsonPropertyName("notificationId")]
public string? NotificationId { get; set; }
[JsonPropertyName("activityId")]
public string? ActivityId { get; set; }

View File

@@ -4,7 +4,7 @@ namespace salesbook.Shared.Core.Interface.IntegryApi;
public interface IIntegryNotificationRestClient
{
Task<List<WtbNotification>> Get();
Task<List<WtbNotification>?> Get();
Task<WtbNotification> MarkAsRead(long id);
Task Delete(long id);
Task DeleteAll();

View File

@@ -10,7 +10,7 @@ public class IntegryNotificationRestClient(
IUserSession userSession,
IIntegryApiRestClient integryApiRestClient) : IIntegryNotificationRestClient
{
public Task<List<WtbNotification>> Get()
public Task<List<WtbNotification>?> Get()
{
var queryParams = new Dictionary<string, object>
{
@@ -18,7 +18,7 @@ public class IntegryNotificationRestClient(
{ "forUser", userSession.User.Username }
};
return integryApiRestClient.Get<List<WtbNotification>>("notification", queryParams)!;
return integryApiRestClient.Get<List<WtbNotification>>("notification", queryParams);
}
public Task<WtbNotification> MarkAsRead(long id) =>

View File

@@ -12,6 +12,8 @@ public class NotificationService(
public async Task LoadNotification()
{
var allNotifications = await integryNotificationRestClient.Get();
if (allNotifications == null) return;
var allIds = allNotifications.Select(n => n.Id).ToHashSet();
Notification.ReceivedNotifications = Notification.ReceivedNotifications

View File

@@ -145,13 +145,17 @@ function initRow(row) {
}
function removeRow(row) {
//collapseAndRemove(row);
dotnetHelper.invokeMethodAsync('Delete', row.id);
const id = row.id;
collapseAndRemove(row);
dotnetHelper.invokeMethodAsync('Delete', id);
}
function markAsRead(row) {
//collapseAndRemove(row);
dotnetHelper.invokeMethodAsync('MarkAsRead', row.id);
const id = row.id;
collapseAndRemove(row);
dotnetHelper.invokeMethodAsync('MarkAsRead', id);
}
function collapseAndRemove(row) {

View File

@@ -14,12 +14,12 @@ public class ManageDataService : IManageDataService
throw new NotImplementedException();
}
public Task<List<AnagClie>> GetClienti(WhereCondContact? whereCond)
public Task<List<AnagClie>> GetClienti(WhereCondContact? whereCond = null)
{
throw new NotImplementedException();
}
public Task<List<PtbPros>> GetProspect(WhereCondContact? whereCond)
public Task<List<PtbPros>> GetProspect(WhereCondContact? whereCond = null)
{
throw new NotImplementedException();
}
@@ -34,6 +34,11 @@ public class ManageDataService : IManageDataService
throw new NotImplementedException();
}
public Task<List<ActivityDTO>> GetActivityTryLocalDb(WhereCondActivity whereCond)
{
throw new NotImplementedException();
}
public Task<List<ActivityDTO>> GetActivity(WhereCondActivity whereCond, bool useLocalDb = false)
{
throw new NotImplementedException();