Completata gestione allegati e riepilogo commessa

This commit is contained in:
2025-09-01 17:38:16 +02:00
parent 588dbe308a
commit 8be3fa9f9e
17 changed files with 341 additions and 60 deletions

View File

@@ -57,9 +57,53 @@ public class AttachedService : IAttachedService
Name = file.FileName, Name = file.FileName,
Path = file.FullPath, Path = file.FullPath,
MimeType = file.ContentType, MimeType = file.ContentType,
DimensionBytes= ms.Length, DimensionBytes = ms.Length,
FileContent = ms.ToArray(), FileBytes = ms.ToArray(),
Type = type Type = type
}; };
} }
private static async Task<string?> SaveToTempStorage(Stream file, string fileName)
{
var cacheDirectory = FileSystem.CacheDirectory;
var targetDirectory = Path.Combine(cacheDirectory, "file");
if (!Directory.Exists(targetDirectory)) Directory.CreateDirectory(targetDirectory);
var tempFilePath = Path.Combine(targetDirectory, fileName + ".temp");
var filePath = Path.Combine(targetDirectory, fileName);
if (File.Exists(filePath)) return filePath;
try
{
await using var fileStream =
new FileStream(tempFilePath, FileMode.Create, FileAccess.Write, FileShare.None);
await file.CopyToAsync(fileStream);
File.Move(tempFilePath, filePath);
}
catch (Exception e)
{
Console.WriteLine($"Errore durante il salvataggio dello stream: {e.Message}");
return null;
}
finally
{
if (File.Exists(tempFilePath)) File.Delete(tempFilePath);
}
return filePath;
}
public async Task OpenFile(Stream file, string fileName)
{
var filePath = await SaveToTempStorage(file, fileName);
if (filePath is null) return;
await Launcher.OpenAsync(new OpenFileRequest
{
File = new ReadOnlyFile(filePath)
});
}
} }

View File

