diff --git a/salesbook.Maui/Core/Services/AttachedService.cs b/salesbook.Maui/Core/Services/AttachedService.cs index fa432d3..1aa4fe4 100644 --- a/salesbook.Maui/Core/Services/AttachedService.cs +++ b/salesbook.Maui/Core/Services/AttachedService.cs @@ -57,9 +57,53 @@ public class AttachedService : IAttachedService Name = file.FileName, Path = file.FullPath, MimeType = file.ContentType, - DimensionBytes= ms.Length, - FileContent = ms.ToArray(), + DimensionBytes = ms.Length, + FileBytes = ms.ToArray(), Type = type }; } + + private static async Task 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) + }); + } } \ No newline at end of file diff --git a/salesbook.Maui/Core/Services/SyncDbService.cs b/salesbook.Maui/Core/Services/SyncDbService.cs index 8178aa6..a3d21ed 100644 --- a/salesbook.Maui/Core/Services/SyncDbService.cs +++ b/salesbook.Maui/Core/Services/SyncDbService.cs @@ -1,4 +1,5 @@ -using salesbook.Shared.Core.Helpers; +using salesbook.Shared.Core.Dto; +using salesbook.Shared.Core.Helpers; using salesbook.Shared.Core.Interface; namespace salesbook.Maui.Core.Services; @@ -7,7 +8,7 @@ public class SyncDbService(IIntegryApiService integryApiService, LocalDbService { 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 (dateFilter is null) diff --git a/salesbook.Shared/Components/Pages/Commessa.razor b/salesbook.Shared/Components/Pages/Commessa.razor index 15a88fe..a51fb84 100644 --- a/salesbook.Shared/Components/Pages/Commessa.razor +++ b/salesbook.Shared/Components/Pages/Commessa.razor @@ -1,15 +1,19 @@ @page "/commessa/{CodJcom}" @page "/commessa/{CodJcom}/{RagSoc}" @attribute [Authorize] +@using AutoMapper @using salesbook.Shared.Components.Layout @using salesbook.Shared.Components.Layout.Spinner @using salesbook.Shared.Components.SingleElements +@using salesbook.Shared.Core.Dto @using salesbook.Shared.Core.Dto.JobProgress @using salesbook.Shared.Core.Dto.PageState @using salesbook.Shared.Core.Entity @using salesbook.Shared.Core.Interface @inject JobSteps JobSteps @inject IManageDataService ManageData +@inject IIntegryApiService IntegryApiService +@inject IMapper Mapper @@ -62,8 +66,48 @@ else } -
Contenuto 2
-
Contenuto 3
+
+ @if (ActivityIsLoading) + { + + } + else + { + @if (ActivityList is { Count: > 0 }) + { +
+ + + +
+ } + else + { + + } + } +
+
+ @if (AttachedIsLoading) + { + + } + else + { + @if (ListAttached != null) + { +
+ + + +
+ } + else + { + + } + } +
} @@ -74,9 +118,13 @@ else [Parameter] public string RagSoc { get; set; } = ""; private List? Steps { get; set; } + private List ActivityList { get; set; } = []; + private List? ListAttached { get; set; } private JtbComt? CommessaModel { get; set; } private bool IsLoading { get; set; } = true; + private bool ActivityIsLoading { get; set; } = true; + private bool AttachedIsLoading { get; set; } = true; protected override async Task OnInitializedAsync() { @@ -88,8 +136,34 @@ else CommessaModel = (await ManageData.GetTable(x => x.CodJcom.Equals(CodJcom))).LastOrDefault(); Steps = JobSteps.Steps; + _ = LoadActivity(); + _ = LoadAttached(); + IsLoading = false; } + private async Task LoadActivity() + { + await Task.Run(async () => + { + var activities = await IntegryApiService.RetrieveActivity(new CRMRetrieveActivityRequestDTO { CodJcom = CodJcom }); + ActivityList = Mapper.Map>(activities); + }); + + ActivityIsLoading = false; + StateHasChanged(); + } + + private async Task LoadAttached() + { + await Task.Run(async () => + { + ListAttached = await IntegryApiService.RetrieveAttached(CodJcom); + }); + + AttachedIsLoading = false; + StateHasChanged(); + } + } diff --git a/salesbook.Shared/Components/Pages/Commessa.razor.css b/salesbook.Shared/Components/Pages/Commessa.razor.css index 6ae66ef..bc18e3a 100644 --- a/salesbook.Shared/Components/Pages/Commessa.razor.css +++ b/salesbook.Shared/Components/Pages/Commessa.razor.css @@ -198,6 +198,21 @@ 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), #tab2:checked ~ .tab-container .tab-content:nth-child(2), #tab3:checked ~ .tab-container .tab-content:nth-child(3) { display: block; } diff --git a/salesbook.Shared/Components/SingleElements/Card/AttachCard.razor b/salesbook.Shared/Components/SingleElements/Card/AttachCard.razor new file mode 100644 index 0000000..26d27a8 --- /dev/null +++ b/salesbook.Shared/Components/SingleElements/Card/AttachCard.razor @@ -0,0 +1,44 @@ +@using salesbook.Shared.Core.Dto +@using salesbook.Shared.Core.Interface +@inject IIntegryApiService IntegryApiService +@inject IAttachedService AttachedService + +
+
+
+
+ + @(Attached.Description.IsNullOrEmpty() ? Attached.FileName : Attached.Description) + +
+ @($"{Attached.DateAttached:g}") +
+
+
+
+ +
+ @if (Attached.IsActivity) + { + + @Attached.RefAttached + + } + else + { + + @Attached.RefAttached + + } +
+
+ +@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); + } +} \ No newline at end of file diff --git a/salesbook.Shared/Components/SingleElements/Card/AttachCard.razor.css b/salesbook.Shared/Components/SingleElements/Card/AttachCard.razor.css new file mode 100644 index 0000000..0e0c335 --- /dev/null +++ b/salesbook.Shared/Components/SingleElements/Card/AttachCard.razor.css @@ -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; +} \ No newline at end of file diff --git a/salesbook.Shared/Components/SingleElements/Modal/ActivityForm.razor b/salesbook.Shared/Components/SingleElements/Modal/ActivityForm.razor index b91bc5e..40dbd59 100644 --- a/salesbook.Shared/Components/SingleElements/Modal/ActivityForm.razor +++ b/salesbook.Shared/Components/SingleElements/Modal/ActivityForm.razor @@ -13,6 +13,7 @@ @inject IIntegryApiService IntegryApiService @inject IMessenger Messenger @inject IDialogService Dialog +@inject IAttachedService AttachedService @@ -142,7 +143,7 @@ { foreach (var file in ActivityFileList) { - + @file.FileName } @@ -315,7 +316,7 @@ await ManageData.InsertOrUpdate(newActivity); - await SaveAttached(newActivity.ActivityId); + await SaveAttached(newActivity.ActivityId!); SuccessAnimation = true; StateHasChanged(); @@ -353,7 +354,7 @@ { 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(); } + 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) { if (attached is { FileContent: not null, MimeType: not null }) { - var fileViewerUrl = $"data:{attached.MimeType};base64,{Convert.ToBase64String(attached.FileContent)}"; - await ModalHelpers.OpenViewAttach(Dialog, fileViewerUrl); + await AttachedService.OpenFile(attached.FileContent!, attached.Name); } else { diff --git a/salesbook.Shared/Components/SingleElements/Modal/ViewAttached.razor b/salesbook.Shared/Components/SingleElements/Modal/ViewAttached.razor deleted file mode 100644 index 0dc97c5..0000000 --- a/salesbook.Shared/Components/SingleElements/Modal/ViewAttached.razor +++ /dev/null @@ -1,14 +0,0 @@ - - - @if (!string.IsNullOrEmpty(FileViewerUrl)) - { - - } - - - -@code { - [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } - - [Parameter] public string? FileViewerUrl { get; set; } -} \ No newline at end of file diff --git a/salesbook.Shared/Components/SingleElements/Modal/ViewAttached.razor.css b/salesbook.Shared/Components/SingleElements/Modal/ViewAttached.razor.css deleted file mode 100644 index 6eeaddd..0000000 --- a/salesbook.Shared/Components/SingleElements/Modal/ViewAttached.razor.css +++ /dev/null @@ -1,7 +0,0 @@ -.content.attached { - display: flex; - flex-direction: column; - gap: 2rem; - padding: 1rem; - height: unset; -} diff --git a/salesbook.Shared/Core/Dto/AttachedDTO.cs b/salesbook.Shared/Core/Dto/AttachedDTO.cs index 74e91b3..d125cbf 100644 --- a/salesbook.Shared/Core/Dto/AttachedDTO.cs +++ b/salesbook.Shared/Core/Dto/AttachedDTO.cs @@ -8,8 +8,12 @@ public class AttachedDTO public string? MimeType { get; set; } public long? DimensionBytes { 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? Lng { get; set; } diff --git a/salesbook.Shared/Core/Dto/CRMAttachedResponseDTO.cs b/salesbook.Shared/Core/Dto/CRMAttachedResponseDTO.cs new file mode 100644 index 0000000..9914954 --- /dev/null +++ b/salesbook.Shared/Core/Dto/CRMAttachedResponseDTO.cs @@ -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; } +} \ No newline at end of file diff --git a/salesbook.Shared/Core/Dto/CRMRetrieveActivityRequestDTO.cs b/salesbook.Shared/Core/Dto/CRMRetrieveActivityRequestDTO.cs new file mode 100644 index 0000000..4953c16 --- /dev/null +++ b/salesbook.Shared/Core/Dto/CRMRetrieveActivityRequestDTO.cs @@ -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; } +} \ No newline at end of file diff --git a/salesbook.Shared/Core/Helpers/IconConstants.cs b/salesbook.Shared/Core/Helpers/IconConstants.cs index 23df9cc..c7b6e97 100644 --- a/salesbook.Shared/Core/Helpers/IconConstants.cs +++ b/salesbook.Shared/Core/Helpers/IconConstants.cs @@ -7,5 +7,7 @@ class IconConstants 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 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"; } } \ No newline at end of file diff --git a/salesbook.Shared/Core/Helpers/ModalHelpers.cs b/salesbook.Shared/Core/Helpers/ModalHelpers.cs index 574a8ba..fa199f6 100644 --- a/salesbook.Shared/Core/Helpers/ModalHelpers.cs +++ b/salesbook.Shared/Core/Helpers/ModalHelpers.cs @@ -85,23 +85,4 @@ public class ModalHelpers return await modal.Result; } - - public static async Task OpenViewAttach(IDialogService dialog, string? fileViewUrl) - { - var modal = await dialog.ShowAsync( - "View attached", - new DialogParameters - { - { x => x.FileViewerUrl, fileViewUrl } - }, - new DialogOptions - { - FullScreen = true, - CloseButton = true, - NoHeader = true - } - ); - - return await modal.Result; - } } \ No newline at end of file diff --git a/salesbook.Shared/Core/Interface/IAttachedService.cs b/salesbook.Shared/Core/Interface/IAttachedService.cs index 6897b19..a038c49 100644 --- a/salesbook.Shared/Core/Interface/IAttachedService.cs +++ b/salesbook.Shared/Core/Interface/IAttachedService.cs @@ -7,4 +7,5 @@ public interface IAttachedService Task SelectImage(); Task SelectFile(); Task SelectPosition(); + Task OpenFile(Stream file, string fileName); } \ No newline at end of file diff --git a/salesbook.Shared/Core/Interface/IIntegryApiService.cs b/salesbook.Shared/Core/Interface/IIntegryApiService.cs index 90eb85c..a04484d 100644 --- a/salesbook.Shared/Core/Interface/IIntegryApiService.cs +++ b/salesbook.Shared/Core/Interface/IIntegryApiService.cs @@ -6,11 +6,12 @@ namespace salesbook.Shared.Core.Interface; public interface IIntegryApiService { - Task?> RetrieveActivity(string? dateFilter = null); + Task?> RetrieveActivity(CRMRetrieveActivityRequestDTO activityRequest); Task?> RetrieveAllCommesse(string? dateFilter = null); Task RetrieveAnagClie(string? dateFilter = null); Task RetrieveProspect(string? dateFilter = null); Task RetrieveSettings(); + Task?> RetrieveAttached(string codJcom); Task DeleteActivity(string activityId); @@ -22,6 +23,7 @@ public interface IIntegryApiService Task UploadFile(string id, byte[] file, string fileName); Task> GetActivityFile(string activityId); Task DownloadFile(string activityId, string fileName); + Task DownloadFileFromRefUuid(string refUuid, string fileName); Task RetrieveJobProgress(string codJcom); diff --git a/salesbook.Shared/Core/Services/IntegryApiService.cs b/salesbook.Shared/Core/Services/IntegryApiService.cs index 112f87e..a29e251 100644 --- a/salesbook.Shared/Core/Services/IntegryApiService.cs +++ b/salesbook.Shared/Core/Services/IntegryApiService.cs @@ -11,12 +11,8 @@ namespace salesbook.Shared.Core.Services; public class IntegryApiService(IIntegryApiRestClient integryApiRestClient, IUserSession userSession) : IIntegryApiService { - public Task?> RetrieveActivity(string? dateFilter) - { - var queryParams = new Dictionary { { "dateFilter", dateFilter ?? "2020-01-01" } }; - - return integryApiRestClient.AuthorizedGet?>("crm/retrieveActivity", queryParams); - } + public Task?> RetrieveActivity(CRMRetrieveActivityRequestDTO activityRequest) => + integryApiRestClient.AuthorizedPost?>("crm/retrieveActivity", activityRequest); public Task?> RetrieveAllCommesse(string? dateFilter) { @@ -57,6 +53,17 @@ public class IntegryApiService(IIntegryApiRestClient integryApiRestClient, IUser public Task RetrieveSettings() => integryApiRestClient.AuthorizedGet("crm/retrieveSettings")!; + public Task?> RetrieveAttached(string codJcom) + { + var queryParams = new Dictionary + { + { "codJcom", codJcom } + }; + + return integryApiRestClient.AuthorizedGet?>("crm/retrieveAttachedForCodJcom", + queryParams); + } + public Task DeleteActivity(string activityId) { var queryParams = new Dictionary @@ -138,6 +145,17 @@ public class IntegryApiService(IIntegryApiRestClient integryApiRestClient, IUser public Task DownloadFile(string activityId, string fileName) => integryApiRestClient.Download($"downloadStbFileAttachment/{activityId}/{fileName}")!; + public Task DownloadFileFromRefUuid(string refUuid, string fileName) + { + var queryParams = new Dictionary + { + { "refUuid", refUuid }, + { "fileName", fileName } + }; + + return integryApiRestClient.Download("downloadFileFromRefUuid", queryParams); + } + public Task SavePosition(PositionDTO position) => integryApiRestClient.Post("savePosition", position)!;