187 lines
5.9 KiB
Plaintext
187 lines
5.9 KiB
Plaintext
@page "/projects/tasks/creation/{ProjectId:int}"
|
|
|
|
@using Microsoft.AspNetCore.Authorization
|
|
@using Services;
|
|
@using Data.Entities;
|
|
@using Microsoft.AspNetCore.Components.Forms
|
|
|
|
@attribute [Authorize]
|
|
|
|
@rendermode InteractiveServer
|
|
|
|
@inject ProjectService ProjectService
|
|
@inject ProjectTaskService ProjectTaskService
|
|
@inject NavigationManager NavigationManager
|
|
@inject ILogger<ProjectTaskCreation> Logger
|
|
|
|
<h3>Project Task Creation</h3>
|
|
|
|
@if (Project is null)
|
|
{
|
|
<p class="alert alert-warning">Project not found</p>
|
|
}
|
|
else
|
|
{
|
|
<h5>@Project.Name task creation</h5>
|
|
|
|
<EditForm EditContext="@editContext" OnValidSubmit="Submit" class="mb-3" FormName="project-task-creation">
|
|
<ValidationSummary />
|
|
|
|
<div class="form-group mb-2">
|
|
<label for="title" class="form-label">Title</label>
|
|
<InputText id="title" class="form-control" @bind-Value="Model!.Title" />
|
|
<ValidationMessage For="@(() => Model!.Title)" />
|
|
</div>
|
|
|
|
<div class="form-group mb-2">
|
|
<label for="description" class="form-label">Description</label>
|
|
<InputTextArea id="description" class="form-control" @bind-Value="Model!.Description" />
|
|
<ValidationMessage For="@(() => Model!.Description)" />
|
|
</div>
|
|
|
|
<div class="form-group mb-2">
|
|
<label for="due-date" class="form-label">Due date</label>
|
|
<InputDate id="due-date" class="form-control" @bind-Value="Model!.DueDate" />
|
|
<ValidationMessage For="@(() => Model!.DueDate)" />
|
|
</div>
|
|
|
|
@if (Project.Tasks != null && Project.Tasks.Any())
|
|
{
|
|
<div class="form-group mb-3">
|
|
<label class="form-label">Previous tasks (0..N)</label>
|
|
<div class="border rounded p-2" style="max-height:220px; overflow:auto;">
|
|
@foreach (var t in Project.Tasks)
|
|
{
|
|
<div class="form-check">
|
|
<input class="form-check-input"
|
|
type="checkbox"
|
|
id="prev-@t.Id"
|
|
checked="@selectedPreviousTaskIds.Contains(t.Id)"
|
|
@onchange="e => TogglePreviousTask((ChangeEventArgs)e, t.Id)" />
|
|
<label class="form-check-label" for="prev-@t.Id">
|
|
@t.Title (@t.DueDate.ToShortDateString())
|
|
</label>
|
|
</div>
|
|
}
|
|
</div>
|
|
@if (previousTasksError is not null)
|
|
{
|
|
<div class="text-danger mt-1">@previousTasksError</div>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
<button type="submit" class="btn btn-primary">+</button>
|
|
</EditForm>
|
|
}
|
|
|
|
@code {
|
|
[Parameter]
|
|
public int ProjectId { get; set; }
|
|
|
|
public Project? Project { get; set; }
|
|
public ProjectTask? Model { get; set; }
|
|
|
|
private EditContext? editContext;
|
|
private ValidationMessageStore? messageStore;
|
|
|
|
// contient les ids sélectionnés via les checkboxes
|
|
private HashSet<int> selectedPreviousTaskIds = new();
|
|
private string? previousTasksError;
|
|
|
|
protected async override Task OnInitializedAsync()
|
|
{
|
|
Project = await ProjectService.GetProjectByIdAsync(ProjectId);
|
|
Model = new()
|
|
{
|
|
DueDate = DateTime.Now.AddDays(1)
|
|
};
|
|
|
|
editContext = new EditContext(Model!);
|
|
messageStore = new ValidationMessageStore(editContext);
|
|
|
|
editContext.OnFieldChanged += (_, args) =>
|
|
{
|
|
if (args.FieldIdentifier.FieldName == nameof(ProjectTask.DueDate))
|
|
{
|
|
ValidatePreviousDates();
|
|
}
|
|
};
|
|
}
|
|
|
|
private void TogglePreviousTask(ChangeEventArgs e, int id)
|
|
{
|
|
var isChecked = e?.Value is bool b && b;
|
|
if (!isChecked && e?.Value is string s)
|
|
isChecked = s == "true" || s == "on" || s == "checked";
|
|
|
|
if (isChecked)
|
|
selectedPreviousTaskIds.Add(id);
|
|
else
|
|
selectedPreviousTaskIds.Remove(id);
|
|
|
|
ValidatePreviousDates();
|
|
}
|
|
|
|
private void ValidatePreviousDates()
|
|
{
|
|
if (messageStore is null || editContext is null || Model is null)
|
|
return;
|
|
|
|
messageStore.Clear();
|
|
|
|
if (selectedPreviousTaskIds.Count > 0)
|
|
{
|
|
var invalids = new List<string>();
|
|
foreach (var id in selectedPreviousTaskIds)
|
|
{
|
|
var prev = Project?.Tasks?.FirstOrDefault(t => t.Id == id);
|
|
if (prev != null)
|
|
{
|
|
if (prev.DueDate >= Model.DueDate)
|
|
{
|
|
invalids.Add($"« {prev.Title} » due {prev.DueDate.ToShortDateString()} must be before the new task due date.");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (invalids.Any())
|
|
{
|
|
messageStore.Add(new FieldIdentifier(Model, nameof(Model.DueDate)), invalids);
|
|
previousTasksError = "Certaines tâches précédentes ont une date d'échéance non cohérente.";
|
|
}
|
|
else
|
|
{
|
|
previousTasksError = null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
previousTasksError = null;
|
|
}
|
|
|
|
editContext.NotifyValidationStateChanged();
|
|
}
|
|
|
|
private async Task Submit()
|
|
{
|
|
if (Model is null || Project is null || editContext is null)
|
|
return;
|
|
|
|
ValidatePreviousDates();
|
|
|
|
var valid = editContext.Validate();
|
|
if (!valid)
|
|
{
|
|
Logger.LogWarning("Project task form invalid.");
|
|
return;
|
|
}
|
|
|
|
// On passe uniquement les ids au service (solution simple et sans conflit de tracking)
|
|
Model.Project = Project;
|
|
await ProjectTaskService.AddTaskAsync(Model, selectedPreviousTaskIds);
|
|
|
|
NavigationManager.NavigateTo($"/projects/tasks/{ProjectId}");
|
|
}
|
|
}
|