@@ -1,4 +1,5 @@
using salesbook.Shared.Core.Helpers; using salesbook.Shared.Core.Dto;
using salesbook.Shared.Core.Helpers;
using salesbook.Shared.Core.Interface; using salesbook.Shared.Core.Interface;
namespace salesbook.Maui.Core.Services; namespace salesbook.Maui.Core.Services;
@@ -7,7 +8,7 @@ public class SyncDbService(IIntegryApiService integryApiService, LocalDbService
{ {
public async Task GetAndSaveActivity(string? dateFilter) public async Task GetAndSaveActivity(string? dateFilter)
{ {
var allActivity = await integryApiService.RetrieveActivity(dateFilter); var allActivity = await integryApiService.RetrieveActivity(new CRMRetrieveActivityRequestDTO{DateFilter = dateFilter});
if (!allActivity.IsNullOrEmpty()) if (!allActivity.IsNullOrEmpty())
if (dateFilter is null) if (dateFilter is null)

View File

@@ -1,15 +1,19 @@
@page "/commessa/{CodJcom}" @page "/commessa/{CodJcom}"
@page "/commessa/{CodJcom}/{RagSoc}" @page "/commessa/{CodJcom}/{RagSoc}"
@attribute [Authorize] @attribute [Authorize]
@using AutoMapper
@using salesbook.Shared.Components.Layout @using salesbook.Shared.Components.Layout
@using salesbook.Shared.Components.Layout.Spinner @using salesbook.Shared.Components.Layout.Spinner
@using salesbook.Shared.Components.SingleElements @using salesbook.Shared.Components.SingleElements
@using salesbook.Shared.Core.Dto
@using salesbook.Shared.Core.Dto.JobProgress @using salesbook.Shared.Core.Dto.JobProgress
@using salesbook.Shared.Core.Dto.PageState @using salesbook.Shared.Core.Dto.PageState
@using salesbook.Shared.Core.Entity @using salesbook.Shared.Core.Entity
@using salesbook.Shared.Core.Interface @using salesbook.Shared.Core.Interface
@inject JobSteps JobSteps @inject JobSteps JobSteps
@inject IManageDataService ManageData @inject IManageDataService ManageData
@inject IIntegryApiService IntegryApiService
@inject IMapper Mapper
<HeaderLayout Title="@CodJcom" ShowProfile="false" Back="true" BackTo="Indietro"/> <HeaderLayout Title="@CodJcom" ShowProfile="false" Back="true" BackTo="Indietro"/>
@@ -62,8 +66,48 @@ else
</div> </div>
} }
</div> </div>
<div class="tab-content">Contenuto 2</div> <div class="tab-content">
<div class="tab-content">Contenuto 3</div> @if (ActivityIsLoading)
{
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Class="my-7" />
}
else
{
@if (ActivityList is { Count: > 0 })
{
<div class="contentFlex">
<Virtualize Items="ActivityList" Context="activity">
<ActivityCard Activity="activity" />
</Virtualize>
</div>
}
else
{
<NoDataAvailable Text="Nessuna attività trovata" />
}
}
</div>
<div class="tab-content">
@if (AttachedIsLoading)
{
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Class="my-7" />
}
else
{
@if (ListAttached != null)
{
<div class="contentFlex">
<Virtualize Items="ListAttached" Context="attached">
<AttachCard Attached="attached" />
</Virtualize>
</div>
}
else
{
<NoDataAvailable Text="Nessun allegato presente" />
}
}
</div>
</div> </div>
} }
</div> </div>
@@ -74,9 +118,13 @@ else
[Parameter] public string RagSoc { get; set; } = ""; [Parameter] public string RagSoc { get; set; } = "";
private List<CRMJobStepDTO>? Steps { get; set; } private List<CRMJobStepDTO>? Steps { get; set; }
private List<ActivityDTO> ActivityList { get; set; } = [];
private List<CRMAttachedResponseDTO>? ListAttached { get; set; }
private JtbComt? CommessaModel { get; set; } private JtbComt? CommessaModel { get; set; }
private bool IsLoading { get; set; } = true; private bool IsLoading { get; set; } = true;
private bool ActivityIsLoading { get; set; } = true;
private bool AttachedIsLoading { get; set; } = true;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@@ -88,8 +136,34 @@ else
CommessaModel = (await ManageData.GetTable<JtbComt>(x => x.CodJcom.Equals(CodJcom))).LastOrDefault(); CommessaModel = (await ManageData.GetTable<JtbComt>(x => x.CodJcom.Equals(CodJcom))).LastOrDefault();
Steps = JobSteps.Steps; Steps = JobSteps.Steps;
_ = LoadActivity();
_ = LoadAttached();
IsLoading = false; IsLoading = false;
} }
private async Task LoadActivity()
{
await Task.Run(async () =>
{
var activities = await IntegryApiService.RetrieveActivity(new CRMRetrieveActivityRequestDTO { CodJcom = CodJcom });
ActivityList = Mapper.Map<List<ActivityDTO>>(activities);
});
ActivityIsLoading = false;
StateHasChanged();
}
private async Task LoadAttached()
{
await Task.Run(async () =>
{
ListAttached = await IntegryApiService.RetrieveAttached(CodJcom);
});
AttachedIsLoading = false;
StateHasChanged();
}
} }

View File

@@ -198,6 +198,21 @@
padding: .5rem; padding: .5rem;
} }
.contentFlex {
display: flex;
flex-direction: column;
gap: 1.25rem;
}
.tab-content::-webkit-scrollbar { width: 6px; }
.tab-content::-webkit-scrollbar-thumb {
background: #bbb;
border-radius: 3px;
}
.contentFlex ::deep > div:first-child:not(.activity-card) { display: none; }
#tab1:checked ~ .tab-container .tab-content:nth-child(1), #tab1:checked ~ .tab-container .tab-content:nth-child(1),
#tab2:checked ~ .tab-container .tab-content:nth-child(2), #tab2:checked ~ .tab-container .tab-content:nth-child(2),
#tab3:checked ~ .tab-container .tab-content:nth-child(3) { display: block; } #tab3:checked ~ .tab-container .tab-content:nth-child(3) { display: block; }

View File

@@ -0,0 +1,44 @@
@using salesbook.Shared.Core.Dto
@using salesbook.Shared.Core.Interface
@inject IIntegryApiService IntegryApiService
@inject IAttachedService AttachedService
<div @onclick="OpenAttached" class="activity-card">
<div class="activity-left-section">
<div class="activity-body-section">
<div class="title-section">
<MudText Class="activity-title" Typo="Typo.body1" HtmlTag="h3">
@(Attached.Description.IsNullOrEmpty() ? Attached.FileName : Attached.Description)
</MudText>
<div class="activity-hours-section">
<span class="activity-hours">@($"{Attached.DateAttached:g}")</span>
</div>
</div>
</div>
</div>
<div class="activity-info-section">
@if (Attached.IsActivity)
{
<MudChip T="string" Color="Color.Primary" Variant="Variant.Outlined" Icon="@IconConstants.Chip.Tag" Size="Size.Small">
@Attached.RefAttached
</MudChip>
}
else
{
<MudChip T="string" Color="Color.Warning" Variant="Variant.Outlined" Icon="@IconConstants.Chip.FileTextLine" Size="Size.Small">
@Attached.RefAttached
</MudChip>
}
</div>
</div>
@code {
[Parameter] public CRMAttachedResponseDTO Attached { get; set; } = new();
private async Task OpenAttached()
{
var bytes = await IntegryApiService.DownloadFileFromRefUuid(Attached.RefUuid, Attached.FileName);
await AttachedService.OpenFile(bytes, Attached.FileName);
}
}

