Compare commits

...

10 Commits

34 changed files with 1481 additions and 606 deletions

View File

@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore; using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using SteUp.Shared.Core.Entities; using SteUp.Shared.Core.Entities;
using SteUp.Shared.Core.Enum; using SteUp.Shared.Core.Enum;
using SteUp.Shared.Core.Helpers; using SteUp.Shared.Core.Helpers;
@@ -17,8 +18,15 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
x.Data == data && x.Data == data &&
x.Rilevatore == rilevatore); x.Rilevatore == rilevatore);
public Task<List<Ispezione>> GetAllIspezioniWithSchedeAsync() => public Task<List<Ispezione>> GetAllIspezioni(string rilevatore) =>
db.Ispezioni db.Ispezioni
.Where(x => x.Rilevatore.Equals(rilevatore, StringComparison.InvariantCultureIgnoreCase))
.AsNoTracking()
.ToListAsync();
public Task<List<Ispezione>> GetAllIspezioniWithSchedeAsync(string rilevatore) =>
db.Ispezioni
.Where(x => x.Rilevatore.Equals(rilevatore, StringComparison.InvariantCultureIgnoreCase))
.Include(x => x.Schede) .Include(x => x.Schede)
.ThenInclude(s => s.Articoli) .ThenInclude(s => s.Articoli)
.AsNoTracking() .AsNoTracking()
@@ -68,7 +76,8 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
return true; return true;
} }
public async Task<bool> UpdateStatoIspezioneAsync(string codMdep, DateTime data, string rilevatore, StatusEnum stato) public async Task<bool> UpdateStatoIspezioneAsync(string codMdep, DateTime data, string rilevatore,
StatusEnum stato)
{ {
var ispezione = await db.Ispezioni var ispezione = await db.Ispezioni
.FirstOrDefaultAsync(x => .FirstOrDefaultAsync(x =>
@@ -175,6 +184,17 @@ public class IspezioniService(AppDbContext db) : IIspezioniService
return true; return true;
} }
public async Task<bool> UpdateFileListSchedaAsync(int schedaId, List<string>? imageNames)
{
var scheda = await db.Schede.FirstOrDefaultAsync(x => x.Id == schedaId);
if (scheda is null) return false;
scheda.ImageNames = imageNames;
db.Schede.Update(scheda);
await db.SaveChangesAsync();
return true;
}
public async Task<bool> DeleteSchedaAsync(int schedaId) public async Task<bool> DeleteSchedaAsync(int schedaId)
{ {
var scheda = await db.Schede.FirstOrDefaultAsync(x => x.Id == schedaId); var scheda = await db.Schede.FirstOrDefaultAsync(x => x.Id == schedaId);

View File

@@ -1,10 +1,14 @@
using Microsoft.Extensions.Logging;
using SteUp.Maui.Core.UtilityException;
namespace SteUp.Maui namespace SteUp.Maui
{ {
public partial class App public partial class App
{ {
public App() public App(ILogger<App> logger)
{ {
InitializeComponent(); InitializeComponent();
GlobalExceptionHandler.Register(logger);
} }
protected override Window CreateWindow(IActivationState? activationState) protected override Window CreateWindow(IActivationState? activationState)

View File

@@ -1,8 +1,10 @@
using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using SteUp.Data.LocalDb; using SteUp.Data.LocalDb;
using SteUp.Data.LocalDb.EntityServices; using SteUp.Data.LocalDb.EntityServices;
using SteUp.Maui.Core.Logger;
using SteUp.Maui.Core.Services; using SteUp.Maui.Core.Services;
using SteUp.Maui.Core.System; using SteUp.Maui.Core.System;
using SteUp.Maui.Core.System.Network; using SteUp.Maui.Core.System.Network;
@@ -45,6 +47,7 @@ public static class CoreModule
{ {
builder.Services.AddSingleton<INetworkService, NetworkService>(); builder.Services.AddSingleton<INetworkService, NetworkService>();
builder.Services.AddSingleton<IAttachedService, AttachedService>(); builder.Services.AddSingleton<IAttachedService, AttachedService>();
builder.Services.AddSingleton<IFileManager, FileManager>();
builder.Services.AddSingleton<IBarcodeReaderService, HoneywellScannerService>(); builder.Services.AddSingleton<IBarcodeReaderService, HoneywellScannerService>();
} }
@@ -75,5 +78,17 @@ public static class CoreModule
builder.Services.AddSingleton<IDbInitializer, DbInitializer>(); builder.Services.AddSingleton<IDbInitializer, DbInitializer>();
builder.Services.AddSingleton<IIspezioniService, IspezioniService>(); builder.Services.AddSingleton<IIspezioniService, IspezioniService>();
} }
public void RegisterLoggerServices()
{
var logPath = Path.Combine(FileSystem.AppDataDirectory, "logs");
const string logFilePrefix = "SteUp-log";
builder.Services.AddLogging(loggingBuilder =>
{
loggingBuilder.AddProvider(new FileLoggerProvider(logPath, logFilePrefix));
loggingBuilder.SetMinimumLevel(LogLevel.Information);
});
}
} }
} }

View File

@@ -0,0 +1,185 @@
using System.Diagnostics;
using Microsoft.Extensions.Logging;
using System.Globalization;
using System.Text;
namespace SteUp.Maui.Core.Logger;
public class FileLogger : ILogger
{
private readonly string _path;
private readonly string _fileNamePrefix;
private readonly string _categoryName;
private readonly Lock _lock = new();
private readonly int _retentionDays;
private string? _currentFileName;
private DateTime _currentFileDate;
private DateTime _lastCleanupDate;
public FileLogger(string path, string fileNamePrefix, string categoryName, int retentionDays = 60)
{
_path = path;
_fileNamePrefix = fileNamePrefix;
_retentionDays = retentionDays;
_lastCleanupDate = DateTime.MinValue;
_categoryName = categoryName;
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
UpdateCurrentFileName();
TryCleanOldLogs();
}
/// <summary>
/// Elimina i log più vecchi di <see cref="_retentionDays"/> giorni.
/// Viene eseguita al massimo una volta al giorno.
/// </summary>
private void ClearOldLogs()
{
try
{
var cutoff = DateTime.Now.Date.AddDays(-_retentionDays);
var logFiles = Directory.GetFiles(_path, $"{_fileNamePrefix}-*.log");
foreach (var file in logFiles)
{
try
{
var fileName = Path.GetFileNameWithoutExtension(file);
var datePart = fileName[(_fileNamePrefix.Length + 1)..];
if (!DateTime.TryParseExact(datePart, "yyyy-MM-dd",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out var fileDate) || fileDate >= cutoff) continue;
File.Delete(file);
Debug.WriteLine($"[FileLogger] Log eliminato: {file}");
}
catch (Exception ex)
{
Debug.WriteLine($"[FileLogger] Errore durante l'eliminazione del file {file}: {ex.Message}");
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"[FileLogger] Errore durante la pulizia dei log: {ex.Message}");
}
}
/// <summary>
/// Esegue la pulizia dei log solo se non è già stata eseguita oggi.
/// </summary>
private void TryCleanOldLogs()
{
var today = DateTime.Now.Date;
if (_lastCleanupDate == today) return;
_lastCleanupDate = today;
ClearOldLogs();
}
private void UpdateCurrentFileName()
{
var today = DateTime.Now.Date;
if (_currentFileName != null && _currentFileDate == today) return;
_currentFileDate = today;
_currentFileName = $"{_fileNamePrefix}-{today:yyyy-MM-dd}.log";
}
public IDisposable? BeginScope<TState>(TState state) => null;
public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None;
public void Log<TState>(
LogLevel logLevel,
EventId eventId,
TState state,
Exception? exception,
Func<TState, Exception?, string> formatter)
{
if (!IsEnabled(logLevel))
return;
try
{
lock (_lock)
{
UpdateCurrentFileName();
TryCleanOldLogs();
if (_currentFileName == null) return;
var fullPath = Path.Combine(_path, _currentFileName);
var logEntry = BuildLogEntry(logLevel, eventId, state, exception, formatter);
File.AppendAllText(fullPath, logEntry + Environment.NewLine + Environment.NewLine);
Debug.WriteLine($"[FileLogger] {logEntry}");
}
}
catch (Exception ex)
{
Debug.WriteLine($"[FileLogger] Errore durante la scrittura del log: {ex}");
}
}
private string BuildLogEntry<TState>(
LogLevel logLevel,
EventId eventId,
TState state,
Exception? exception,
Func<TState, Exception?, string> formatter)
{
var sb = new StringBuilder();
sb.Append($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}]");
sb.Append($" [{GetLogLevelShort(logLevel)}]");
sb.Append($" [{_categoryName}]");
if (eventId.Id != 0 || !string.IsNullOrEmpty(eventId.Name))
sb.Append($" [{eventId}]");
sb.Append($" {formatter(state, exception)}");
if (exception != null)
AppendException(sb, exception);
return sb.ToString();
}
private static void AppendException(StringBuilder sb, Exception exception, int depth = 0)
{
while (true)
{
var indent = depth == 0 ? "" : " Inner ";
sb.AppendLine();
sb.Append($"{indent}Exception: {exception.GetType().FullName}: {exception.Message}");
if (!string.IsNullOrWhiteSpace(exception.StackTrace))
{
sb.AppendLine();
sb.Append($"{indent}StackTrace: {exception.StackTrace.Trim()}");
}
if (exception.InnerException != null)
{
exception = exception.InnerException;
depth += 1;
continue;
}
break;
}
}
private static string GetLogLevelShort(LogLevel level) => level switch
{
LogLevel.Trace => "TRC",
LogLevel.Debug => "DBG",
LogLevel.Information => "INF",
LogLevel.Warning => "WRN",
LogLevel.Error => "ERR",
LogLevel.Critical => "CRT",
_ => "???"
};
}

View File

@@ -0,0 +1,14 @@
using Microsoft.Extensions.Logging;
namespace SteUp.Maui.Core.Logger;
public class FileLoggerProvider(string path, string logFilePrefix, int retentionDays = 60)
: ILoggerProvider
{
public ILogger CreateLogger(string categoryName)
{
return new FileLogger(path, logFilePrefix, categoryName, retentionDays);
}
public void Dispose() { }
}

View File

@@ -1,15 +1,13 @@
using SteUp.Shared.Core.Dto; using Microsoft.Extensions.Logging;
using SteUp.Shared.Core.Entities; using SteUp.Maui.Core.Utility;
using SteUp.Shared.Core.Dto;
using SteUp.Shared.Core.Helpers; using SteUp.Shared.Core.Helpers;
using SteUp.Shared.Core.Interface.System; using SteUp.Shared.Core.Interface.System;
namespace SteUp.Maui.Core.Services; namespace SteUp.Maui.Core.Services;
public class AttachedService : IAttachedService public class AttachedService(ILogger<FileManager> logger) : IAttachedService
{ {
private static string AttachedRoot =>
Path.Combine(FileSystem.CacheDirectory, "attached");
public async Task<AttachedDto?> SelectImageFromCamera() public async Task<AttachedDto?> SelectImageFromCamera()
{ {
var cameraPerm = await Permissions.RequestAsync<Permissions.Camera>(); var cameraPerm = await Permissions.RequestAsync<Permissions.Camera>();
@@ -27,12 +25,13 @@ public class AttachedService : IAttachedService
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.LogError(ex, ex.Message);
Console.WriteLine($"Errore cattura foto: {ex.Message}"); Console.WriteLine($"Errore cattura foto: {ex.Message}");
SentrySdk.CaptureException(ex); SentrySdk.CaptureException(ex);
return null; return null;
} }
return result is null ? null : await ConvertToDto(result, AttachedDto.TypeAttached.Image); return result is null ? null : await UtilityFile.ConvertToDto(result, AttachedDto.TypeAttached.Image);
} }
public async Task<List<AttachedDto>?> SelectImageFromGallery() public async Task<List<AttachedDto>?> SelectImageFromGallery()
@@ -48,6 +47,7 @@ public class AttachedService : IAttachedService
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.LogError(ex, ex.Message);
Console.WriteLine($"Errore selezione galleria: {ex.Message}"); Console.WriteLine($"Errore selezione galleria: {ex.Message}");
SentrySdk.CaptureException(ex); SentrySdk.CaptureException(ex);
return null; return null;
@@ -58,308 +58,9 @@ public class AttachedService : IAttachedService
List<AttachedDto> returnList = []; List<AttachedDto> returnList = [];
foreach (var fileResult in resultList) foreach (var fileResult in resultList)
{ {
returnList.Add(await ConvertToDto(fileResult, AttachedDto.TypeAttached.Image)); returnList.Add(await UtilityFile.ConvertToDto(fileResult, AttachedDto.TypeAttached.Image));
} }
return returnList; return returnList;
} }
private static async Task<AttachedDto> ConvertToDto(FileResult file, AttachedDto.TypeAttached type)
{
var stream = await file.OpenReadAsync();
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
return new AttachedDto
{
Name = file.FileName,
Path = file.FullPath,
MimeType = file.ContentType,
DimensionBytes = ms.Length,
FileBytes = ms.ToArray(),
Type = type
};
}
private async Task<AttachedDto> ConvertToDto(FileInfo file, AttachedDto.TypeAttached type, bool isFromToUpload)
{
var (origUrl, thumbUrl) = await SaveAndCreateThumbAsync(
await File.ReadAllBytesAsync(file.FullName),
file.Name
);
return new AttachedDto
{
Name = file.Name,
Path = file.FullName,
TempPath = origUrl,
ThumbPath = thumbUrl,
Type = type,
SavedOnAppData = true,
ToUpload = isFromToUpload
};
}
private const string ToUploadFolderName = "toUpload";
private string GetInspectionBaseDir(Ispezione ispezione)
{
var baseDir = FileSystem.AppDataDirectory;
return Path.Combine(baseDir, $"attached_{GetInspectionKey(ispezione)}");
}
private string GetInspectionToUploadDir(Ispezione ispezione)
=> Path.Combine(GetInspectionBaseDir(ispezione), ToUploadFolderName);
private string GetInspectionFinalDir(Ispezione ispezione)
=> GetInspectionBaseDir(ispezione);
/// <summary>
/// Ritorna i file dell'ispezione filtrati per nome.
/// Per default include sia "final" sia "toUpload" (utile per UI).
/// </summary>
public async Task<List<AttachedDto>?> GetInspectionFiles(
Ispezione ispezione,
List<string> fileNameFilter,
bool includeToUpload,
CancellationToken ct)
{
ArgumentNullException.ThrowIfNull(ispezione);
ArgumentNullException.ThrowIfNull(fileNameFilter);
var baseDir = GetInspectionBaseDir(ispezione);
if (!Directory.Exists(baseDir)) return null;
var result = new List<AttachedDto>();
var finalDir = GetInspectionFinalDir(ispezione);
if (Directory.Exists(finalDir))
{
var finalFiles = new DirectoryInfo(finalDir)
.GetFiles("*", SearchOption.TopDirectoryOnly);
foreach (var file in finalFiles)
{
if (file.Directory?.Name == ToUploadFolderName)
continue;
if (!fileNameFilter.Contains(file.Name))
continue;
ct.ThrowIfCancellationRequested();
result.Add(await ConvertToDto(
file,
AttachedDto.TypeAttached.Image,
isFromToUpload: false));
}
}
if (!includeToUpload) return result;
var toUploadDir = GetInspectionToUploadDir(ispezione);
if (!Directory.Exists(toUploadDir)) return result;
var toUploadFiles = new DirectoryInfo(toUploadDir)
.GetFiles("*", SearchOption.TopDirectoryOnly);
foreach (var file in toUploadFiles)
{
if (!fileNameFilter.Contains(file.Name))
continue;
ct.ThrowIfCancellationRequested();
result.Add(await ConvertToDto(
file,
AttachedDto.TypeAttached.Image,
isFromToUpload: true));
}
return result;
}
/// <summary>
/// Salva SEMPRE in /toUpload.
/// </summary>
public async Task<string?> SaveInspectionFile(
Ispezione ispezione,
byte[] file,
string fileName,
CancellationToken ct)
{
ArgumentNullException.ThrowIfNull(ispezione);
ArgumentNullException.ThrowIfNull(file);
ArgumentException.ThrowIfNullOrWhiteSpace(fileName);
var toUploadDir = GetInspectionToUploadDir(ispezione);
Directory.CreateDirectory(toUploadDir);
var filePath = Path.Combine(toUploadDir, fileName);
await File.WriteAllBytesAsync(filePath, file, ct);
return filePath;
}
public Task<bool> MoveInspectionFileFromToUploadToFinal(
Ispezione ispezione,
string fileName,
bool overwrite,
CancellationToken ct)
{
ArgumentNullException.ThrowIfNull(ispezione);
ArgumentException.ThrowIfNullOrWhiteSpace(fileName);
ct.ThrowIfCancellationRequested();
var toUploadDir = GetInspectionToUploadDir(ispezione);
var finalDir = GetInspectionFinalDir(ispezione);
if (!Directory.Exists(toUploadDir)) return Task.FromResult(false);
var sourcePath = Path.Combine(toUploadDir, fileName);
if (!File.Exists(sourcePath)) return Task.FromResult(false);
Directory.CreateDirectory(finalDir);
var destPath = Path.Combine(finalDir, fileName);
if (File.Exists(destPath))
{
if (!overwrite) return Task.FromResult(false);
File.Delete(destPath);
}
File.Move(sourcePath, destPath);
// Pulizia: se /toUpload resta vuota la elimino
CleanupDirectoriesIfEmpty(ispezione);
return Task.FromResult(true);
}
/// <summary>
/// Rimuove un file cercandolo prima in /toUpload e poi in final (o viceversa).
/// Default: prova a cancellare ovunque.
/// </summary>
public bool RemoveInspectionFile(
Ispezione ispezione,
string fileName,
bool removeAlsoFromFinal,
bool removeAlsoFromToUpload)
{
ArgumentNullException.ThrowIfNull(ispezione);
if (string.IsNullOrWhiteSpace(fileName)) return false;
var removed = false;
if (removeAlsoFromToUpload)
{
var toUploadPath = Path.Combine(GetInspectionToUploadDir(ispezione), fileName);
if (File.Exists(toUploadPath))
{
File.Delete(toUploadPath);
removed = true;
}
}
if (removeAlsoFromFinal)
{
var finalPath = Path.Combine(GetInspectionFinalDir(ispezione), fileName);
if (File.Exists(finalPath))
{
File.Delete(finalPath);
removed = true;
}
}
if (removed)
CleanupDirectoriesIfEmpty(ispezione);
return removed;
}
private void CleanupDirectoriesIfEmpty(Ispezione ispezione)
{
var baseDir = GetInspectionBaseDir(ispezione);
var toUploadDir = GetInspectionToUploadDir(ispezione);
// 1) se /toUpload esiste e vuota => delete
if (Directory.Exists(toUploadDir) && !Directory.EnumerateFileSystemEntries(toUploadDir).Any())
Directory.Delete(toUploadDir);
// 2) se base dir vuota (attenzione: dopo delete toUpload) => delete
if (Directory.Exists(baseDir) && !Directory.EnumerateFileSystemEntries(baseDir).Any())
Directory.Delete(baseDir);
}
public async Task<string> SaveToTempStorage(Stream file, string fileName, CancellationToken ct = default)
{
ArgumentNullException.ThrowIfNull(file);
if (file.CanSeek)
file.Position = 0;
fileName = Path.GetFileName(fileName);
var dir = FileSystem.CacheDirectory;
var filePath = Path.Combine(dir, fileName);
await using var fileStream = File.Create(filePath);
await file.CopyToAsync(fileStream, ct);
return filePath;
}
public Task CleanTempStorageAsync(CancellationToken ct = default)
{
return Task.Run(() =>
{
if (Directory.Exists(AttachedRoot))
Directory.Delete(AttachedRoot, true);
}, ct);
}
public Task OpenFile(string fileName, string filePath)
{
#if IOS
throw new NotImplementedException();
#else
return Launcher.OpenAsync(new OpenFileRequest
{
Title = "Apri file",
File = new ReadOnlyFile(filePath)
});
#endif
}
public async Task<(string originalUrl, string thumbUrl)> SaveAndCreateThumbAsync(
byte[] bytes, string fileName, CancellationToken ct = default)
{
Directory.CreateDirectory(AttachedRoot);
var id = Guid.NewGuid().ToString("N");
var safeName = SanitizeFileName(fileName);
var originalFile = $"{id}_{safeName}";
var thumbFile = $"{id}_thumb.jpg";
var originalPath = Path.Combine(AttachedRoot, originalFile);
await File.WriteAllBytesAsync(originalPath, bytes, ct);
var thumbPath = Path.Combine(AttachedRoot, thumbFile);
await ImageThumb.CreateThumbnailAsync(originalPath, thumbPath, maxSide: 320, quality: 70, ct);
return ($"https://localfiles/attached/{originalFile}",
$"https://localfiles/attached/{thumbFile}");
}
private static string SanitizeFileName(string fileName)
{
var name = Path.GetFileName(fileName);
return Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, '_'));
}
private static string GetInspectionKey(Ispezione ispezione) =>
$"{ispezione.CodMdep}_{ispezione.Data:ddMMyyyy}_{ispezione.Rilevatore.ToLower()}";
} }

