Files
Data-Coupler/Data_Coupler/Pages/SchedulingHistory.razor.cs
Alessio d042863a56 feat: Implementazione completa sistema schedulazione con intervalli personalizzati
- Aggiunto supporto schedulazione con intervalli flessibili (secondi/minuti/ore/giorni/settimane/mesi)
- Esteso modello ProfileSchedule con campi IntervalValue e IntervalUnit
- Ottimizzato ScheduledJobService per controlli ogni 30s con esecuzione parallela
- Implementata interfaccia UI completa con anteprima real-time in italiano
- Aggiunta migrazione database AddIntervalSchedulingFields
- Implementati metodi calcolo NextExecutionTime per intervalli
- Aggiunta gestione tracking anti-duplicati e cleanup automatico
- Creata documentazione completa (6 file, 2500+ righe)

Modifiche tecniche:
- ProfileSchedule.cs: Nuovi campi e metodi CalculateNextInterval/GetScheduleDescription
- ScheduledJobService.cs: Ridotto check interval a 30s, aggiunto parallel processing
- ProfileScheduleService.cs: Supporto calcolo intervalli in UpdateNextExecutionTimeAsync
- Scheduling.razor: Aggiunta sezione UI per configurazione intervalli
- Scheduling.razor.cs: Implementato GetIntervalPreview() e gestione stato campi
2025-10-02 01:12:39 +02:00

152 lines
5.2 KiB
C#

using CredentialManager.Models;
using CredentialManager.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Logging;
using Microsoft.JSInterop;
namespace Data_Coupler.Pages;
public partial class SchedulingHistory : ComponentBase
{
[Inject] private IProfileScheduleService ScheduleService { get; set; } = null!;
[Inject] private IJSRuntime JSRuntime { get; set; } = null!;
[Inject] private ILogger<SchedulingHistory> Logger { get; set; } = null!;
protected List<ScheduleExecutionHistory>? executionHistory;
protected ScheduleExecutionHistory? selectedExecution;
protected bool isLoading = false;
protected override async Task OnInitializedAsync()
{
await LoadHistory();
}
protected async Task LoadHistory()
{
if (isLoading) return;
isLoading = true;
StateHasChanged();
try
{
// Carica le ultime 100 esecuzioni
executionHistory = await ScheduleService.GetRecentExecutionsAsync(100);
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nel caricamento dello storico delle esecuzioni");
await ShowErrorMessage("Errore nel caricamento dello storico: " + ex.Message);
}
finally
{
isLoading = false;
StateHasChanged();
}
}
protected async Task ShowExecutionDetails(ScheduleExecutionHistory execution)
{
try
{
// Carica i dettagli completi dell'esecuzione
selectedExecution = await ScheduleService.GetExecutionByIdAsync(execution.Id);
if (selectedExecution != null)
{
await ShowModal();
}
else
{
await ShowErrorMessage("Dettagli dell'esecuzione non trovati.");
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Errore nel caricamento dei dettagli esecuzione {ExecutionId}", execution.Id);
await ShowErrorMessage("Errore nel caricamento dei dettagli: " + ex.Message);
}
}
protected async Task ShowModal()
{
StateHasChanged();
await Task.Delay(100);
try
{
// Proviamo prima con l'approccio Bootstrap standard
await JSRuntime.InvokeVoidAsync("eval",
"if (typeof bootstrap !== 'undefined' && bootstrap.Modal) { " +
"var modal = new bootstrap.Modal(document.getElementById('executionDetailModal')); " +
"modal.show(); " +
"} else { " +
"document.getElementById('executionDetailModal').style.display = 'block'; " +
"document.getElementById('executionDetailModal').classList.add('show'); " +
"}");
}
catch (Exception)
{
// Fallback: mostra il modal manualmente
await JSRuntime.InvokeVoidAsync("eval",
"var modal = document.getElementById('executionDetailModal');" +
"modal.style.display = 'block';" +
"modal.classList.add('show');" +
"document.body.classList.add('modal-open');" +
"var backdrop = document.createElement('div');" +
"backdrop.className = 'modal-backdrop fade show';" +
"document.body.appendChild(backdrop);");
}
}
protected string FormatDuration(TimeSpan duration)
{
if (duration.TotalHours >= 1)
{
return duration.ToString(@"h\:mm\:ss");
}
else if (duration.TotalMinutes >= 1)
{
return duration.ToString(@"m\:ss");
}
else
{
return $"{duration.TotalSeconds:F1}s";
}
}
protected async Task HideModal()
{
try
{
await JSRuntime.InvokeVoidAsync("eval",
"if (typeof bootstrap !== 'undefined' && bootstrap.Modal) { " +
"var modalElement = document.getElementById('executionDetailModal'); " +
"var modal = bootstrap.Modal.getInstance(modalElement); " +
"if (modal) modal.hide(); " +
"} else { " +
"document.getElementById('executionDetailModal').style.display = 'none'; " +
"document.getElementById('executionDetailModal').classList.remove('show'); " +
"document.body.classList.remove('modal-open'); " +
"var backdrop = document.querySelector('.modal-backdrop'); " +
"if (backdrop) backdrop.remove(); " +
"}");
}
catch (Exception)
{
// Fallback: nascondi il modal manualmente
await JSRuntime.InvokeVoidAsync("eval",
"var modal = document.getElementById('executionDetailModal');" +
"modal.style.display = 'none';" +
"modal.classList.remove('show');" +
"document.body.classList.remove('modal-open');" +
"var backdrop = document.querySelector('.modal-backdrop');" +
"if (backdrop) backdrop.remove();");
}
}
private async Task ShowErrorMessage(string message)
{
await JSRuntime.InvokeVoidAsync("alert", "Errore: " + message);
}
}