View File

@@ -0,0 +1,61 @@
.activity-card {
width: 100%;
display: flex;
flex-direction: column;
padding: .5rem .5rem;
border-radius: 12px;
line-height: normal;
box-shadow: var(--custom-box-shadow);
}
.activity-card.memo { border-left: 5px solid var(--mud-palette-info-darken); }
.activity-card.interna { border-left: 5px solid var(--mud-palette-success-darken); }
.activity-card.commessa { border-left: 5px solid var(--mud-palette-warning); }
.activity-left-section {
display: flex;
align-items: center;
margin-left: 4px;
}
.title-section {
display: flex;
flex-direction: column;
width: 100%;
}
.activity-hours {
color: var(--mud-palette-gray-darker);
font-weight: 600;
font-size: .8rem;
}
.activity-hours-section ::deep .mud-chip { margin: 5px 0 0 !important; }
.activity-body-section {
width: 100%;
display: flex;
flex-direction: column;
}
.title-section ::deep > .activity-title {
font-weight: 700 !important;
margin: 0 !important;
line-height: normal !important;
color: var(--mud-palette-text-primary);
}
.activity-body-section ::deep > .activity-subtitle {
color: var(--mud-palette-gray-darker);
margin: .2rem 0 !important;
line-height: normal !important;
}
.activity-info-section {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: .25rem;
}

View File

