@page "/tasks/assign" @using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Authorization @using TaskTrackingSystem.Shared.Models.Task @using TaskTrackingSystem.Shared.Models.Project @using TaskTrackingSystem.Shared.Models.Role @rendermode @(new InteractiveServerRenderMode(prerender: false)) @attribute [Microsoft.AspNetCore.Authorization.Authorize] @inject ApiClientService ApiClient @inject IJSRuntime JS @inject AuthenticationStateProvider AuthStateProvider @inject MenuAuthorizationService MenuAuthorization @AppLocalization.PageTitle("taskAssignment", "Task Assignment")

@AppLocalization.PageTitle("taskAssignment", "Task Assignment")

@AppLocalization.PageDescription("taskAssignment", "Assign tasks to project members and track task owners.")

@if (isLoading) { } else {

@AppLocalization.PageTitle("tasks", "Tasks")

@if (FilteredTasks.Any()) { @foreach (var task in FilteredTasks) { var isSelected = SelectedTask?.Id == task.Id; var proj = projects.FirstOrDefault(p => p.Id == task.ProjectId); } } else {
@AppLocalization.Text("common.noTasksFound", "No tasks found.")
}
@if (SelectedTask == null) {

@AppLocalization.Text("common.noTaskSelected", "No Task Selected")

@AppLocalization.Text("task.selectTaskPrompt", "Select a task from the left panel to assign it to a team member.")

} else {
@AppLocalization.Text("common.currentlyAssigningTask", "Currently Assigning Task") TSK-@AppLocalization.Number(SelectedTask.Id)

@Content.GetTaskTitle(SelectedTask)

@if (!string.IsNullOrEmpty(Content.GetTaskDescription(SelectedTask))) {

@Content.GetTaskDescription(SelectedTask)

}
@AppLocalization.Text("common.status", "Status"): @GetStatusLabel(SelectedTask.StatusId) @AppLocalization.Text("common.priority", "Priority"): @GetPriorityLabel(SelectedTask.PriorityId) @DisplayFormats.Date(SelectedTask.DueDate)
@AppLocalization.Text("task.selectedAssignee", "Selected Assignee")
@{ var targetAssignee = TargetAssignee; } @if (targetAssignee != null) { @(targetAssignee.FirstName.Length > 0 ? targetAssignee.FirstName[0].ToString().ToUpper() : "")@(targetAssignee.LastName.Length > 0 ? targetAssignee.LastName[0].ToString().ToUpper() : "") @Content.GetUserFullName(targetAssignee) @@@targetAssignee.Username } else { @AppLocalization.Text("common.unassigned", "Unassigned") }
@if (isAdminUser) {
} else {
@AppLocalization.Text("project.employeesInProject", "Employees in this project")
}
@AppLocalization.Text("common.selected", "Selected"): @(TargetAssigneeId.HasValue ? allUsers.FirstOrDefault(u => u.Id == TargetAssigneeId)?.Username ?? AppLocalization.Text("common.selected", "Selected") : AppLocalization.Text("common.unassigned", "Unassigned"))
@foreach (var user in FilteredUsers) { var isSelected = TargetAssigneeId == user.Id; }
@if (!string.IsNullOrEmpty(statusMessage)) {

@statusMessage

}
@if (canSaveAssignments) { }
}
}
@code { private bool isLoading = true; private bool isSaving = false; private bool canSaveAssignments = false; private bool showConfirmDialog = false; private string? statusMessage; private string? successMessage; private bool isError = false; private List tasks = new(); private List projects = new(); private List allUsers = new(); private List projectMembers = new(); private List roles = new(); private long? adminRoleId; private long? employeeRoleId; private bool isAdminUser; private long selectedProjectId = 0; private long selectedProjectIdInput = 0; private long selectedRoleId = 0; private string taskSearchInput = ""; private string taskSearch = ""; private string userSearchInput = ""; private string userSearch = ""; private TaskDto? SelectedTask { get; set; } private long? TargetAssigneeId { get; set; } protected override async Task OnInitializedAsync() { var authState = await AuthStateProvider.GetAuthenticationStateAsync(); isAdminUser = authState.User.IsInRole("Admin"); canSaveAssignments = authState.User.IsInRole("Admin") || await MenuAuthorization.HasAccessCodeAsync(authState.User, "Tasks_Update"); var client = ApiClient.CreateClient(); try { var tasksTask = client.GetFromJsonAsync>("Task", Serialization.CaseInsensitive); var projectsTask = client.GetFromJsonAsync>("Project", Serialization.CaseInsensitive); var usersTask = client.GetFromJsonAsync>("User", Serialization.CaseInsensitive); var rolesTask = client.GetFromJsonAsync>("Role", Serialization.CaseInsensitive); await Task.WhenAll(tasksTask, projectsTask, usersTask, rolesTask); tasks = tasksTask.Result ?? new(); projects = projectsTask.Result ?? new(); allUsers = usersTask.Result ?? new(); var allLoadedRoles = rolesTask.Result ?? new(); var adminRole = allLoadedRoles.FirstOrDefault(r => string.Equals(r.Name, "Admin", StringComparison.OrdinalIgnoreCase)); var employeeRole = allLoadedRoles.FirstOrDefault(r => string.Equals(r.Name, "Employee", StringComparison.OrdinalIgnoreCase)); adminRoleId = adminRole?.Id; employeeRoleId = employeeRole?.Id; roles = allLoadedRoles.Where(r => !string.Equals(r.Name, "Admin", StringComparison.OrdinalIgnoreCase)).ToList(); if (!isAdminUser && employeeRoleId.HasValue) { selectedRoleId = employeeRoleId.Value; } } catch (Exception ex) { Console.WriteLine($"Error loading initial data: {ex.Message}"); } finally { isLoading = false; } } protected override async Task OnAfterRenderAsync(bool firstRender) { await JS.InvokeVoidAsync("initIcons"); } private IEnumerable FilteredTasks { get { var result = tasks.AsEnumerable(); if (selectedProjectId > 0) { result = result.Where(t => t.ProjectId == selectedProjectId); } if (!string.IsNullOrWhiteSpace(taskSearch)) { result = result.Where(t => t.Title.Contains(taskSearch, StringComparison.OrdinalIgnoreCase) || (!string.IsNullOrWhiteSpace(t.TitleMy) && t.TitleMy.Contains(taskSearch, StringComparison.OrdinalIgnoreCase)) || (!string.IsNullOrWhiteSpace(t.Description) && t.Description.Contains(taskSearch, StringComparison.OrdinalIgnoreCase)) || (!string.IsNullOrWhiteSpace(t.DescriptionMy) && t.DescriptionMy.Contains(taskSearch, StringComparison.OrdinalIgnoreCase))); } return result; } } private IEnumerable FilteredUsers { get { var result = projectMembers.Where(u => u.IsActive); if (adminRoleId.HasValue) { result = result.Where(u => u.RoleId != adminRoleId.Value); } if (isAdminUser && selectedRoleId > 0) { result = result.Where(u => u.RoleId == selectedRoleId); } else if (!isAdminUser && employeeRoleId.HasValue) { result = result.Where(u => u.RoleId == employeeRoleId.Value); } if (!string.IsNullOrWhiteSpace(userSearch)) { result = result.Where(u => Content.GetUserFullName(u).Contains(userSearch, StringComparison.OrdinalIgnoreCase) || $"{u.FirstName} {u.LastName} {u.FirstNameMy} {u.LastNameMy}".Contains(userSearch, StringComparison.OrdinalIgnoreCase) || u.Username.Contains(userSearch, StringComparison.OrdinalIgnoreCase)); } return result; } } private UserDto? TargetAssignee => TargetAssigneeId.HasValue ? allUsers.FirstOrDefault(u => u.Id == TargetAssigneeId.Value) : null; private void ApplyTaskSearch() { taskSearch = taskSearchInput; } private void ApplyUserSearch() { userSearch = userSearchInput; } private void ApplyProjectFilter() { selectedProjectId = selectedProjectIdInput; SelectedTask = null; TargetAssigneeId = null; projectMembers.Clear(); statusMessage = null; } private void ResetProjectFilter() { selectedProjectIdInput = 0; selectedProjectId = 0; SelectedTask = null; TargetAssigneeId = null; projectMembers.Clear(); statusMessage = null; } private void ResetTaskSearch() { taskSearchInput = ""; taskSearch = ""; } private void ResetUserSearch() { userSearchInput = ""; userSearch = ""; selectedRoleId = 0; } private async Task SelectTask(TaskDto task) { SelectedTask = task; TargetAssigneeId = task.AssignedTo; statusMessage = null; await LoadProjectMembersAsync(task.ProjectId); } private void SetTargetAssignee(long? userId) { TargetAssigneeId = userId; statusMessage = null; } private void RequestSaveConfirmation() { if (SelectedTask == null) return; showConfirmDialog = true; } private void CloseConfirmDialog() { showConfirmDialog = false; } private async Task ConfirmSaveAssignment() { showConfirmDialog = false; await SaveAssignment(); } private async Task LoadProjectMembersAsync(long projectId) { projectMembers.Clear(); if (projectId <= 0) { return; } var client = ApiClient.CreateClient(); try { var membersResult = await client.GetFromJsonAsync>>($"Project/{projectId}/members", Serialization.CaseInsensitive); if (membersResult?.IsSuccess == true && membersResult.Value != null) { projectMembers = membersResult.Value.ToList(); } } catch (Exception ex) { Console.WriteLine($"Error loading project members: {ex.Message}"); statusMessage = AppLocalization.Text("task.failedLoadProjectMembers", "Failed to load project members."); isError = true; } finally { await InvokeAsync(StateHasChanged); await JS.InvokeVoidAsync("initIcons"); } } private async Task SaveAssignment() { if (SelectedTask == null) return; isSaving = true; statusMessage = null; var client = ApiClient.CreateClient(); try { var dto = new UpdateTaskDto { Title = SelectedTask.Title, TitleMy = SelectedTask.TitleMy, Description = SelectedTask.Description, DescriptionMy = SelectedTask.DescriptionMy, StatusId = SelectedTask.StatusId, PriorityId = SelectedTask.PriorityId, DueDate = SelectedTask.DueDate, AssignedTo = TargetAssigneeId, AssignedBy = SelectedTask.AssignedBy }; var response = await client.PutAsJsonAsync($"Task/{SelectedTask.Id}", dto); if (response.IsSuccessStatusCode) { SelectedTask.AssignedTo = TargetAssigneeId; var taskInList = tasks.FirstOrDefault(t => t.Id == SelectedTask.Id); if (taskInList != null) { taskInList.AssignedTo = TargetAssigneeId; } statusMessage = AppLocalization.Text("task.assigneeUpdated", "Task assignee updated successfully!"); successMessage = AppLocalization.Text("common.assignmentSaved", "Assignment saved successfully!"); isError = false; } else { var apiError = await ReadApiErrorAsync(response); statusMessage = string.IsNullOrWhiteSpace(apiError) ? AppLocalization.Text("task.failedUpdateAssignee", "Failed to update assignee.") : AppLocalization.ErrorMessage(apiError, AppLocalization.Text("task.failedUpdateAssignee", "Failed to update assignee.")); isError = true; } } catch (Exception ex) { statusMessage = AppLocalization.ErrorMessage(ex.Message, AppLocalization.Text("task.failedUpdateAssignee", "Failed to update assignee.")); isError = true; } finally { isSaving = false; await JS.InvokeVoidAsync("initIcons"); } } private static async Task ReadApiErrorAsync(HttpResponseMessage response) { try { var body = await response.Content.ReadAsStringAsync(); if (string.IsNullOrWhiteSpace(body)) { return null; } try { var result = System.Text.Json.JsonSerializer.Deserialize(body, Serialization.CaseInsensitive); if (!string.IsNullOrWhiteSpace(result?.ErrorMessage)) { return result.ErrorMessage; } } catch { // Try validation/problem-details payloads below. } try { using var doc = System.Text.Json.JsonDocument.Parse(body); var root = doc.RootElement; if (root.TryGetProperty("errorMessage", out var errorMessage) && errorMessage.ValueKind == System.Text.Json.JsonValueKind.String) { return errorMessage.GetString(); } if (root.TryGetProperty("message", out var message) && message.ValueKind == System.Text.Json.JsonValueKind.String) { return message.GetString(); } if (root.TryGetProperty("errors", out var errors) && errors.ValueKind == System.Text.Json.JsonValueKind.Object) { var messages = new List(); foreach (var property in errors.EnumerateObject()) { if (property.Value.ValueKind != System.Text.Json.JsonValueKind.Array) { continue; } foreach (var item in property.Value.EnumerateArray()) { if (item.ValueKind == System.Text.Json.JsonValueKind.String) { messages.Add(item.GetString() ?? string.Empty); } } } return string.Join(" ", messages.Where(message => !string.IsNullOrWhiteSpace(message))); } } catch { return body; } return body; } catch { return null; } } private string GetStatusBadge(AppTaskStatus statusId) => statusId switch { AppTaskStatus.Todo => "bg-slate-100 text-slate-700", AppTaskStatus.InProgress => "bg-blue-100 text-blue-700", AppTaskStatus.Done => "bg-emerald-100 text-emerald-700", _ => "bg-slate-100 text-slate-600" }; private string GetStatusLabel(AppTaskStatus statusId) => statusId switch { AppTaskStatus.Todo => AppLocalization.Text("status.todo", "To Do"), AppTaskStatus.InProgress => AppLocalization.Text("status.inprogress", "In Progress"), AppTaskStatus.Done => AppLocalization.Text("status.done", "Done"), _ => AppLocalization.Text("common.unknown", "Unknown") }; private string GetPriorityBadge(TaskPriority priorityId) => priorityId switch { TaskPriority.Low => "bg-slate-100 text-slate-600", TaskPriority.Medium => "bg-amber-100 text-amber-700", TaskPriority.High => "bg-red-100 text-red-700", _ => "bg-slate-100 text-slate-600" }; private string GetPriorityLabel(TaskPriority priorityId) => priorityId switch { TaskPriority.Low => AppLocalization.Text("priority.low", "Low"), TaskPriority.Medium => AppLocalization.Text("priority.medium", "Medium"), TaskPriority.High => AppLocalization.Text("priority.high", "High"), _ => "-" }; }