Files
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

229 lines
7.9 KiB
Plaintext

@page "/settings"
@using Data_Coupler.Services
@using Data_Coupler.Models
@inject IJSRuntime JSRuntime
@inject ILogger<SettingsPage> Logger
<PageTitle>Impostazioni - Data Coupler</PageTitle>
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>
<i class="fas fa-cog text-primary"></i>
Impostazioni Sistema
</h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">Impostazioni</li>
</ol>
</nav>
</div>
@if (!string.IsNullOrEmpty(toastMessage))
{
<div class="alert alert-@(toastType == "success" ? "success" : toastType == "error" ? "danger" : "info") alert-dismissible fade show" role="alert">
<i class="fas fa-@(toastType == "success" ? "check-circle" : toastType == "error" ? "exclamation-circle" : "info-circle")"></i>
@toastMessage
<button type="button" class="btn-close" @onclick="ClearToast" aria-label="Close"></button>
</div>
}
<!-- Tab Navigation -->
<ul class="nav nav-tabs mb-4" id="settingsTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link @(activeTab == "backup" ? "active" : "")"
id="backup-tab"
@onclick='() => SetActiveTab("backup")'
type="button"
role="tab"
aria-controls="backup"
aria-selected="@(activeTab == "backup")">
<i class="fas fa-download me-2"></i>
Backup e Ripristino
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @(activeTab == "system" ? "active" : "")"
id="system-tab"
@onclick='() => SetActiveTab("system")'
type="button"
role="tab"
aria-controls="system"
aria-selected="@(activeTab == "system")">
<i class="fas fa-server me-2"></i>
Sistema
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @(activeTab == "security" ? "active" : "")"
id="security-tab"
@onclick='() => SetActiveTab("security")'
type="button"
role="tab"
aria-controls="security"
aria-selected="@(activeTab == "security")">
<i class="fas fa-shield-alt me-2"></i>
Sicurezza
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @(activeTab == "maintenance" ? "active" : "")"
id="maintenance-tab"
@onclick='() => SetActiveTab("maintenance")'
type="button"
role="tab"
aria-controls="maintenance"
aria-selected="@(activeTab == "maintenance")">
<i class="fas fa-tools me-2"></i>
Manutenzione
</button>
</li>
</ul>
<!-- Tab Content -->
<div class="tab-content" id="settingsTabContent">
<!-- Backup Tab -->
<div class="tab-pane fade @(activeTab == "backup" ? "show active" : "")"
id="backup"
role="tabpanel"
aria-labelledby="backup-tab">
<BackupTab OnShowToast="ShowToastTuple" />
</div>
<!-- System Tab -->
<div class="tab-pane fade @(activeTab == "system" ? "show active" : "")"
id="system"
role="tabpanel"
aria-labelledby="system-tab">
<SystemTab OnShowToast="ShowToastTuple" />
</div>
<!-- Security Tab -->
<div class="tab-pane fade @(activeTab == "security" ? "show active" : "")"
id="security"
role="tabpanel"
aria-labelledby="security-tab">
<SecurityTab OnShowToast="ShowToastTuple" />
</div>
<!-- Maintenance Tab -->
<div class="tab-pane fade @(activeTab == "maintenance" ? "show active" : "")"
id="maintenance"
role="tabpanel"
aria-labelledby="maintenance-tab">
<MaintenanceTab OnShowToast="ShowToastTuple" />
</div>
</div>
</div>
</div>
</div>
<!-- Include CSS per migliorare l'aspetto -->
<style>
.nav-tabs .nav-link {
border: 1px solid transparent;
border-radius: 0.375rem 0.375rem 0 0;
color: #6c757d;
background-color: #f8f9fa;
margin-right: 2px;
transition: all 0.15s ease-in-out;
}
.nav-tabs .nav-link:hover {
border-color: #e9ecef #e9ecef #dee2e6;
color: #495057;
background-color: #e9ecef;
}
.nav-tabs .nav-link.active {
color: #495057;
background-color: #fff;
border-color: #dee2e6 #dee2e6 #fff;
border-bottom: 1px solid #fff;
margin-bottom: -1px;
}
.tab-content {
border: 1px solid #dee2e6;
border-top: none;
border-radius: 0 0 0.375rem 0.375rem;
padding: 1.5rem;
background-color: #fff;
min-height: 500px;
}
.settings-section {
margin-bottom: 2rem;
}
.settings-section h4 {
border-bottom: 2px solid #e9ecef;
padding-bottom: 0.5rem;
margin-bottom: 1rem;
color: #495057;
}
.alert {
border-radius: 0.375rem;
border: none;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
}
.card {
border: none;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
border-radius: 0.5rem;
}
.card-header {
background-color: #f8f9fa;
border-bottom: 1px solid #dee2e6;
border-radius: 0.5rem 0.5rem 0 0 !important;
}
</style>
@code {
private string activeTab = "backup";
private string toastMessage = "";
private string toastType = "info";
protected override async Task OnInitializedAsync()
{
// Inizializzazione se necessaria
await base.OnInitializedAsync();
}
private void SetActiveTab(string tabName)
{
activeTab = tabName;
StateHasChanged();
}
private void ShowToast(string message, string type = "info")
{
toastMessage = message;
toastType = type;
StateHasChanged();
// Auto-hide dopo 5 secondi per messaggi di successo
if (type == "success")
{
_ = Task.Delay(5000).ContinueWith(_ => ClearToast());
}
}
private void ShowToastTuple((string message, string type) toast)
{
ShowToast(toast.message, toast.type);
}
private void ClearToast()
{
toastMessage = "";
toastType = "info";
InvokeAsync(StateHasChanged);
}
}