Merge pull request #5953 from sbwalker/dev

improve Edit Job  UI
This commit is contained in:
Shaun Walker
2026-01-14 08:20:48 -05:00
committed by GitHub
3 changed files with 92 additions and 121 deletions

View File

@@ -11,26 +11,24 @@
<div class="container">
@if (!_editable)
{
<div class="alert alert-warning alert-dismissible fade show mb-3 custom" role="alert">
@Localizer["JobNotEditable"]
</div>
<ModuleMessage Message="@Localizer["JobNotEditable"]" Type="MessageType.Warning" />
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Enter the job name" ResourceKey="Name">Name: </Label>
<div class="col-sm-9">
<input id="name" class="form-control" @bind="@_name" maxlength="200" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="type" HelpText="The fully qualified job type name" ResourceKey="Type">Type: </Label>
<div class="col-sm-9">
<input id="type" class="form-control" @bind="@_jobType" readonly />
<input id="type" class="form-control" @bind="@_jobType" required disabled />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Enter the job name" ResourceKey="Name">Name: </Label>
<div class="col-sm-9">
<input id="name" class="form-control" @bind="@_name" maxlength="200" required disabled="@(!_editable)" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="enabled" HelpText="Select whether you want the job enabled or not" ResourceKey="Enabled">Enabled? </Label>
<div class="col-sm-9">
<select id="enabled" class="form-select" @bind="@_isEnabled" required>
<select id="enabled" class="form-select" @bind="@_isEnabled" required disabled="@(!_editable)">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
@@ -83,17 +81,20 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="next" HelpText="Optionally modify the date and time when this job should execute next" ResourceKey="NextExecution">Next Execution: </Label>
<Label Class="col-sm-3" For="next" HelpText="The date and time when this job will execute next. This value cannot be modified. Use the settings above to control the execution of the job." ResourceKey="NextExecution">Next Execution: </Label>
<div class="col-sm-9">
<input id="next" class="form-control" @bind="@_nextDate" readonly />
<input id="next" class="form-control" @bind="@_nextDate" disabled />
</div>
</div>
</div>
<br />
<button type="button" class="btn btn-success" @onclick="SaveJob">@SharedLocalizer["Save"]</button>
@if (!_editable)
@if (_editable)
{
<button type="button" class="btn btn-danger ms-1" @onclick="ExecuteJob">@SharedLocalizer["Execute"]</button>
<button type="button" class="btn btn-success" @onclick="SaveJob">@SharedLocalizer["Save"]</button>
}
else
{
<button type="button" class="btn btn-danger" @onclick="DisableJob">@Localizer["Disable"]</button>
}
<NavLink class="btn btn-secondary ms-1" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<br />
@@ -128,6 +129,11 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync()
{
await LoadJob();
}
protected async Task LoadJob()
{
try
{
@@ -165,23 +171,15 @@
private async Task SaveJob()
{
if (!Utilities.ValidateEffectiveExpiryDates(_startDate, _endDate))
try
{
AddModuleMessage(Localizer["Message.StartEndDateError"], MessageType.Warning);
return;
}
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{
var job = await JobService.GetJobAsync(_jobId);
job.Name = _name;
job.JobType = _jobType;
var enabledChanged = job.IsEnabled != Boolean.Parse(_isEnabled);
job.IsEnabled = Boolean.Parse(_isEnabled);
if (_editable)
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{
var job = await JobService.GetJobAsync(_jobId);
job.Name = _name;
job.IsEnabled = bool.Parse(_isEnabled);
job.Frequency = _frequency;
if (job.Frequency == "O") // once
{
@@ -191,6 +189,7 @@
{
job.Interval = int.Parse(_interval);
}
job.StartDate = _startDate.HasValue && _startTime.HasValue
? LocalToUtc(_startDate.GetValueOrDefault().Date.Add(_startTime.GetValueOrDefault().TimeOfDay))
: null;
@@ -200,80 +199,63 @@
: null;
job.RetentionHistory = int.Parse(_retentionHistory);
}
if (!job.IsEnabled)
{
job.NextExecution = null;
}
else if (enabledChanged)
{
job.NextExecution = null;
if(job.StartDate != null && job.StartDate < DateTime.UtcNow)
if (!job.IsEnabled || Utilities.ValidateEffectiveExpiryDates(job.StartDate, job.EndDate))
{
var startDate = DateTime.UtcNow;
if ((job.Frequency == "d" || job.Frequency == "w" || job.Frequency == "M") && job.StartDate.Value.TimeOfDay.TotalSeconds != 0)
if (!job.IsEnabled || (job.StartDate >= DateTime.UtcNow || job.StartDate == null))
{
// set the start time
startDate = startDate.Date.Add(job.StartDate.Value.TimeOfDay);
if(startDate < DateTime.UtcNow)
{
switch (job.Frequency)
{
case "d":
startDate = startDate.AddDays(job.Interval);
break;
case "w":
startDate = startDate.AddDays(job.Interval * 7);
break;
case "M":
startDate = startDate.AddMonths(job.Interval);
break;
}
}
job.NextExecution = null;
job = await JobService.UpdateJobAsync(job);
await logger.LogInformation("Job Updated {Job}", job);
NavigationManager.NavigateTo(NavigateUrl());
}
job.StartDate = startDate;
else
{
AddModuleMessage(Localizer["Message.StartDateError"], MessageType.Warning);
}
}
else
{
AddModuleMessage(Localizer["Message.StartEndDateError"], MessageType.Warning);
}
}
await PerformSaveJobAction(job);
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning);
}
}
private async Task ExecuteJob()
{
var job = await JobService.GetJobAsync(_jobId);
if (job != null)
{
job.NextExecution = null;
await PerformSaveJobAction(job);
if(!job.IsStarted)
else
{
await JobService.StartJobAsync(_jobId);
AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning);
}
NavigationManager.NavigateTo(NavigateUrl());
}
}
private async Task PerformSaveJobAction(Job job)
{
try
{
job = await JobService.UpdateJobAsync(job);
await logger.LogInformation("Job Updated {Job}", job);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Updating Job {Job} {Error}", job, ex.Message);
await logger.LogError(ex, "Error Updating Job {JobId} {Error}", _jobId, ex.Message);
AddModuleMessage(Localizer["Error.Job.Update"], MessageType.Error);
}
}
private async Task DisableJob()
{
try
{
var job = await JobService.GetJobAsync(_jobId);
if (job != null)
{
if (job.IsExecuting)
{
AddModuleMessage(Localizer["Message.ExecutingError"], MessageType.Warning);
}
else
{
job.IsEnabled = false;
job.NextExecution = null;
job = await JobService.UpdateJobAsync(job);
await logger.LogInformation("Job Updated {Job}", job);
await LoadJob();
StateHasChanged();
}
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Updating Job {JobId} {Error}", _jobId, ex.Message);
AddModuleMessage(Localizer["Error.Job.Update"], MessageType.Error);
}
}

View File

@@ -139,7 +139,7 @@
<value>Error Updating Job</value>
</data>
<data name="Message.Required.JobInfo" xml:space="preserve">
<value>You Must Provide The Job Name, Type, Frequency, and Retention</value>
<value>You Must Provide The Job Name, Frequency, and Retention</value>
</data>
<data name="Name.HelpText" xml:space="preserve">
<value>Enter the job name</value>
@@ -154,7 +154,7 @@
<value>Select how often you want the job to run</value>
</data>
<data name="Starting.HelpText" xml:space="preserve">
<value>Optionally enter the date and time when this job should start executing</value>
<value>Optionally enter the date and time when this job should start executing. If no date or time is specified, the job will execute immediately.</value>
</data>
<data name="Ending.HelpText" xml:space="preserve">
<value>Optionally enter the date and time when this job should stop executing</value>
@@ -163,7 +163,7 @@
<value>Number of log entries to retain for this job</value>
</data>
<data name="NextExecution.HelpText" xml:space="preserve">
<value>Optionally modify the date and time when this job should execute next</value>
<value>The date and time when this job will execute next. This value cannot be modified. Use the settings above to control the execution of the job.</value>
</data>
<data name="Type.Text" xml:space="preserve">
<value>Type: </value>
@@ -193,12 +193,15 @@
<value>Execute Once</value>
</data>
<data name="Message.StartEndDateError" xml:space="preserve">
<value>Start Date cannot be after End Date.</value>
<value>The Start Date Cannot Be Later Than The End Date</value>
</data>
<data name="Message.StartDateError" xml:space="preserve">
<value>The Start Date Cannot Be Prior To The Current Date</value>
</data>
<data name="Message.ExecutingError" xml:space="preserve">
<value>The Job Is Currently Executing. You Must Wait Until The Job Has Completed.</value>
</data>
<data name="JobNotEditable" xml:space="preserve">
<value>The job properties is not able to be modified because it's enabled or running in progress.</value>
</data>
<data name="Execute" xml:space="preserve">
<value>Execute</value>
<value>The Job Cannot Be Modified As It Is Currently Enabled. You Must Disable The Job To Change Its Settings.</value>
</data>
</root>

View File

@@ -198,41 +198,27 @@ namespace Oqtane.Infrastructure
{
case "m": // minutes
nextExecution = nextExecution.AddMinutes(job.Interval);
if (nextExecution < DateTime.UtcNow) nextExecution = DateTime.UtcNow;
break;
case "H": // hours
nextExecution = nextExecution.AddHours(job.Interval);
if (nextExecution < DateTime.UtcNow) nextExecution = DateTime.UtcNow;
break;
case "d": // days
nextExecution = DateTime.UtcNow.Date.Add(nextExecution.TimeOfDay); // preserve time of day
nextExecution = nextExecution.AddDays(job.Interval);
if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0)
{
// set the start time
nextExecution = nextExecution.Date.Add(job.StartDate.Value.TimeOfDay);
}
break;
case "w": // weeks
nextExecution = DateTime.UtcNow.Date.Add(nextExecution.TimeOfDay); // preserve time of day
nextExecution = nextExecution.AddDays(job.Interval * 7);
if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0)
{
// set the start time
nextExecution = nextExecution.Date.Add(job.StartDate.Value.TimeOfDay);
}
break;
case "M": // months
nextExecution = DateTime.UtcNow.Date.Add(nextExecution.TimeOfDay); // preserve time of day
nextExecution = nextExecution.AddMonths(job.Interval);
if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0)
{
// set the start time
nextExecution = nextExecution.Date.Add(job.StartDate.Value.TimeOfDay);
}
break;
case "O": // one time
break;
}
if (nextExecution < DateTime.UtcNow)
{
nextExecution = DateTime.UtcNow;
}
return nextExecution;
}