@@ -13,6 +13,7 @@
@inject IIntegryApiService IntegryApiService @inject IIntegryApiService IntegryApiService
@inject IMessenger Messenger @inject IMessenger Messenger
@inject IDialogService Dialog @inject IDialogService Dialog
@inject IAttachedService AttachedService
<MudDialog Class="customDialog-form"> <MudDialog Class="customDialog-form">
<DialogContent> <DialogContent>
@@ -142,7 +143,7 @@
{ {
foreach (var file in ActivityFileList) foreach (var file in ActivityFileList)
{ {
<MudChip T="string" Color="Color.Default"> <MudChip T="string" OnClick="() => OpenAttached(file.Id, file.FileName)" Color="Color.Default">
@file.FileName @file.FileName
</MudChip> </MudChip>
} }
@@ -315,7 +316,7 @@
await ManageData.InsertOrUpdate(newActivity); await ManageData.InsertOrUpdate(newActivity);
await SaveAttached(newActivity.ActivityId); await SaveAttached(newActivity.ActivityId!);
SuccessAnimation = true; SuccessAnimation = true;
StateHasChanged(); StateHasChanged();
@@ -353,7 +354,7 @@
{ {
if (attached.FileContent is not null && attached.Type != AttachedDTO.TypeAttached.Position) if (attached.FileContent is not null && attached.Type != AttachedDTO.TypeAttached.Position)
{ {
await IntegryApiService.UploadFile(activityId, attached.FileContent, attached.Name); await IntegryApiService.UploadFile(activityId, attached.FileBytes, attached.Name);
} }
} }
} }
@@ -541,12 +542,26 @@
StateHasChanged(); StateHasChanged();
} }
private async Task OpenAttached(string idAttached, string fileName)
{
try
{
var bytes = await IntegryApiService.DownloadFile(ActivityModel.ActivityId!, fileName);
await AttachedService.OpenFile(bytes, fileName);
}
catch (Exception ex)
{
Snackbar.Clear();
Snackbar.Add("Impossibile aprire il file", Severity.Error);
Console.WriteLine($"Errore durante l'apertura del file: {ex.Message}");
}
}
private async Task OpenAttached(AttachedDTO attached) private async Task OpenAttached(AttachedDTO attached)
{ {
if (attached is { FileContent: not null, MimeType: not null }) if (attached is { FileContent: not null, MimeType: not null })
{ {
var fileViewerUrl = $"data:{attached.MimeType};base64,{Convert.ToBase64String(attached.FileContent)}"; await AttachedService.OpenFile(attached.FileContent!, attached.Name);
await ModalHelpers.OpenViewAttach(Dialog, fileViewerUrl);
} }
else else
{ {

View File

@@ -1,14 +0,0 @@
<MudDialog Class="customDialog-form">
<DialogContent>
@if (!string.IsNullOrEmpty(FileViewerUrl))
{
<iframe src="@FileViewerUrl" style="width:100%; height:80vh; border:none;"></iframe>
}
</DialogContent>
</MudDialog>
@code {
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; }
[Parameter] public string? FileViewerUrl { get; set; }
}

View File

@@ -1,7 +0,0 @@
.content.attached {
display: flex;
flex-direction: column;
gap: 2rem;
padding: 1rem;
height: unset;
}

View File

@@ -8,7 +8,11 @@ public class AttachedDTO
public string? MimeType { get; set; } public string? MimeType { get; set; }
public long? DimensionBytes { get; set; } public long? DimensionBytes { get; set; }
public string? Path { get; set; } public string? Path { get; set; }
public byte[]? FileContent { get; set; }
public byte[]? FileBytes { get; set; }
public Stream? FileContent =>
FileBytes is null ? null : new MemoryStream(FileBytes);
public double? Lat { get; set; } public double? Lat { get; set; }
public double? Lng { get; set; } public double? Lng { get; set; }

View File

@@ -0,0 +1,27 @@
using System.Text.Json.Serialization;
namespace salesbook.Shared.Core.Dto;
public class CRMAttachedResponseDTO
{
[JsonPropertyName("fileName")]
public string FileName { get; set; }
[JsonPropertyName("description")]
public string? Description { get; set; }
[JsonPropertyName("dateAttached")]
public DateTime DateAttached { get; set; }
[JsonPropertyName("fileSize")]
public decimal FileSize { get; set; }
[JsonPropertyName("refUuid")]
public string RefUuid { get; set; }
[JsonPropertyName("refAttached")]
public string RefAttached { get; set; }
[JsonPropertyName("activity")]
public bool IsActivity { get; set; }
}

View File

@@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
using Java.Time;
namespace salesbook.Shared.Core.Dto;
public class CRMRetrieveActivityRequestDTO
{
[JsonPropertyName("dateFilter")]
public string? DateFilter { get; set; }
[JsonPropertyName("codJcom")]
public string? CodJcom { get; set; }
}

View File

@@ -7,5 +7,7 @@ class IconConstants
public const string Stato = "ri-list-check-3 fa-fw fa-chip"; public const string Stato = "ri-list-check-3 fa-fw fa-chip";
public const string User = "ri-user-fill fa-fw fa-chip"; public const string User = "ri-user-fill fa-fw fa-chip";
public const string Time = "ri-time-line fa-fw fa-chip"; public const string Time = "ri-time-line fa-fw fa-chip";
public const string FileTextLine = "ri-file-text-line fa-fw fa-chip";
public const string Tag = "ri-price-tag-3-fill fa-fw fa-chip";
} }
} }

View File

@@ -85,23 +85,4 @@ public class ModalHelpers
return await modal.Result; return await modal.Result;
} }
public static async Task<DialogResult?> OpenViewAttach(IDialogService dialog, string? fileViewUrl)
{
var modal = await dialog.ShowAsync<ViewAttached>(
"View attached",
new DialogParameters<ViewAttached>
{
{ x => x.FileViewerUrl, fileViewUrl }
},
new DialogOptions
{
FullScreen = true,
CloseButton = true,
NoHeader = true
}
);
return await modal.Result;
}
} }

View File

@@ -7,4 +7,5 @@ public interface IAttachedService
Task<AttachedDTO?> SelectImage(); Task<AttachedDTO?> SelectImage();
Task<AttachedDTO?> SelectFile(); Task<AttachedDTO?> SelectFile();
Task<AttachedDTO?> SelectPosition(); Task<AttachedDTO?> SelectPosition();
Task OpenFile(Stream file, string fileName);
} }

View File

