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
This commit is contained in:
@@ -0,0 +1,229 @@
|
||||
@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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user