View File

@@ -0,0 +1,488 @@
using System.IO.Compression;
using Microsoft.Extensions.Logging;
using SteUp.Data.LocalDb;
using SteUp.Shared.Core.Dto;
using SteUp.Shared.Core.Entities;
using SteUp.Shared.Core.Helpers;
using SteUp.Shared.Core.Interface.System;
namespace SteUp.Maui.Core.Services;
public class FileManager(
IDbPathProvider dbPathProvider,
ILogger<FileManager> logger) : IFileManager
{
private static string AttachedRoot =>
Path.Combine(FileSystem.CacheDirectory, "attached");
private async Task<AttachedDto> ConvertToDto(FileInfo file, AttachedDto.TypeAttached type, bool isFromToUpload)
{
var (origUrl, thumbUrl) = await SaveAndCreateThumbAsync(
await File.ReadAllBytesAsync(file.FullName),
file.Name
);
return new AttachedDto
{
Name = file.Name,
Path = file.FullName,
TempPath = origUrl,
ThumbPath = thumbUrl,
Type = type,
SavedOnAppData = true,
ToUpload = isFromToUpload
};
}
private const string ToUploadFolderName = "toUpload";
private string GetInspectionBaseDir(Ispezione ispezione)
{
var baseDir = FileSystem.AppDataDirectory;
return Path.Combine(baseDir, "attached", $"inspection_{GetInspectionKey(ispezione)}");
}
private string GetInspectionToUploadDir(Ispezione ispezione)
=> Path.Combine(GetInspectionBaseDir(ispezione), ToUploadFolderName);
public string GetFileToUploadDir(Ispezione ispezione, string fileName) =>
Path.Combine(GetInspectionToUploadDir(ispezione), fileName);
private string GetInspectionFinalDir(Ispezione ispezione)
=> GetInspectionBaseDir(ispezione);
/// <summary>
/// Ritorna i file dell'ispezione filtrati per nome.
/// Per default include sia "final" sia "toUpload" (utile per UI).
/// </summary>
public async Task<List<AttachedDto>?> GetInspectionFiles(
Ispezione ispezione,
List<string> fileNameFilter,
bool includeToUpload,
CancellationToken ct)
{
ArgumentNullException.ThrowIfNull(ispezione);
ArgumentNullException.ThrowIfNull(fileNameFilter);
var baseDir = GetInspectionBaseDir(ispezione);
if (!Directory.Exists(baseDir)) return null;
var result = new List<AttachedDto>();
var finalDir = GetInspectionFinalDir(ispezione);
if (Directory.Exists(finalDir))
{
var finalFiles = new DirectoryInfo(finalDir)
.GetFiles("*", SearchOption.TopDirectoryOnly);
foreach (var file in finalFiles)
{
if (file.Directory?.Name == ToUploadFolderName)
continue;
if (!fileNameFilter.Contains(file.FullName))
continue;
ct.ThrowIfCancellationRequested();
result.Add(await ConvertToDto(
file,
AttachedDto.TypeAttached.Image,
isFromToUpload: false));
}
}
if (!includeToUpload) return result;
var toUploadDir = GetInspectionToUploadDir(ispezione);
if (!Directory.Exists(toUploadDir)) return result;
var toUploadFiles = new DirectoryInfo(toUploadDir)
.GetFiles("*", SearchOption.TopDirectoryOnly);
foreach (var file in toUploadFiles)
{
if (!fileNameFilter.Contains(file.Name))
continue;
ct.ThrowIfCancellationRequested();
result.Add(await ConvertToDto(
file,
AttachedDto.TypeAttached.Image,
isFromToUpload: true));
}
return result;
}
/// <summary>
/// Salva SEMPRE in /toUpload.
/// </summary>
public async Task<string?> SaveInspectionFile(
Ispezione ispezione,
byte[] file,
string fileName,
CancellationToken ct)
{
ArgumentNullException.ThrowIfNull(ispezione);
ArgumentNullException.ThrowIfNull(file);
ArgumentException.ThrowIfNullOrWhiteSpace(fileName);
var toUploadDir = GetInspectionToUploadDir(ispezione);
Directory.CreateDirectory(toUploadDir);
var filePath = Path.Combine(toUploadDir, fileName);
await File.WriteAllBytesAsync(filePath, file, ct);
return filePath;
}
public Task<string?> MoveInspectionFileFromToUploadToFinal(
Ispezione ispezione,
string fileName,
bool overwrite,
CancellationToken ct)
{
ArgumentNullException.ThrowIfNull(ispezione);
ArgumentException.ThrowIfNullOrWhiteSpace(fileName);
ct.ThrowIfCancellationRequested();
var toUploadDir = GetInspectionToUploadDir(ispezione);
var finalDir = GetInspectionFinalDir(ispezione);
if (!Directory.Exists(toUploadDir)) return Task.FromResult<string?>(null);
var sourcePath = Path.Combine(toUploadDir, fileName);
if (!File.Exists(sourcePath)) return Task.FromResult<string?>(null);
Directory.CreateDirectory(finalDir);
var destPath = Path.Combine(finalDir, fileName);
if (File.Exists(destPath))
{
if (!overwrite) return Task.FromResult<string?>(null);
File.Delete(destPath);
}
File.Move(sourcePath, destPath);
// Pulizia: se /toUpload resta vuota la elimino
CleanupDirectoriesIfEmpty(ispezione);
return Task.FromResult<string?>(destPath);
}
/// <summary>
/// Rimuove un file cercandolo prima in /toUpload e poi in final (o viceversa).
/// Default: prova a cancellare ovunque.
/// </summary>
public bool RemoveInspectionFile(
Ispezione ispezione,
string fileName,
bool removeAlsoFromFinal,
bool removeAlsoFromToUpload)
{
ArgumentNullException.ThrowIfNull(ispezione);
if (string.IsNullOrWhiteSpace(fileName)) return false;
var removed = false;
if (removeAlsoFromToUpload)
{
var toUploadPath = Path.Combine(GetInspectionToUploadDir(ispezione), fileName);
if (File.Exists(toUploadPath))
{
File.Delete(toUploadPath);
removed = true;
}
}
if (removeAlsoFromFinal)
{
var finalPath = Path.Combine(GetInspectionFinalDir(ispezione), fileName);
if (File.Exists(finalPath))
{
File.Delete(finalPath);
removed = true;
}
}
if (removed)
CleanupDirectoriesIfEmpty(ispezione);
return removed;
}
public bool RemoveInspection(Ispezione ispezione, bool removeAlsoFromFinal, bool removeAlsoFromToUpload)
{
ArgumentNullException.ThrowIfNull(ispezione);
var removedAnything = false;
if (removeAlsoFromToUpload)
TryDeleteDirectory(GetInspectionToUploadDir(ispezione));
if (removeAlsoFromFinal)
TryDeleteDirectory(GetInspectionFinalDir(ispezione));
try
{
CleanupDirectoriesIfEmpty(ispezione);
}
catch(Exception e)
{
logger.LogError(e, e.Message);
SentrySdk.CaptureException(e);
}
return removedAnything;
void TryDeleteDirectory(string? dir)
{
if (string.IsNullOrWhiteSpace(dir)) return;
try
{
if (!Directory.Exists(dir)) return;
Directory.Delete(dir, recursive: true);
removedAnything = true;
}
catch(Exception e)
{
logger.LogError(e, e.Message);
SentrySdk.CaptureException(e);
}
}
}
private void CleanupDirectoriesIfEmpty(Ispezione ispezione)
{
var baseDir = GetInspectionBaseDir(ispezione);
var toUploadDir = GetInspectionToUploadDir(ispezione);
// 1) se /toUpload esiste e vuota => delete
if (Directory.Exists(toUploadDir) && !Directory.EnumerateFileSystemEntries(toUploadDir).Any())
Directory.Delete(toUploadDir);
// 2) se base dir vuota (attenzione: dopo delete toUpload) => delete
if (Directory.Exists(baseDir) && !Directory.EnumerateFileSystemEntries(baseDir).Any())
Directory.Delete(baseDir);
}
public async Task<string> SaveToTempStorage(Stream file, string fileName, CancellationToken ct = default)
{
ArgumentNullException.ThrowIfNull(file);
if (file.CanSeek)
file.Position = 0;
fileName = Path.GetFileName(fileName);
var dir = FileSystem.CacheDirectory;
var filePath = Path.Combine(dir, fileName);
await using var fileStream = File.Create(filePath);
await file.CopyToAsync(fileStream, ct);
return filePath;
}
public Task CleanTempStorageAsync(CancellationToken ct = default)
{
return Task.Run(() =>
{
if (Directory.Exists(AttachedRoot))
Directory.Delete(AttachedRoot, true);
}, ct);
}
public Task OpenFile(string fileName, string filePath)
{
#if IOS
throw new NotImplementedException();
#else
return Launcher.OpenAsync(new OpenFileRequest
{
Title = "Apri file",
File = new ReadOnlyFile(filePath)
});
#endif
}
public async Task<(string originalUrl, string thumbUrl)> SaveAndCreateThumbAsync(
byte[] bytes, string fileName, CancellationToken ct = default)
{
Directory.CreateDirectory(AttachedRoot);
var id = Guid.NewGuid().ToString("N");
var safeName = SanitizeFileName(fileName);
var originalFile = $"{id}_{safeName}";
var thumbFile = $"{id}_thumb.jpg";
var originalPath = Path.Combine(AttachedRoot, originalFile);
await File.WriteAllBytesAsync(originalPath, bytes, ct);
var thumbPath = Path.Combine(AttachedRoot, thumbFile);
await ImageThumb.CreateThumbnailAsync(originalPath, thumbPath, maxSide: 320, quality: 70, ct);
return ($"https://localfiles/attached/{originalFile}",
$"https://localfiles/attached/{thumbFile}");
}
private static string SanitizeFileName(string fileName)
{
var name = Path.GetFileName(fileName);
return Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, '_'));
}
private static string GetInspectionKey(Ispezione ispezione) =>
$"{ispezione.CodMdep}_{ispezione.Data:ddMMyyyy}_{ispezione.Rilevatore.ToLower()}";
public List<SendEmailDto.AttachmentsDto> GetFileForExport()
{
var attachments = new List<SendEmailDto.AttachmentsDto>();
// 1) log file singolo (se ti serve ancora)
var logFile = RetrieveLogFile();
if (!logFile.IsNullOrEmpty())
{
attachments.Add(new SendEmailDto.AttachmentsDto
{
FileName = $"logs_{DateTime.Today:yyyyMMdd}.zip",
FileContent = CreateZipBytes(logFile!)
});
}
// 2) database zip
var dbZip = CreateDatabaseZipAttachment();
if (dbZip != null) attachments.Add(dbZip);
// 3) Img zip
var attachedInfo = new DirectoryInfo(Path.Combine(FileSystem.AppDataDirectory, "attached"));
if (!attachedInfo.Exists) return attachments;
var attachedFiles = attachedInfo
.EnumerateFiles("*", SearchOption.AllDirectories)
.ToList();
if (attachedFiles.Count > 0)
{
attachments.Add(new SendEmailDto.AttachmentsDto
{
FileName = $"immagini_allegate_{DateTime.Today:yyyyMMdd}.zip",
FileContent = CreateZipBytes(attachedInfo.FullName, attachedFiles)
});
}
return attachments;
}
private static List<FileInfo>? RetrieveLogFile()
{
var appDataPath = FileSystem.AppDataDirectory;
var targetDirectory = Path.Combine(appDataPath, "logs");
var directory = new DirectoryInfo(targetDirectory);
List<FileInfo>? files = null;
if (directory.Exists)
files = directory.GetFiles().ToList();
return files;
}
private SendEmailDto.AttachmentsDto? CreateDatabaseZipAttachment()
{
var files = new[]
{
new FileInfo(dbPathProvider.GetDbPath())
};
// Filtra solo quelli esistenti
var existingFiles = files.Where(f => f.Exists).ToList();
if (existingFiles.Count == 0)
return null;
using var memoryStream = new MemoryStream();
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
foreach (var file in existingFiles)
{
var entry = archive.CreateEntry(file.Name, CompressionLevel.Optimal);
using var entryStream = entry.Open();
using var fileStream = file.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
fileStream.CopyTo(entryStream);
}
}
return new SendEmailDto.AttachmentsDto
{
FileName = $"database_{DateTime.Now:yyyyMMdd_HHmm}.zip",
FileContent = memoryStream.ToArray()
};
}
private static byte[] CreateZipBytes(IEnumerable<FileInfo> files)
{
using var ms = new MemoryStream();
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, leaveOpen: true))
{
foreach (var file in files)
{
if (!file.Exists)
continue;
// Nome dentro lo zip (evita path e collisioni minime)
var entryName = file.Name;
var entry = archive.CreateEntry(entryName, CompressionLevel.Optimal);
using var entryStream = entry.Open();
using var fileStream = file.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
fileStream.CopyTo(entryStream);
}
}
return ms.ToArray();
}
private static byte[] CreateZipBytes(string rootDir, IEnumerable<FileInfo> files)
{
using var ms = new MemoryStream();
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, leaveOpen: true))
{
foreach (var file in files)
{
if (!file.Exists)
continue;
// Path relativo rispetto a rootDir -> mantiene le directory nello zip
var relativePath = Path.GetRelativePath(rootDir, file.FullName);
// Zip usa "/" come separatore: normalizziamo per compatibilità
var entryName = relativePath.Replace('\\', '/');
var entry = archive.CreateEntry(entryName, CompressionLevel.Optimal);
using var entryStream = entry.Open();
using var fileStream = file.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
fileStream.CopyTo(entryStream);
}
}
return ms.ToArray();
}
}