@@ -6,11 +6,12 @@ namespace salesbook.Shared.Core.Interface;
public interface IIntegryApiService public interface IIntegryApiService
{ {
Task<List<StbActivity>?> RetrieveActivity(string? dateFilter = null); Task<List<StbActivity>?> RetrieveActivity(CRMRetrieveActivityRequestDTO activityRequest);
Task<List<JtbComt>?> RetrieveAllCommesse(string? dateFilter = null); Task<List<JtbComt>?> RetrieveAllCommesse(string? dateFilter = null);
Task<TaskSyncResponseDTO> RetrieveAnagClie(string? dateFilter = null); Task<TaskSyncResponseDTO> RetrieveAnagClie(string? dateFilter = null);
Task<TaskSyncResponseDTO> RetrieveProspect(string? dateFilter = null); Task<TaskSyncResponseDTO> RetrieveProspect(string? dateFilter = null);
Task<SettingsResponseDTO> RetrieveSettings(); Task<SettingsResponseDTO> RetrieveSettings();
Task<List<CRMAttachedResponseDTO>?> RetrieveAttached(string codJcom);
Task DeleteActivity(string activityId); Task DeleteActivity(string activityId);
@@ -22,6 +23,7 @@ public interface IIntegryApiService
Task UploadFile(string id, byte[] file, string fileName); Task UploadFile(string id, byte[] file, string fileName);
Task<List<ActivityFileDto>> GetActivityFile(string activityId); Task<List<ActivityFileDto>> GetActivityFile(string activityId);
Task<Stream> DownloadFile(string activityId, string fileName); Task<Stream> DownloadFile(string activityId, string fileName);
Task<Stream> DownloadFileFromRefUuid(string refUuid, string fileName);
Task<CRMJobProgressResponseDTO> RetrieveJobProgress(string codJcom); Task<CRMJobProgressResponseDTO> RetrieveJobProgress(string codJcom);

View File

@@ -11,12 +11,8 @@ namespace salesbook.Shared.Core.Services;
public class IntegryApiService(IIntegryApiRestClient integryApiRestClient, IUserSession userSession) public class IntegryApiService(IIntegryApiRestClient integryApiRestClient, IUserSession userSession)
: IIntegryApiService : IIntegryApiService
{ {
public Task<List<StbActivity>?> RetrieveActivity(string? dateFilter) public Task<List<StbActivity>?> RetrieveActivity(CRMRetrieveActivityRequestDTO activityRequest) =>
{ integryApiRestClient.AuthorizedPost<List<StbActivity>?>("crm/retrieveActivity", activityRequest);
var queryParams = new Dictionary<string, object> { { "dateFilter", dateFilter ?? "2020-01-01" } };
return integryApiRestClient.AuthorizedGet<List<StbActivity>?>("crm/retrieveActivity", queryParams);
}
public Task<List<JtbComt>?> RetrieveAllCommesse(string? dateFilter) public Task<List<JtbComt>?> RetrieveAllCommesse(string? dateFilter)
{ {
@@ -57,6 +53,17 @@ public class IntegryApiService(IIntegryApiRestClient integryApiRestClient, IUser
public Task<SettingsResponseDTO> RetrieveSettings() => public Task<SettingsResponseDTO> RetrieveSettings() =>
integryApiRestClient.AuthorizedGet<SettingsResponseDTO>("crm/retrieveSettings")!; integryApiRestClient.AuthorizedGet<SettingsResponseDTO>("crm/retrieveSettings")!;
public Task<List<CRMAttachedResponseDTO>?> RetrieveAttached(string codJcom)
{
var queryParams = new Dictionary<string, object>
{
{ "codJcom", codJcom }
};
return integryApiRestClient.AuthorizedGet<List<CRMAttachedResponseDTO>?>("crm/retrieveAttachedForCodJcom",
queryParams);
}
public Task DeleteActivity(string activityId) public Task DeleteActivity(string activityId)
{ {
var queryParams = new Dictionary<string, object> var queryParams = new Dictionary<string, object>
@@ -138,6 +145,17 @@ public class IntegryApiService(IIntegryApiRestClient integryApiRestClient, IUser
public Task<Stream> DownloadFile(string activityId, string fileName) => public Task<Stream> DownloadFile(string activityId, string fileName) =>
integryApiRestClient.Download($"downloadStbFileAttachment/{activityId}/{fileName}")!; integryApiRestClient.Download($"downloadStbFileAttachment/{activityId}/{fileName}")!;
public Task<Stream> DownloadFileFromRefUuid(string refUuid, string fileName)
{
var queryParams = new Dictionary<string, object>
{
{ "refUuid", refUuid },
{ "fileName", fileName }
};
return integryApiRestClient.Download("downloadFileFromRefUuid", queryParams);
}
public Task<PositionDTO> SavePosition(PositionDTO position) => public Task<PositionDTO> SavePosition(PositionDTO position) =>
integryApiRestClient.Post<PositionDTO>("savePosition", position)!; integryApiRestClient.Post<PositionDTO>("savePosition", position)!;