View File

@@ -0,0 +1,23 @@
using SteUp.Shared.Core.Dto;
namespace SteUp.Maui.Core.Utility;
public static class UtilityFile
{
public static async Task<AttachedDto> ConvertToDto(FileResult file, AttachedDto.TypeAttached type)
{
var stream = await file.OpenReadAsync();
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
return new AttachedDto
{
Name = file.FileName,
Path = file.FullPath,
MimeType = file.ContentType,
DimensionBytes = ms.Length,
FileBytes = ms.ToArray(),
Type = type
};
}
}

View File

@@ -0,0 +1,37 @@
using Microsoft.Extensions.Logging;
namespace SteUp.Maui.Core.UtilityException;
public static class GlobalExceptionHandler
{
public static void Register(ILogger logger)
{
AppDomain.CurrentDomain.UnhandledException += (_, args) =>
{
var ex = args.ExceptionObject as Exception;
logger.LogCritical(ex, "UnhandledException (AppDomain) — IsTerminating: {t}", args.IsTerminating);
};
TaskScheduler.UnobservedTaskException += (_, args) =>
{
logger.LogCritical(args.Exception, "UnobservedTaskException");
args.SetObserved();
};
#if ANDROID
Android.Runtime.AndroidEnvironment.UnhandledExceptionRaiser += (_, args) =>
{
logger.LogCritical(args.Exception, "Android UnhandledException");
args.Handled = true;
};
#endif
#if IOS || MACCATALYST
ObjCRuntime.Runtime.MarshalManagedException += (_, args) =>
{
logger.LogCritical(args.Exception, "iOS MarshalManagedException");
args.ExceptionMode = ObjCRuntime.MarshalManagedExceptionMode.UnwindNativeCode;
};
#endif
}
}

View File

@@ -46,6 +46,7 @@ namespace SteUp.Maui
builder.RegisterSystemService(); builder.RegisterSystemService();
builder.RegisterDbServices(); builder.RegisterDbServices();
builder.RegisterMessageServices(); builder.RegisterMessageServices();
builder.RegisterLoggerServices();
return builder.Build(); return builder.Build();
} }

View File

@@ -1,7 +1,6 @@
@using CommunityToolkit.Mvvm.Messaging @using CommunityToolkit.Mvvm.Messaging
@using SteUp.Shared.Core.Entities @using SteUp.Shared.Components.SingleElements.MessageBox
@using SteUp.Shared.Core.Interface.IntegryApi @using SteUp.Shared.Core.Enum
@using SteUp.Shared.Core.Interface.System.Network
@using SteUp.Shared.Core.Messages.Ispezione @using SteUp.Shared.Core.Messages.Ispezione
@using SteUp.Shared.Core.Messages.Scheda @using SteUp.Shared.Core.Messages.Scheda
@inject INetworkService NetworkService @inject INetworkService NetworkService
@@ -40,18 +39,18 @@
Size="Size.Medium" IconSize="Size.Medium"> Size="Size.Medium" IconSize="Size.Medium">
@if (SchedaVisible) @if (SchedaVisible)
{ {
<MudFabMenuItem Disabled="!NetworkService.IsNetworkAvailable()" OnClick="@NewScheda"
Label="Nuova scheda" Color="Color.Surface"/>
if (ShowCompleteInspection) if (ShowCompleteInspection)
{ {
<MudFabMenuItem Disabled="!NetworkService.IsNetworkAvailable()" OnClick="@CompleteInspection" <MudFabMenuItem Disabled="!NetworkService.IsNetworkAvailable()" OnClick="@CompleteInspection"
Label="Concludi ispezione" Color="Color.Surface"/> Label="Concludi ispezione" Color="Color.Surface"/>
} }
<MudFabMenuItem Disabled="!NetworkService.IsNetworkAvailable()" OnClick="@NewScheda"
Label="Nuova scheda" Color="Color.Surface"/>
} }
else else
{ {
<MudFabMenuItem Disabled="!NetworkService.IsNetworkAvailable()" OnClick="@NewActivity" <MudFabMenuItem Disabled="!NetworkService.IsNetworkAvailable()" OnClick="@NewInspection"
Label="Nuova ispezione" Color="Color.Surface"/> Label="Nuova ispezione" Color="Color.Surface"/>
} }
</MudFabMenu> </MudFabMenu>
@@ -59,6 +58,9 @@
</nav> </nav>
</div> </div>
<ConfirmMessageBox @ref="_messageBox" NoText="No" YesText="Si" YesColor="Color.Primary"
Message="Completando l'ispezione non sarà più possibile editarla o aggiungere ulteriori segnalazioni. Procedere?"/>
@code @code
{ {
private bool IsVisible { get; set; } = true; private bool IsVisible { get; set; } = true;
@@ -66,6 +68,8 @@
private bool ShowCompleteInspection { get; set; } private bool ShowCompleteInspection { get; set; }
private bool SchedaVisible { get; set; } private bool SchedaVisible { get; set; }
private ConfirmMessageBox _messageBox = null!;
protected override Task OnInitializedAsync() protected override Task OnInitializedAsync()
{ {
NavigationManager.LocationChanged += (_, args) => NavigationManager.LocationChanged += (_, args) =>
@@ -82,23 +86,42 @@
IsVisible = newIsVisible; IsVisible = newIsVisible;
PlusVisible = newPlusVisible; PlusVisible = newPlusVisible;
if (location.EqualsIgnoreCase("ispezione"))
{
var ispezione = SteupDataService.InspectionPageState.Ispezione;
if (ispezione.Stato == StatusEnum.Completata) PlusVisible = false;
}
StateHasChanged(); StateHasChanged();
}; };
return Task.CompletedTask; return Task.CompletedTask;
} }
private void NewActivity() private async Task NewInspection()
{ {
_ = ModalHelper.OpenSelectShop(Dialog); if (await SteupDataService.CanOpenNewInspection())
{
_ = Dialog.OpenSelectShop();
}
else
{
await Dialog.ShowWarning("Per aprire una nuova ispezione è necessario concludere prima tutte le ispezioni aperte più vecchie di 20 giorni!");
}
} }
private void CompleteInspection() => private async Task CompleteInspection()
Messenger.Send(new CompleteInspectionMessage()); {
var result = await _messageBox.ShowAsync();
if (result is true)
Messenger.Send(new CompleteInspectionMessage());
}
private async Task NewScheda() private async Task NewScheda()
{ {
var ispezione = SteupDataService.InspectionPageState.Ispezione; var ispezione = SteupDataService.InspectionPageState.Ispezione;
var modal = await ModalHelper.OpenFormScheda(Dialog, ispezione.CodMdep, ispezione.Data, true); var modal = await Dialog.OpenFormScheda(ispezione.CodMdep, ispezione.Data, true);
if (modal is { Canceled: false }) if (modal is { Canceled: false })
Messenger.Send(new NewSchedaMessage()); Messenger.Send(new NewSchedaMessage());

View File

@@ -1,4 +1,5 @@
@page "/ispezione" @page "/ispezione"
@using Microsoft.Extensions.Logging
@using SteUp.Shared.Components.Layout @using SteUp.Shared.Components.Layout
@using SteUp.Shared.Components.Layout.Overlay @using SteUp.Shared.Components.Layout.Overlay
@using SteUp.Shared.Components.SingleElements.Card @using SteUp.Shared.Components.SingleElements.Card
@@ -7,6 +8,7 @@
@using SteUp.Shared.Core.Enum @using SteUp.Shared.Core.Enum
@using SteUp.Shared.Core.Interface.IntegryApi @using SteUp.Shared.Core.Interface.IntegryApi
@using SteUp.Shared.Core.Interface.LocalDb @using SteUp.Shared.Core.Interface.LocalDb
@using SteUp.Shared.Core.Interface.System
@using SteUp.Shared.Core.Messages.Ispezione @using SteUp.Shared.Core.Messages.Ispezione
@using SteUp.Shared.Core.Messages.Scheda @using SteUp.Shared.Core.Messages.Scheda
@inject NewSchedaService NewScheda @inject NewSchedaService NewScheda
@@ -14,6 +16,8 @@
@inject IIspezioniService IspezioniService @inject IIspezioniService IspezioniService
@inject IDialogService Dialog @inject IDialogService Dialog
@inject IIntegrySteupService IntegrySteupService @inject IIntegrySteupService IntegrySteupService
@inject IFileManager FileManager
@inject ILogger<IspezionePage> Logger
@implements IDisposable @implements IDisposable
<HeaderLayout Title="Ispezione" BackTo="Indietro" Back="true"/> <HeaderLayout Title="Ispezione" BackTo="Indietro" Back="true"/>
@@ -35,25 +39,28 @@
@($"{group.Value.Count} sched{(group.Value.Count == 1 ? "a" : "e")}") @($"{group.Value.Count} sched{(group.Value.Count == 1 ? "a" : "e")}")
</MudChip> </MudChip>
</div> </div>
<div class="action-scheda-group"> @if (SteupDataService.InspectionPageState.Ispezione.Stato != StatusEnum.Completata)
@if (NetworkService.IsNetworkAvailable()) {
{ <div class="action-scheda-group">
<MudFab StartIcon="@Icons.Material.Rounded.Add" @if (NetworkService.IsNetworkAvailable())
Color="Color.Warning" Size="Size.Small" {
OnClick="@(() => CreateNewScheda(group.Key))"/> <MudFab StartIcon="@Icons.Material.Rounded.Add"
Color="Color.Warning" Size="Size.Small"
OnClick="@(() => CreateNewScheda(group.Key))"/>
<MudFab StartIcon="@Icons.Material.Rounded.CloudSync" Label="Esporta reparto" <MudFab StartIcon="@Icons.Material.Rounded.CloudSync" Label="Esporta reparto"
Color="Color.Success" Size="Size.Small" Color="Color.Success" Size="Size.Small"
OnClick="@(() => ExportReparto(group.Key))"/> OnClick="@(() => ExportReparto(group.Key))"/>
} }
else else
{ {
<MudFab StartIcon="@Icons.Material.Rounded.Add" <MudFab StartIcon="@Icons.Material.Rounded.Add"
Label="@($"Nuova scheda su {group.Key.Descrizione}")" Label="@($"Nuova scheda su {group.Key.Descrizione}")"
Color="Color.Warning" Size="Size.Medium" Color="Color.Warning" Size="Size.Medium"
OnClick="@(() => CreateNewScheda(group.Key))"/> OnClick="@(() => CreateNewScheda(group.Key))"/>
} }
</div> </div>
}
</div> </div>
</TitleContent> </TitleContent>
<ChildContent> <ChildContent>
@@ -61,8 +68,7 @@
{ {
<SchedaCard Scheda="scheda" OnSchedaDeleted="OnSchedaDeleted" <SchedaCard Scheda="scheda" OnSchedaDeleted="OnSchedaDeleted"
OnSchedaModified="OnSchedaModified" OnSchedaModified="OnSchedaModified"
CodMdep="@SteupDataService.InspectionPageState.Ispezione.CodMdep" Ispezione="@SteupDataService.InspectionPageState.Ispezione"/>
Data="@SteupDataService.InspectionPageState.Ispezione.Data"/>
} }
</ChildContent> </ChildContent>
</MudExpansionPanel> </MudExpansionPanel>
@@ -114,7 +120,48 @@
var ispezione = SteupDataService.InspectionPageState.Ispezione; var ispezione = SteupDataService.InspectionPageState.Ispezione;
SteupDataService.InspectionPageState.Ispezione.Stato = StatusEnum.Completata; foreach (var scheda in ispezione.Schede.Where(x => x.ActivityId == null))
{
var apiResponse = await IntegrySteupService.SaveScheda(
new SaveRequestDto
{
LocalIdScheda = scheda.Id,
ActivityTypeId = scheda.ActivityTypeId,
CodJfas = scheda.CodJfas,
CodMdep = ispezione.CodMdep,
DataCreazione = ispezione.Data,
Note = scheda.Note,
PersonaRif = scheda.Responsabile,
Barcodes = scheda.Articoli.ConvertAll(x => x.Barcode),
Scandeza = (ScadenzaEnum)scheda.Scadenza,
ParentActivityId = scheda.Ispezione?.ActivityId
}
);
if (apiResponse == null) continue;
scheda.ActivityId = apiResponse.ActivityIdScheda;
await IspezioniService.UpdateSchedaAsync(scheda);
await IspezioniService.UpdateActivityIdIspezioneAsync(
ispezione.CodMdep,
ispezione.Data,
ispezione.Rilevatore,
apiResponse.ActivityIdIspezione
);
if (scheda.ImageNames == null) continue;
var fileList = (await FileManager.GetInspectionFiles(ispezione, scheda.ImageNames))?
.Where(x => x.ToUpload).ToList();
if (fileList == null) continue;
foreach (var file in fileList)
{
await IntegrySteupService.UploadFile(scheda.ActivityId!, file.FileBytes!, file.Name!);
}
}
await IntegrySteupService.CompleteInspection(ispezione.ActivityId!); await IntegrySteupService.CompleteInspection(ispezione.ActivityId!);
await IspezioniService.UpdateStatoIspezioneAsync( await IspezioniService.UpdateStatoIspezioneAsync(
@@ -124,6 +171,8 @@
StatusEnum.Completata StatusEnum.Completata
); );
SteupDataService.InspectionPageState.Ispezione.Stato = StatusEnum.Completata;
await InvokeAsync(() => await InvokeAsync(() =>
{ {
VisibleOverlay = false; VisibleOverlay = false;
@@ -140,7 +189,7 @@
StateHasChanged(); StateHasChanged();
}); });
OnError(e.Message); OnError(e, e.Message);
} }
} }
@@ -173,8 +222,7 @@
private async Task CreateNewScheda(JtbFasiDto jtbFasi) private async Task CreateNewScheda(JtbFasiDto jtbFasi)
{ {
var modal = await ModalHelper.OpenFormScheda( var modal = await Dialog.OpenFormScheda(
Dialog,
SteupDataService.InspectionPageState.Ispezione.CodMdep, SteupDataService.InspectionPageState.Ispezione.CodMdep,
SteupDataService.InspectionPageState.Ispezione.Data, SteupDataService.InspectionPageState.Ispezione.Data,
true, true,
@@ -240,11 +288,12 @@
StateHasChanged(); StateHasChanged();
} }
private void OnError(string? errorMessage) private void OnError(Exception? e, string? errorMessage)
{ {
if (e != null) Logger.LogError(e, errorMessage);
if (errorMessage == null) return; if (errorMessage == null) return;
_ = ModalHelper.ShowError(Dialog, errorMessage); _ = Dialog.ShowError(errorMessage);
} }
void IDisposable.Dispose() void IDisposable.Dispose()

View File

@@ -41,7 +41,7 @@
private async Task LoadData() private async Task LoadData()
{ {
Ispezioni = await IspezioniService.GetAllIspezioniWithSchedeAsync(); Ispezioni = await IspezioniService.GetAllIspezioniWithSchedeAsync(UserSession.User.Username);
} }
private void OnClickIspezione(Ispezione ispezione) private void OnClickIspezione(Ispezione ispezione)

View File

@@ -1,4 +1,5 @@
@page "/login" @page "/login"
@using Microsoft.Extensions.Logging
@using SteUp.Shared.Components.Layout.Spinner @using SteUp.Shared.Components.Layout.Spinner
@using SteUp.Shared.Core.BarcodeReader.Contracts @using SteUp.Shared.Core.BarcodeReader.Contracts
@using SteUp.Shared.Core.Interface.System @using SteUp.Shared.Core.Interface.System
@@ -7,6 +8,7 @@
@inject AppAuthenticationStateProvider AuthenticationStateProvider @inject AppAuthenticationStateProvider AuthenticationStateProvider
@inject IGenericSystemService GenericSystemService @inject IGenericSystemService GenericSystemService
@inject IBarcodeManager BarcodeManager @inject IBarcodeManager BarcodeManager
@inject ILogger<LoginPage> Logger
@if (Spinner) @if (Spinner)
{ {
@@ -100,12 +102,13 @@ else
} }
catch (Exception e) catch (Exception e)
{ {
ErrorMessage = e.Message;
Logger.LogError(e, ErrorMessage);
Console.WriteLine(e.Message); Console.WriteLine(e.Message);
Spinner = false; Spinner = false;
StateHasChanged(); StateHasChanged();
ErrorMessage = e.Message;
_attemptFailed = true; _attemptFailed = true;
Console.WriteLine(e); Console.WriteLine(e);
} }

View File

@@ -1,13 +1,19 @@
@page "/user" @page "/user"
@attribute [Authorize] @attribute [Authorize]
@using SteUp.Shared.Components.Layout @using SteUp.Shared.Components.Layout
@using SteUp.Shared.Components.Layout.Overlay
@using SteUp.Shared.Components.SingleElements @using SteUp.Shared.Components.SingleElements
@using SteUp.Shared.Core.Authorization.Enum @using SteUp.Shared.Core.Authorization.Enum
@using SteUp.Shared.Core.Interface.System.Network @using SteUp.Shared.Core.Dto
@using SteUp.Shared.Core.Interface.IntegryApi
@using SteUp.Shared.Core.Interface.System
@using SteUp.Shared.Core.Services @using SteUp.Shared.Core.Services
@using SteUp.Shared.Core.Utility @using SteUp.Shared.Core.Utility
@inject AppAuthenticationStateProvider AuthenticationStateProvider @inject AppAuthenticationStateProvider AuthenticationStateProvider
@inject INetworkService NetworkService @inject INetworkService NetworkService
@inject IGenericSystemService GenericSystemService
@inject IFileManager FileManager
@inject IIntegryApiService IntegryApiService
<HeaderLayout Title="Profilo"/> <HeaderLayout Title="Profilo"/>
@@ -63,18 +69,29 @@
</div> </div>
</div> </div>
<div class="container-button mud-elevation-1"> <div class="container-button ripple-container mud-elevation-1">
<MudButton Class="button-settings red-icon"
FullWidth="true"
StartIcon="@Icons.Material.Rounded.UploadFile"
Size="Size.Medium"
OnClick="@ExportLog"
Variant="Variant.Outlined">
Esporta log
</MudButton>
</div>
<div class="container-button ripple-container mud-elevation-1">
<MudButton Class="button-settings green-icon" <MudButton Class="button-settings green-icon"
FullWidth="true" FullWidth="true"
StartIcon="@Icons.Material.Outlined.Sync" StartIcon="@Icons.Material.Outlined.Sync"
Size="Size.Medium" Size="Size.Medium"
OnClick="@UpdateDb" OnClick="@UpdateDb"
Variant="Variant.Outlined"> Variant="Variant.Outlined">
Sincronizza ispezioni Sincronizza ispezioni esportate
</MudButton> </MudButton>
</div> </div>
<div class="container-button mud-elevation-1"> <div class="container-button ripple-container mud-elevation-1">
<MudButton Class="button-settings exit" <MudButton Class="button-settings exit"
FullWidth="true" FullWidth="true"
Color="Color.Error" Color="Color.Error"
@@ -89,8 +106,11 @@
</div> </div>
} }
<SpinnerOverlay VisibleOverlay="VisibleOverlay"/>
@code { @code {
private bool IsLoggedIn { get; set; } private bool IsLoggedIn { get; set; }
private bool VisibleOverlay { get; set; }
private string? CodHash { get; set; } = ""; private string? CodHash { get; set; } = "";
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
@@ -100,8 +120,41 @@
StateHasChanged(); StateHasChanged();
} }
private void UpdateDb() private async Task ExportLog()
{ {
VisibleOverlay = true;
StateHasChanged();
var profiloAzienda = LocalStorage.GetString("codHash");
var email = new SendEmailDto
{
FromName = "Integry Log",
To = "developer@integry.it",
Subject = $"SteUP - Log del {DateTime.Today:d} di {UserSession.User.Username}",
IsHtml = true,
MsgText = $"Username: <b>{UserSession.User.Username}</b><br/>" +
$"Profilo azienda: <b>{profiloAzienda}</b><br/>" +
$"ProfileDb: <b>{UserSession.ProfileDb}</b><br/>" +
$"Versione app: <b>{GenericSystemService.GetCurrentAppVersion()}</b>",
Attachments = FileManager.GetFileForExport()
};
await IntegryApiService.SendEmail(email);
VisibleOverlay = false;
StateHasChanged();
}
private async Task UpdateDb()
{
VisibleOverlay = true;
StateHasChanged();
await SteupDataService.CheckAndUpdateStatus();
VisibleOverlay = false;
StateHasChanged();
} }
private async Task Logout() private async Task Logout()

View File

@@ -3,6 +3,7 @@
@using SteUp.Shared.Core.BarcodeReader.Contracts @using SteUp.Shared.Core.BarcodeReader.Contracts
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IBarcodeManager BarcodeManager @inject IBarcodeManager BarcodeManager
@inject AuthenticationStateProvider AuthStateProvider
<ErrorBoundary @ref="ErrorBoundary"> <ErrorBoundary @ref="ErrorBoundary">
<ChildContent> <ChildContent>
@@ -57,7 +58,12 @@
LoadData = true; LoadData = true;
StateHasChanged(); StateHasChanged();
await SteupDataService.Init(); var authState = await AuthStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
if (user.Identity is { IsAuthenticated: true })
await SteupDataService.Init();
BarcodeManager.Init(); BarcodeManager.Init();
LoadData = false; LoadData = false;

View File

@@ -5,9 +5,11 @@
@using SteUp.Shared.Core.Enum @using SteUp.Shared.Core.Enum
@using SteUp.Shared.Core.Interface.IntegryApi @using SteUp.Shared.Core.Interface.IntegryApi
@using SteUp.Shared.Core.Interface.LocalDb @using SteUp.Shared.Core.Interface.LocalDb
@using SteUp.Shared.Core.Interface.System
@inject IIspezioniService IspezioniService @inject IIspezioniService IspezioniService
@inject IIntegrySteupService IntegrySteupService @inject IIntegrySteupService IntegrySteupService
@inject IDialogService Dialog @inject IDialogService Dialog
@inject IFileManager FileManager
<div class="scheda-card"> <div class="scheda-card">
<div class="scheda-body-section"> <div class="scheda-body-section">
@@ -27,58 +29,87 @@
</div> </div>
<div class="scheda-card-action"> <div class="scheda-card-action">
@if (NetworkService.IsNetworkAvailable()) @if (Ispezione.Stato != StatusEnum.Completata)
{ {
<MudFab Color="Color.Info" Size="Size.Small" StartIcon="@Icons.Material.Rounded.Edit" OnClick="@UpdateScheda"/> if (NetworkService.IsNetworkAvailable())
<MudFab Color="Color.Error" Size="Size.Small" StartIcon="@Icons.Material.Rounded.Delete" OnClick="@DeleteScheda"/> {
<MudFab Color="Color.Success" Size="Size.Small" StartIcon="@Icons.Material.Rounded.CloudSync" OnClick="@ExportScheda"/> <MudFab Color="Color.Info" Size="Size.Small" StartIcon="@Icons.Material.Rounded.Edit"
OnClick="@UpdateScheda"/>
<MudFab Color="Color.Error" Size="Size.Small" StartIcon="@Icons.Material.Rounded.Delete"
OnClick="@DeleteScheda"/>
<MudFab Color="Color.Success" Size="Size.Small" StartIcon="@Icons.Material.Rounded.CloudSync"
OnClick="@ExportScheda"/>
}
else
{
<MudFab Color="Color.Info" Size="Size.Small" StartIcon="@Icons.Material.Rounded.Edit"
OnClick="@UpdateScheda"/>
<MudFab Color="Color.Error" Size="Size.Small" StartIcon="@Icons.Material.Rounded.Delete"
OnClick="@DeleteScheda"/>
}
} }
else else
{ {
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Rounded.Edit" <MudFab Color="Color.Info" Size="Size.Small" StartIcon="@Icons.Material.Rounded.RemoveRedEye"
Color="Color.Info" Size="Size.Small" OnClick="@UpdateScheda"> OnClick="@ViewScheda"/>
Modifica <MudFab Color="Color.Success" Size="Size.Small" StartIcon="@Icons.Material.Rounded.CloudSync"
</MudButton> OnClick="@ExportImg"/>
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Rounded.Delete"
Color="Color.Error" Size="Size.Small" OnClick="@DeleteScheda">
Cancella
</MudButton>
} }
</div> </div>
</div> </div>
</div> </div>
<SpinnerOverlay VisibleOverlay="VisibleOverlay"/> <SpinnerOverlay VisibleOverlay="VisibleOverlay"/>
<ConfirmDeleteMessageBox @ref="_deleteMessageBox" Message="Confermi la cancellazione della scheda corrente?"/> <ConfirmMessageBox @ref="_messageBox" YesText="Cancella" YesColor="Color.Error"
Message="Confermi la cancellazione della scheda corrente?"/>
@code{ @code{
[Parameter] public string CodMdep { get; set; } = string.Empty; [Parameter] public Ispezione Ispezione { get; set; } = new();
[Parameter] public DateTime Data { get; set; }
[Parameter] public required Scheda Scheda { get; set; } [Parameter] public required Scheda Scheda { get; set; }
[Parameter] public EventCallback<Scheda> OnSchedaModified { get; set; } [Parameter] public EventCallback<Scheda> OnSchedaModified { get; set; }
[Parameter] public EventCallback<Scheda> OnSchedaDeleted { get; set; } [Parameter] public EventCallback<Scheda> OnSchedaDeleted { get; set; }
private bool VisibleOverlay { get; set; } private bool VisibleOverlay { get; set; }
private ConfirmDeleteMessageBox _deleteMessageBox = null!; private ConfirmMessageBox _messageBox = null!;
private async Task UpdateScheda() private async Task UpdateScheda()
{ {
var modal = await ModalHelper.OpenFormScheda(Dialog, CodMdep, Data, false, Scheda); var modal = await Dialog.OpenFormScheda(Ispezione.CodMdep, Ispezione.Data, false, Scheda);
if (modal is { Canceled: false, Data: Scheda scheda }) await OnSchedaModified.InvokeAsync(scheda); if (modal is { Canceled: false, Data: Scheda scheda }) await OnSchedaModified.InvokeAsync(scheda);
} }
private async Task DeleteScheda() private async Task DeleteScheda()
{ {
var result = await _deleteMessageBox.ShowAsync(); var result = await _messageBox.ShowAsync();
if (result is true) if (result is true)
{ {
VisibleOverlay = true;
StateHasChanged();
if (Scheda.ActivityId != null)
await IntegrySteupService.DeleteScheda(Scheda.ActivityId);
if (Scheda.ImageNames != null)
{
foreach (var fileName in Scheda.ImageNames)
{
FileManager.RemoveInspectionFile(Ispezione, fileName);
}
}
var deleteScheda = await IspezioniService.DeleteSchedaAsync(Scheda.Id); var deleteScheda = await IspezioniService.DeleteSchedaAsync(Scheda.Id);
if (deleteScheda) await OnSchedaDeleted.InvokeAsync(Scheda); if (deleteScheda) await OnSchedaDeleted.InvokeAsync(Scheda);
VisibleOverlay = false;
StateHasChanged();
} }
} }
private void ViewScheda() =>
_ = Dialog.OpenFormScheda(Ispezione.CodMdep, Ispezione.Data, false, Scheda, true);
private async Task ExportScheda() private async Task ExportScheda()
{ {
VisibleOverlay = true; VisibleOverlay = true;
@@ -90,8 +121,8 @@
LocalIdScheda = Scheda.Id, LocalIdScheda = Scheda.Id,
ActivityTypeId = Scheda.ActivityTypeId, ActivityTypeId = Scheda.ActivityTypeId,
CodJfas = Scheda.CodJfas, CodJfas = Scheda.CodJfas,
CodMdep = CodMdep, CodMdep = Ispezione.CodMdep,
DataCreazione = Data, DataCreazione = Ispezione.Data,
Note = Scheda.Note, Note = Scheda.Note,
PersonaRif = Scheda.Responsabile, PersonaRif = Scheda.Responsabile,
Barcodes = Scheda.Articoli.ConvertAll(x => x.Barcode), Barcodes = Scheda.Articoli.ConvertAll(x => x.Barcode),
@@ -105,7 +136,7 @@
Scheda.ActivityId = apiResponse.ActivityIdScheda; Scheda.ActivityId = apiResponse.ActivityIdScheda;
SteupDataService.InspectionPageState.Ispezione.ActivityId = apiResponse.ActivityIdIspezione; SteupDataService.InspectionPageState.Ispezione.ActivityId = apiResponse.ActivityIdIspezione;
await IspezioniService.UpdateActivityIdIspezioneAsync(CodMdep, Data, await IspezioniService.UpdateActivityIdIspezioneAsync(Ispezione.CodMdep, Ispezione.Data,
UserSession.User.Username, apiResponse.ActivityIdIspezione UserSession.User.Username, apiResponse.ActivityIdIspezione
); );
await IspezioniService.UpdateActivityIdSchedaAsync(Scheda.Id, Scheda.ActivityId); await IspezioniService.UpdateActivityIdSchedaAsync(Scheda.Id, Scheda.ActivityId);
@@ -115,4 +146,18 @@
StateHasChanged(); StateHasChanged();
} }
private async Task ExportImg()
{
if (Scheda.ImageNames == null) return;
var fileList = (await FileManager.GetInspectionFiles(Ispezione, Scheda.ImageNames))?
.Where(x => x.ToUpload).ToList();
if (fileList == null) return;
foreach (var file in fileList)
{
await IntegrySteupService.UploadFile(Scheda.ActivityId!, file.FileBytes!, file.Name!);
}
}
} }

View File

@@ -1,10 +1,10 @@
<MudMessageBox @ref="_confirmDelete" Title="Attenzione!" CancelText="Annulla"> <MudMessageBox @ref="_confirmDelete" Title="Attenzione!" CancelText="@NoText">
<MessageContent> <MessageContent>
@Message @Message
</MessageContent> </MessageContent>
<YesButton> <YesButton>
<MudButton Size="Size.Small" Variant="Variant.Filled" Color="Color.Error"> <MudButton Size="Size.Small" Variant="Variant.Filled" Color="YesColor">
Cancella @YesText
</MudButton> </MudButton>
</YesButton> </YesButton>
</MudMessageBox> </MudMessageBox>
@@ -12,6 +12,9 @@
@code @code
{ {
[Parameter] public string Message { get; set; } = string.Empty; [Parameter] public string Message { get; set; } = string.Empty;
[Parameter] public string YesText { get; set; } = string.Empty;
[Parameter] public string NoText { get; set; } = "Annulla";
[Parameter] public Color YesColor { get; set; } = Color.Primary;
private MudMessageBox? _confirmDelete; private MudMessageBox? _confirmDelete;

View File

@@ -1,4 +1,7 @@
<div class="container container-modal"> @using Microsoft.Extensions.Logging
@inject ILogger<ExceptionModal> Logger
<div class="container container-modal">
<div class="c-modal"> <div class="c-modal">
<div class="exception-header mb-2"> <div class="exception-header mb-2">
<i class="ri-emotion-unhappy-line"></i> <i class="ri-emotion-unhappy-line"></i>
@@ -43,6 +46,8 @@
{ {
Message = Exception.Message; Message = Exception.Message;
} }
Logger.LogError(Exception, "Errore nel componente Blazor: {Message}", Message);
StateHasChanged(); StateHasChanged();
} }

View File

@@ -1,9 +1,19 @@
<MudDialog OnBackdropClick="Cancel"> <MudDialog OnBackdropClick="Cancel">
<DialogContent> <DialogContent>
<div class="exception-header mb-2"> @if (IsWarning)
<i class="ri-emotion-unhappy-line"></i> {
<span>Ops</span> <div class="exception-header mb-2">
</div> <i class="ri-error-warning-line warning-icon"></i>
<span>Attenzione</span>
</div>
}
else
{
<div class="exception-header mb-2">
<i class="ri-emotion-unhappy-line error-icon"></i>
<span>Ops</span>
</div>
}
<div class="text"> <div class="text">
<code>@ErrorMessage</code> <code>@ErrorMessage</code>
</div> </div>
@@ -18,6 +28,7 @@
@code { @code {
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!; [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
[Parameter] public string ErrorMessage { get; set; } = string.Empty; [Parameter] public string ErrorMessage { get; set; } = string.Empty;
[Parameter] public bool IsWarning { get; set; }
private void Cancel() => MudDialog.Cancel(); private void Cancel() => MudDialog.Cancel();
} }

View File

@@ -8,9 +8,16 @@
.exception-header > i { .exception-header > i {
font-size: 3rem; font-size: 3rem;
line-height: normal; line-height: normal;
}
.error-icon {
color: var(--bs-danger); color: var(--bs-danger);
} }
.warning-icon {
color: var(--bs-warning);
}
.exception-header > span { .exception-header > span {
font-size: x-large; font-size: x-large;
font-weight: 700; font-weight: 700;

View File

@@ -1,4 +1,5 @@
@using SteUp.Shared.Components.Layout @using Microsoft.Extensions.Logging
@using SteUp.Shared.Components.Layout
@using SteUp.Shared.Components.Layout.Overlay @using SteUp.Shared.Components.Layout.Overlay
@using SteUp.Shared.Components.Layout.Spinner @using SteUp.Shared.Components.Layout.Spinner
@using SteUp.Shared.Components.SingleElements.Card.ModalForm @using SteUp.Shared.Components.SingleElements.Card.ModalForm
@@ -13,14 +14,15 @@
@inject INetworkService NetworkService @inject INetworkService NetworkService
@inject IDialogService Dialog @inject IDialogService Dialog
@inject IIntegryApiService IntegryApiService @inject IIntegryApiService IntegryApiService
@inject IAttachedService AttachedService @inject IFileManager FileManager
@inject IIspezioniService IspezioniService @inject IIspezioniService IspezioniService
@inject IIntegrySteupService IntegrySteupService @inject IIntegrySteupService IntegrySteupService
@inject OnScannerService OnScannerService @inject OnScannerService OnScannerService
@inject ILogger<ModalFormScheda> Logger
<MudDialog Class="customDialog-form"> <MudDialog Class="customDialog-form">
<DialogContent> <DialogContent>
<MudForm @ref="_form"> <MudForm ReadOnly="ReadOnly" @ref="_form">
<HeaderLayout Cancel="true" OnCancel="@Cancel" LabelSave="@LabelSave" <HeaderLayout Cancel="true" OnCancel="@Cancel" LabelSave="@LabelSave"
OnSave="Save" Title="Scheda"/> OnSave="Save" Title="Scheda"/>
@@ -75,7 +77,7 @@
} }
<MudCardContent Class="image_card"> <MudCardContent Class="image_card">
<MudText Typo="Typo.subtitle1"><b>@item.p.Name</b></MudText> <MudText Typo="Typo.subtitle1"><b>@item.p.Name</b></MudText>
@if (IsNew) @if (item.p.ToUpload)
{ {
<MudIconButton Variant="Variant.Outlined" <MudIconButton Variant="Variant.Outlined"
Icon="@Icons.Material.Rounded.Close" Icon="@Icons.Material.Rounded.Close"
@@ -90,24 +92,30 @@
</CardFormModal> </CardFormModal>
} }
<div class="container-button ripple-container"> @if (!ReadOnly)
<MudButton Class="button-settings green-icon" {
FullWidth="true" <div class="container-button ripple-container">
StartIcon="@Icons.Material.Rounded.AttachFile" <MudButton Class="button-settings green-icon"
Size="Size.Medium" FullWidth="true"
OnClick="@OpenAddAttached" StartIcon="@Icons.Material.Rounded.AttachFile"
Variant="Variant.Outlined"> Size="Size.Medium"
Aggiungi foto OnClick="@OpenAddAttached"
</MudButton> Variant="Variant.Outlined">
</div> Aggiungi foto
</MudButton>
</div>
}
<CardFormModal Title="Articoli"> <CardFormModal Title="Articoli">
<div class="input-manual-barcode"> @if (!ReadOnly)
<MudTextField FullWidth="true" ReadOnly="IsView" T="string?" Variant="Variant.Text" {
@bind-Value="ManualBarcode" Placeholder="Digita manualmente il codice"/> <div class="input-manual-barcode">
<MudIconButton Color="Color.Primary" OnClick="@OnManualBarcodeSet" <MudTextField FullWidth="true" T="string?" Variant="Variant.Text"
Size="Size.Small" Icon="@Icons.Material.Rounded.Send"/> @bind-Value="ManualBarcode" Placeholder="Digita manualmente il codice"/>
</div> <MudIconButton Color="Color.Primary" OnClick="@OnManualBarcodeSet"
Size="Size.Small" Icon="@Icons.Material.Rounded.Send"/>
</div>
}
@if (!Scheda.Articoli.IsNullOrEmpty()) @if (!Scheda.Articoli.IsNullOrEmpty())
{ {
<div class="art-list"> <div class="art-list">
@@ -129,7 +137,7 @@
} }
</CardFormModal> </CardFormModal>
@if (!IsView) @if (NetworkService.ConnectionAvailable && !ReadOnly)
{ {
<div class="container-button ripple-container"> <div class="container-button ripple-container">
<MudButton Class="button-settings red-icon" <MudButton Class="button-settings red-icon"
@@ -190,10 +198,9 @@
[Parameter] public required string CodMdep { get; set; } [Parameter] public required string CodMdep { get; set; }
[Parameter] public required DateTime Data { get; set; } [Parameter] public required DateTime Data { get; set; }
[Parameter] public bool IsNew { get; set; } [Parameter] public bool IsNew { get; set; }
[Parameter] public bool ReadOnly { get; set; }
[Parameter] public Scheda Scheda { get; set; } = new(); [Parameter] public Scheda Scheda { get; set; } = new();
private bool IsView => !NetworkService.ConnectionAvailable;
private string? ManualBarcode { get; set; } private string? ManualBarcode { get; set; }
//Overlay //Overlay
@@ -234,7 +241,7 @@
Task.Run(async () => Task.Run(async () =>
{ {
var fileList = await AttachedService.GetInspectionFiles( var fileList = await FileManager.GetInspectionFiles(
new Ispezione new Ispezione
{ {
CodMdep = CodMdep, CodMdep = CodMdep,
@@ -262,7 +269,7 @@
SaveSchedaResponseDto? apiResponse = null; SaveSchedaResponseDto? apiResponse = null;
try try
{ {
if (!IsView) if (NetworkService.ConnectionAvailable)
{ {
apiResponse = await IntegrySteupService.SaveScheda( apiResponse = await IntegrySteupService.SaveScheda(
new SaveRequestDto new SaveRequestDto
@@ -283,17 +290,19 @@
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine(e.Message); var message = e.Message;
await ModalHelper.ShowError(Dialog, e.Message); Logger.LogError(e, message);
Console.WriteLine(message);
await Dialog.ShowError(message);
} }
if (IsNew) await NewSave(apiResponse); if (IsNew) await NewSave(apiResponse);
else await Update(apiResponse); else await Update(apiResponse);
if (Scheda.ActivityId.IsValorized()) await UploadFile(Scheda.ActivityId!); if (Scheda.ActivityId.IsValorized()) await UploadFile(Scheda);
await AttachedService.CleanTempStorageAsync(); await FileManager.CleanTempStorageAsync();
SuccessAnimation = true; SuccessAnimation = true;
StateHasChanged(); StateHasChanged();
@@ -309,7 +318,7 @@
{ {
foreach (var attached in AttachedList!) foreach (var attached in AttachedList!)
{ {
var fileNameAdded = await AttachedService.SaveInspectionFile( var fileNameAdded = await FileManager.SaveInspectionFile(
new Ispezione new Ispezione
{ {
CodMdep = CodMdep, CodMdep = CodMdep,
@@ -352,7 +361,7 @@
if (!attached.ToRemove) if (!attached.ToRemove)
{ {
var fileNameAdded = await AttachedService.SaveInspectionFile( var fileNameAdded = await FileManager.SaveInspectionFile(
ispezione, ispezione,
attached.FileBytes!, attached.FileBytes!,
attached.Name! attached.Name!
@@ -363,7 +372,7 @@
Scheda.ImageNames.Add(fileNameAdded); Scheda.ImageNames.Add(fileNameAdded);
} }
else else
_ = AttachedService.RemoveInspectionFile(ispezione, attached.Name!); _ = FileManager.RemoveInspectionFile(ispezione, attached.Name!);
} }
} }
@@ -376,7 +385,7 @@
); );
} }
private async Task UploadFile(string activityId) private async Task UploadFile(Scheda scheda)
{ {
if (AttachedList.IsNullOrEmpty()) return; if (AttachedList.IsNullOrEmpty()) return;
@@ -391,16 +400,23 @@
{ {
if (file.FileBytes == null || file.Name == null) continue; if (file.FileBytes == null || file.Name == null) continue;
await IntegrySteupService.UploadFile(activityId, file.FileBytes, file.Name); await IntegrySteupService.UploadFile(scheda.ActivityId!, file.FileBytes, file.Name);
await AttachedService.MoveInspectionFileFromToUploadToFinal(ispezione, file.Name); var newPath = await FileManager.MoveInspectionFileFromToUploadToFinal(ispezione, file.Name);
if (newPath == null) continue;
var filePathToRemove = FileManager.GetFileToUploadDir(ispezione, file.Name);
scheda.ImageNames!.Remove(filePathToRemove);
scheda.ImageNames.Add(newPath);
} }
await IspezioniService.UpdateFileListSchedaAsync(scheda.Id, scheda.ImageNames);
} }
private async Task Cancel() private async Task Cancel()
{ {
if (await CheckSavePreAction()) if (await CheckSavePreAction())
{ {
await AttachedService.CleanTempStorageAsync(); await FileManager.CleanTempStorageAsync();
DisposeMessage(); DisposeMessage();
MudDialog.Cancel(); MudDialog.Cancel();
} }
@@ -474,7 +490,7 @@
private async Task OpenAddAttached() private async Task OpenAddAttached()
{ {
var result = await ModalHelper.OpenAddAttached(Dialog); var result = await Dialog.OpenAddAttached();
if (result is not { Canceled: false, Data: List<AttachedDto> attachedList }) return; if (result is not { Canceled: false, Data: List<AttachedDto> attachedList }) return;
OnLoading = true; OnLoading = true;
@@ -498,7 +514,7 @@
var a = attachedList[i]; var a = attachedList[i];
if (a.FileBytes is null || a.Name is null) continue; if (a.FileBytes is null || a.Name is null) continue;
var (origUrl, thumbUrl) = await AttachedService.SaveAndCreateThumbAsync(a.FileBytes, a.Name); var (origUrl, thumbUrl) = await FileManager.SaveAndCreateThumbAsync(a.FileBytes, a.Name);
await InvokeAsync(() => await InvokeAsync(() =>
{ {
@@ -555,13 +571,13 @@
TextLoading = null; TextLoading = null;
StateHasChanged(); StateHasChanged();
OnError(e.Message); OnError(e, e.Message);
}); });
return; return;
} }
var modal = await ModalHelper.OpenSelectArt(Dialog, articoli); var modal = await Dialog.OpenSelectArt(articoli);
await InvokeAsync(() => await InvokeAsync(() =>
{ {
@@ -614,7 +630,7 @@
{ {
var activityDescriptions = await IntegryApiService.SuggestActivityDescription(Scheda.ActivityTypeId); var activityDescriptions = await IntegryApiService.SuggestActivityDescription(Scheda.ActivityTypeId);
var modal = await ModalHelper.OpenSuggestActivityDescription(Dialog, activityDescriptions); var modal = await Dialog.OpenSuggestActivityDescription(activityDescriptions);
if (modal is { Canceled: false, Data: not null }) if (modal is { Canceled: false, Data: not null })
Scheda.Note = modal.Data!.ToString(); Scheda.Note = modal.Data!.ToString();
@@ -668,7 +684,7 @@
} }
else else
{ {
OnError("Nessun articolo trovato"); OnError(null, "Nessun articolo trovato");
} }
}); });
} }
@@ -682,19 +698,20 @@
StateHasChanged(); StateHasChanged();
}); });
OnError(e.Message); OnError(e, e.Message);
} }
} }
private void OnErrorScan(string? value) => OnError(value); private void OnErrorScan(string? value) => OnError(new Exception(value), value);
#endregion #endregion
private void OnError(string? errorMessage) private void OnError(Exception? e, string? errorMessage)
{ {
if (e != null) Logger.LogError(e, errorMessage);
if (errorMessage == null) return; if (errorMessage == null) return;
_ = ModalHelper.ShowError(Dialog, errorMessage); _ = Dialog.ShowError(errorMessage);
} }
private void RemoveArt(string barcode) private void RemoveArt(string barcode)

View File

@@ -67,19 +67,20 @@
if (FilterText.IsNullOrEmpty()) if (FilterText.IsNullOrEmpty())
{ {
FilteredList = SteupDataService.PuntiVenditaList; FilteredList = SteupDataService.PuntiVenditaList;
StateHasChanged(); }
return; else
{
FilteredList = SteupDataService.PuntiVenditaList.FindAll(x =>
(x.Indirizzo != null && x.Indirizzo.ContainsIgnoreCase(FilterText!)) ||
(x.Descrizione != null && x.Descrizione.ContainsIgnoreCase(FilterText!)) ||
(x.CodMdep != null && x.CodMdep.ContainsIgnoreCase(FilterText!)) ||
(x.Citta != null && x.Citta.ContainsIgnoreCase(FilterText!)) ||
(x.Cap != null && x.Cap.ContainsIgnoreCase(FilterText!)) ||
(x.Provincia != null && x.Provincia.ContainsIgnoreCase(FilterText!))
);
} }
FilteredList = SteupDataService.PuntiVenditaList.FindAll(x => FilteredList = FilteredList.OrderBy(x => x.CodMdep).ToList();
(x.Indirizzo != null && x.Indirizzo.ContainsIgnoreCase(FilterText!)) ||
(x.Descrizione != null && x.Descrizione.ContainsIgnoreCase(FilterText!)) ||
(x.CodMdep != null && x.CodMdep.ContainsIgnoreCase(FilterText!)) ||
(x.Citta != null && x.Citta.ContainsIgnoreCase(FilterText!)) ||
(x.Cap != null && x.Cap.ContainsIgnoreCase(FilterText!)) ||
(x.Provincia != null && x.Provincia.ContainsIgnoreCase(FilterText!))
);
StateHasChanged(); StateHasChanged();
} }

View File

@@ -6,7 +6,9 @@ namespace SteUp.Shared.Core.Data.Contracts;
public interface ISteupDataService public interface ISteupDataService
{ {
Task Init(); Task Init();
Task<bool> CanOpenNewInspection();
void RegisterAppVersion(); void RegisterAppVersion();
Task CheckAndUpdateStatus();
List<PuntoVenditaDto> PuntiVenditaList { get; } List<PuntoVenditaDto> PuntiVenditaList { get; }
InspectionPageState InspectionPageState { get; set; } InspectionPageState InspectionPageState { get; set; }

View File

@@ -3,11 +3,11 @@ using IntegryApiClient.Core.Domain.Abstraction.Contracts.Device;
using SteUp.Shared.Core.Data.Contracts; using SteUp.Shared.Core.Data.Contracts;
using SteUp.Shared.Core.Dto; using SteUp.Shared.Core.Dto;
using SteUp.Shared.Core.Dto.PageState; using SteUp.Shared.Core.Dto.PageState;
using SteUp.Shared.Core.Enum;
using SteUp.Shared.Core.Helpers; using SteUp.Shared.Core.Helpers;
using SteUp.Shared.Core.Interface.IntegryApi; using SteUp.Shared.Core.Interface.IntegryApi;
using SteUp.Shared.Core.Interface.LocalDb; using SteUp.Shared.Core.Interface.LocalDb;
using SteUp.Shared.Core.Interface.System; using SteUp.Shared.Core.Interface.System;
using SteUp.Shared.Core.Interface.System.Network;
namespace SteUp.Shared.Core.Data; namespace SteUp.Shared.Core.Data;
@@ -17,6 +17,7 @@ public class SteupDataService(
IDeviceService deviceService, IDeviceService deviceService,
IGenericSystemService genericSystemService, IGenericSystemService genericSystemService,
IIspezioniService ispezioniService, IIspezioniService ispezioniService,
IFileManager fileManager,
IDbInitializer dbInitializer) : ISteupDataService IDbInitializer dbInitializer) : ISteupDataService
{ {
public async Task Init() public async Task Init()
@@ -24,6 +25,7 @@ public class SteupDataService(
await dbInitializer.InitializeAsync(); await dbInitializer.InitializeAsync();
await LoadDataAsync(); await LoadDataAsync();
await CheckAndUpdateStatus(); await CheckAndUpdateStatus();
await CleanOldClosedInspection();
RegisterAppVersion(); RegisterAppVersion();
} }
@@ -34,9 +36,24 @@ public class SteupDataService(
); );
} }
private async Task CheckAndUpdateStatus() private async Task CleanOldClosedInspection()
{ {
var ispezioni = await ispezioniService.GetAllIspezioniWithSchedeAsync(); var ispezioni = (await ispezioniService.GetAllIspezioniWithSchedeAsync(userSession.User.Username))
.Where(x =>
x.Stato == StatusEnum.Completata &&
x.Data < DateTime.Now.AddDays(-60)
).ToList();
foreach (var ispezione in ispezioni)
{
fileManager.RemoveInspection(ispezione);
await ispezioniService.DeleteIspezioneAsync(ispezione.CodMdep, ispezione.Data, ispezione.Rilevatore);
}
}
public async Task CheckAndUpdateStatus()
{
var ispezioni = await ispezioniService.GetAllIspezioniWithSchedeAsync(userSession.User.Username);
var listActivityId = ispezioni var listActivityId = ispezioni
.Where(x => x.ActivityId != null) .Where(x => x.ActivityId != null)
.Select(x => x.ActivityId!) .Select(x => x.ActivityId!)
@@ -76,6 +93,20 @@ public class SteupDataService(
} }
} }
public async Task<bool> CanOpenNewInspection()
{
var completedInspection = await ispezioniService.GetAllIspezioni(userSession.User.Username);
if (completedInspection.IsNullOrEmpty()) return true;
//Controllo se sono presenti attività più vecchie di 20 giorni non chiuse
//Se presenti non si possono aprire nuove ispezioni
return !completedInspection.Any(x =>
x.Stato != StatusEnum.Completata &&
x.Data < DateTime.Now.AddDays(-20)
);
}
private async Task LoadDataAsync() private async Task LoadDataAsync()
{ {
if (!await userSession.IsLoggedIn()) return; if (!await userSession.IsLoggedIn()) return;

View File

@@ -0,0 +1,33 @@
using System.Text.Json.Serialization;
namespace SteUp.Shared.Core.Dto;
public class SendEmailDto
{
[JsonPropertyName("from")]
public string? From { get; set; }
[JsonPropertyName("fromName")]
public string? FromName { get; set; }
[JsonPropertyName("to")]
public string? To { get; set; }
[JsonPropertyName("subject")]
public string? Subject { get; set; }
[JsonPropertyName("msgText")]
public string? MsgText { get; set; }
[JsonPropertyName("html")]
public bool IsHtml { get; set; }
[JsonPropertyName("attachments")]
public List<AttachmentsDto>? Attachments { get; set; }
public class AttachmentsDto
{
public string FileName { get; set; } = string.Empty;
public byte[] FileContent { get; set; } = [];
}
}

View File

@@ -6,129 +6,155 @@ using SteUp.Shared.Core.Entities;
namespace SteUp.Shared.Core.Helpers; namespace SteUp.Shared.Core.Helpers;
public abstract class ModalHelper public static class ModalHelper
{ {
public static async Task<DialogResult?> OpenSelectShop(IDialogService dialog) extension(IDialogService dialog)
{ {
var modal = await dialog.ShowAsync<ModalSelectShop>( public async Task<DialogResult?> OpenSelectShop()
"ModalSelectShop", {
new DialogParameters(), var modal = await dialog.ShowAsync<ModalSelectShop>(
new DialogOptions "ModalSelectShop",
{ new DialogParameters(),
FullScreen = false, new DialogOptions
CloseButton = false, {
NoHeader = true, FullScreen = false,
BackdropClick = true CloseButton = false,
} NoHeader = true,
); BackdropClick = true
}
);
return await modal.Result; return await modal.Result;
} }
public static async Task<DialogResult?> OpenFormScheda(IDialogService dialog, string codMdep, DateTime data, public async Task<DialogResult?> OpenFormScheda(string codMdep, DateTime data,
bool isNew = false, Scheda? scheda = null) bool isNew = false, Scheda? scheda = null, bool readOnly = false)
{ {
scheda = isNew && scheda == null ? new Scheda() : scheda; scheda = isNew && scheda == null ? new Scheda() : scheda;
var modal = await dialog.ShowAsync<ModalFormScheda>( var modal = await dialog.ShowAsync<ModalFormScheda>(
"ModalFormScheda", "ModalFormScheda",
new DialogParameters<ModalFormScheda> new DialogParameters<ModalFormScheda>
{ {
{ x => x.CodMdep, codMdep }, { x => x.CodMdep, codMdep },
{ x => x.Data, data }, { x => x.Data, data },
{ x => x.IsNew, isNew }, { x => x.IsNew, isNew },
{ x => x.Scheda, scheda } { x => x.ReadOnly, readOnly },
}, { x => x.Scheda, scheda }
new DialogOptions },
{ new DialogOptions
FullScreen = true, {
CloseButton = false, FullScreen = true,
NoHeader = true CloseButton = false,
} NoHeader = true
); }
);
return await modal.Result; return await modal.Result;
} }
public static async Task<DialogResult?> OpenAddAttached(IDialogService dialog) public async Task<DialogResult?> OpenAddAttached()
{ {
var modal = await dialog.ShowAsync<ModalAddAttached>( var modal = await dialog.ShowAsync<ModalAddAttached>(
"Add attached", "Add attached",
new DialogParameters(), new DialogParameters(),
new DialogOptions new DialogOptions
{ {
FullScreen = false, FullScreen = false,
CloseButton = false, CloseButton = false,
NoHeader = true, NoHeader = true,
BackdropClick = false BackdropClick = false
} }
); );
return await modal.Result; return await modal.Result;
} }
public static async Task<DialogResult?> OpenSuggestActivityDescription(IDialogService dialog, public async Task<DialogResult?> OpenSuggestActivityDescription(List<StbActivityTyperDto>? activityTypers)
List<StbActivityTyperDto>? activityTypers) {
{ var modal = await dialog.ShowAsync<ModalSuggestDescription>(
var modal = await dialog.ShowAsync<ModalSuggestDescription>( "Suggest activity description",
"Suggest activity description", new DialogParameters<ModalSuggestDescription>
new DialogParameters<ModalSuggestDescription> {
{ { x => x.ActivityTypers, activityTypers }
{ x => x.ActivityTypers, activityTypers } },
}, new DialogOptions
new DialogOptions {
{ FullScreen = false,
FullScreen = false, CloseButton = false,
CloseButton = false, NoHeader = true,
NoHeader = true, BackdropClick = true
BackdropClick = true }
} );
);
return await modal.Result; return await modal.Result;
} }
public static async Task<DialogResult?> OpenSelectArt(IDialogService dialog, List<ArticoliInGrigliaDto>? articoli) public async Task<DialogResult?> OpenSelectArt(List<ArticoliInGrigliaDto>? articoli)
{ {
var modal = await dialog.ShowAsync<ModalSelectArt>( var modal = await dialog.ShowAsync<ModalSelectArt>(
"ModalSelectArt", "ModalSelectArt",
new DialogParameters<ModalSelectArt> new DialogParameters<ModalSelectArt>
{ {
{ x => x.Articoli, articoli } { x => x.Articoli, articoli }
}, },
new DialogOptions new DialogOptions
{ {
FullScreen = false, FullScreen = false,
CloseButton = false, CloseButton = false,
NoHeader = true, NoHeader = true,
BackdropClick = true, BackdropClick = true,
FullWidth = true, FullWidth = true,
MaxWidth = MaxWidth.ExtraLarge MaxWidth = MaxWidth.ExtraLarge
} }
); );
return await modal.Result; return await modal.Result;
} }
public static async Task ShowError(IDialogService dialog, string message) public async Task ShowError(string message)
{ {
var modal = await dialog.ShowAsync<ModalError>( var modal = await dialog.ShowAsync<ModalError>(
"ModalError", "ModalError",
new DialogParameters<ModalError> new DialogParameters<ModalError>
{ {
{ x => x.ErrorMessage, message } { x => x.ErrorMessage, message }
}, },
new DialogOptions new DialogOptions
{ {
FullScreen = false, FullScreen = false,
CloseButton = false, CloseButton = false,
NoHeader = true, NoHeader = true,
BackdropClick = true, BackdropClick = true,
FullWidth = true, FullWidth = true,
MaxWidth = MaxWidth.ExtraLarge MaxWidth = MaxWidth.ExtraLarge
} }
); );
await modal.Result; await modal.Result;
}
public async Task ShowWarning(string message)
{
var modal = await dialog.ShowAsync<ModalError>(
"ModalError",
new DialogParameters<ModalError>
{
{ x => x.ErrorMessage, message },
{ x => x.IsWarning, true }
},
new DialogOptions
{
FullScreen = false,
CloseButton = false,
NoHeader = true,
BackdropClick = true,
FullWidth = true,
MaxWidth = MaxWidth.ExtraLarge
}
);
await modal.Result;
}
} }
} }

View File

@@ -7,4 +7,6 @@ public interface IIntegryApiService
Task<bool> SystemOk(); Task<bool> SystemOk();
Task<List<StbActivityTyperDto>?> SuggestActivityDescription(string activityTypeId); Task<List<StbActivityTyperDto>?> SuggestActivityDescription(string activityTypeId);
Task SendEmail(SendEmailDto sendEmail);
} }

View File

@@ -17,4 +17,6 @@ public interface IIntegrySteupService
Task<SaveSchedaResponseDto?> SaveMultipleSchede(List<SaveRequestDto> request); Task<SaveSchedaResponseDto?> SaveMultipleSchede(List<SaveRequestDto> request);
Task CompleteInspection(string activityId); Task CompleteInspection(string activityId);
Task UploadFile(string activityId, byte[] file, string fileName); Task UploadFile(string activityId, byte[] file, string fileName);
Task DeleteScheda(string activityId);
} }

View File

@@ -1,4 +1,5 @@
using SteUp.Shared.Core.Entities; using System.Linq.Expressions;
using SteUp.Shared.Core.Entities;
using SteUp.Shared.Core.Enum; using SteUp.Shared.Core.Enum;
namespace SteUp.Shared.Core.Interface.LocalDb; namespace SteUp.Shared.Core.Interface.LocalDb;
@@ -7,7 +8,8 @@ public interface IIspezioniService
{ {
// ISPEZIONI // ISPEZIONI
Task<Ispezione?> GetIspezioneAsync(string codMdep, DateTime data, string rilevatore); Task<Ispezione?> GetIspezioneAsync(string codMdep, DateTime data, string rilevatore);
Task<List<Ispezione>> GetAllIspezioniWithSchedeAsync(); Task<List<Ispezione>> GetAllIspezioni(string rilevatore);
Task<List<Ispezione>> GetAllIspezioniWithSchedeAsync(string rilevatore);
Task AddIspezioneAsync(Ispezione ispezione); Task AddIspezioneAsync(Ispezione ispezione);
Task<Ispezione> GetOrCreateIspezioneAsync(string codMdep, DateTime data, string rilevatore); Task<Ispezione> GetOrCreateIspezioneAsync(string codMdep, DateTime data, string rilevatore);
Task<bool> UpdateIspezioneAsync(Ispezione ispezione); Task<bool> UpdateIspezioneAsync(Ispezione ispezione);
@@ -22,6 +24,7 @@ public interface IIspezioniService
Task<Scheda?> GetSchedaWithIspezioneAsync(int schedaId); Task<Scheda?> GetSchedaWithIspezioneAsync(int schedaId);
Task<bool> UpdateSchedaAsync(Scheda scheda); Task<bool> UpdateSchedaAsync(Scheda scheda);
Task<bool> UpdateActivityIdSchedaAsync(int schedaId, string? activityId); Task<bool> UpdateActivityIdSchedaAsync(int schedaId, string? activityId);
Task<bool> UpdateFileListSchedaAsync(int schedaId, List<string>? imageNames);
Task<bool> DeleteSchedaAsync(int schedaId); Task<bool> DeleteSchedaAsync(int schedaId);
Task<int> DeleteAllSchedeOfIspezioneAsync(string codMdep, DateTime data, string rilevatore); Task<int> DeleteAllSchedeOfIspezioneAsync(string codMdep, DateTime data, string rilevatore);
} }

View File

@@ -1,5 +1,4 @@
using SteUp.Shared.Core.Dto; using SteUp.Shared.Core.Dto;
using SteUp.Shared.Core.Entities;
namespace SteUp.Shared.Core.Interface.System; namespace SteUp.Shared.Core.Interface.System;
@@ -7,22 +6,4 @@ public interface IAttachedService
{ {
Task<AttachedDto?> SelectImageFromCamera(); Task<AttachedDto?> SelectImageFromCamera();
Task<List<AttachedDto>?> SelectImageFromGallery(); Task<List<AttachedDto>?> SelectImageFromGallery();
Task<List<AttachedDto>?> GetInspectionFiles(Ispezione ispezione, List<string> fileNameFilter,
bool includeToUpload = true, CancellationToken ct = default);
Task<string?> SaveInspectionFile(Ispezione ispezione, byte[] file, string fileName, CancellationToken ct = default);
bool RemoveInspectionFile(Ispezione ispezione, string fileName, bool removeAlsoFromFinal = true,
bool removeAlsoFromToUpload = true);
Task<bool> MoveInspectionFileFromToUploadToFinal(Ispezione ispezione, string fileName, bool overwrite = true,
CancellationToken ct = default);
Task<string> SaveToTempStorage(Stream file, string fileName, CancellationToken ct = default);
Task CleanTempStorageAsync(CancellationToken ct = default);
Task OpenFile(string fileName, string filePath);
Task<(string originalUrl, string thumbUrl)> SaveAndCreateThumbAsync(byte[] bytes, string fileName,
CancellationToken ct = default);
} }

View File

@@ -0,0 +1,30 @@
using SteUp.Shared.Core.Dto;
using SteUp.Shared.Core.Entities;
namespace SteUp.Shared.Core.Interface.System;
public interface IFileManager
{
Task<List<AttachedDto>?> GetInspectionFiles(Ispezione ispezione, List<string> fileNameFilter,
bool includeToUpload = true, CancellationToken ct = default);
Task<string?> SaveInspectionFile(Ispezione ispezione, byte[] file, string fileName, CancellationToken ct = default);
string GetFileToUploadDir(Ispezione ispezione, string fileName);
bool RemoveInspection(Ispezione ispezione, bool removeAlsoFromFinal = true, bool removeAlsoFromToUpload = true);
bool RemoveInspectionFile(Ispezione ispezione, string fileName, bool removeAlsoFromFinal = true,
bool removeAlsoFromToUpload = true);
Task<string?> MoveInspectionFileFromToUploadToFinal(Ispezione ispezione, string fileName, bool overwrite = true,
CancellationToken ct = default);
Task<string> SaveToTempStorage(Stream file, string fileName, CancellationToken ct = default);
Task CleanTempStorageAsync(CancellationToken ct = default);
Task OpenFile(string fileName, string filePath);
Task<(string originalUrl, string thumbUrl)> SaveAndCreateThumbAsync(byte[] bytes, string fileName,
CancellationToken ct = default);
List<SendEmailDto.AttachmentsDto> GetFileForExport();
}

View File

@@ -1,6 +1,8 @@
using IntegryApiClient.Core.Domain.Abstraction.Contracts.Account; using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using IntegryApiClient.Core.Domain.RestClient.Contacts; using IntegryApiClient.Core.Domain.RestClient.Contacts;
using MudBlazor; using Microsoft.Extensions.Logging;
using SteUp.Shared.Core.Dto; using SteUp.Shared.Core.Dto;
using SteUp.Shared.Core.Interface.IntegryApi; using SteUp.Shared.Core.Interface.IntegryApi;
@@ -8,7 +10,7 @@ namespace SteUp.Shared.Core.Services;
public class IntegryApiService( public class IntegryApiService(
IIntegryApiRestClient integryApiRestClient, IIntegryApiRestClient integryApiRestClient,
IUserSession userSession) : IIntegryApiService ILogger<IntegryApiService> logger) : IIntegryApiService
{ {
public async Task<bool> SystemOk() public async Task<bool> SystemOk()
{ {
@@ -19,6 +21,7 @@ public class IntegryApiService(
} }
catch (Exception e) catch (Exception e)
{ {
logger.LogError(e, e.Message);
Console.WriteLine(e.Message); Console.WriteLine(e.Message);
return false; return false;
} }
@@ -31,4 +34,44 @@ public class IntegryApiService(
{ "activityType", activityTypeId } { "activityType", activityTypeId }
} }
); );
public async Task SendEmail(SendEmailDto sendEmail)
{
var content = new MultipartFormDataContent();
try
{
if (sendEmail.Attachments != null)
{
foreach (var a in sendEmail.Attachments)
{
var fileContent = new ByteArrayContent(a.FileContent);
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
content.Add(fileContent, "allegati", a.FileName);
}
}
sendEmail.Attachments = null;
content.Add(
new StringContent(
JsonSerializer.Serialize(sendEmail),
Encoding.UTF8,
"application/json"
),
"request"
);
await integryApiRestClient.AuthorizedPost<object>("sendEmailNew", content);
}
catch (Exception e)
{
Console.WriteLine(e);
logger.LogError(e, e.Message);
throw;
}
finally
{
content.Dispose();
}
}
} }

View File

@@ -48,23 +48,34 @@ public class IntegrySteupService(IIntegryApiRestClient integryApiRestClient) : I
public Task CompleteInspection(string activityId) => public Task CompleteInspection(string activityId) =>
integryApiRestClient.AuthorizedPost<object>( integryApiRestClient.AuthorizedPost<object>(
$"{BaseRequest}/complete", $"{BaseRequest}/complete",
new object(),
new Dictionary<string, object>
{
{ "activityId", activityId }
}!
);
#endregion
public async Task UploadFile(string activityId, byte[] file, string fileName)
{
var queryParams = new Dictionary<string, object> { { "activityId", activityId } };
var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(file);
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
content.Add(fileContent, "file", fileName);
await integryApiRestClient.Post<object>($"{BaseRequest}/uploadAttachment", content, queryParams!);
content.Dispose();
}
public Task DeleteScheda(string activityId) =>
integryApiRestClient.AuthorizedGet<object>(
$"{BaseRequest}/deleteScheda",
new Dictionary<string, object> new Dictionary<string, object>
{ {
{ "activityId", activityId } { "activityId", activityId }
} }
); );
#endregion
public Task UploadFile(string activityId, byte[] file, string fileName)
{
var queryParams = new Dictionary<string, object> { { "activityId", activityId } };
using var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(file);
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
content.Add(fileContent, "file", fileName);
return integryApiRestClient.Post<object>($"{BaseRequest}/uploadAttachment", content, queryParams!);
}
} }