Skip to content

Fix/all/assigning task navigation#152

Merged
Cemonix merged 23 commits intomainfrom
fix/all/assigning-task_navigation
Sep 6, 2025
Merged

Fix/all/assigning task navigation#152
Cemonix merged 23 commits intomainfrom
fix/all/assigning-task_navigation

Conversation

@Cemonix
Copy link
Copy Markdown
Owner

@Cemonix Cemonix commented Sep 6, 2025

No description provided.

- Introduced a new migration to add a RowVersion column for concurrency control in the tasks table.
- Added properties for locking mechanism: LockExpiresAt, LockedAt, and LockedByUserId.
- Updated the LaberisDbContextModelSnapshot to reflect the new schema changes.
…e constructor tests

Add TaskNavigationControllerTests for task navigation functionality

Update AssetTransferStepTests, TaskManagementStepTests, TaskStatusUpdateStepTests, TaskCompletionPipelineTests, TaskVetoPipelineTests to include RowVersion property

Implement TaskLockingServiceTests for task locking functionality

Create TaskNavigationServiceTests for task navigation logic

Enhance TaskServiceTests and TaskServiceUnifiedStatusTests with RowVersion property

Refactor TaskStatusValidatorTests to include RowVersion property

Clean up Program.cs by removing unused imports
…ice tests

feat: Enhance TaskService with task navigation methods for fetching next, previous, and context tasks
@Cemonix Cemonix requested a review from Copilot September 6, 2025 19:57
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces a comprehensive task navigation and locking system to fix task assignment and navigation issues. The changes implement server-side task navigation with proper locking mechanisms to prevent concurrent access conflicts.

  • Implements task locking service to prevent concurrent task access by multiple users
  • Adds task navigation service with server-side navigation logic for next/previous tasks
  • Refactors workspace store to use server-based navigation instead of client-side task filtering
  • Adds self-assignment permissions for all project roles

Reviewed Changes

Copilot reviewed 53 out of 55 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
TaskService.cs Removes unused IAssetService dependency and adds self-assignment permission logic
TaskNavigationService.cs New service providing server-side task navigation with proper user context
TaskLockingService.cs New service managing task locks to prevent concurrent access conflicts
Task.cs Adds locking fields and concurrency token for optimistic concurrency control
TaskRepository.cs Implements GetNavigableTasksAsync for user-specific task filtering
workspaceStore.ts Refactored to use server-side navigation instead of client-side task management
taskService.ts Adds navigation API endpoints for next/previous task operations
Files not reviewed (2)
  • server/Server/Data/Migrations/Laberis/20250906185602_AddTaskLockingFields.Designer.cs: Language not supported
  • server/Server/Data/Migrations/Laberis/20250906192039_RowVersionToken.Designer.cs: Language not supported

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow
UpdatedAt = DateTime.UtcNow,
RowVersion = []
Copy link

Copilot AI Sep 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The empty array initializer [] for RowVersion is correct for Entity Framework, but consider using new byte[0] for better clarity in .NET Framework contexts or add a comment explaining this is EF's automatic handling.

Suggested change
RowVersion = []
RowVersion = new byte[0] // EF will set this automatically on insert

Copilot uses AI. Check for mistakes.
TaskId = nextTask?.TaskId,
AssetId = nextTask?.AssetId,
HasNext = nextTaskHasNext, // Use calculated value for the returned task
HasPrevious = nextIndex > 0, // Use nextIndex instead of currentIndex
Copy link

Copilot AI Sep 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The comments indicate potential confusion about navigation state calculation. Consider renaming variables or simplifying the logic - using nextIndex to determine HasPrevious for the returned task seems counterintuitive.

Suggested change
HasPrevious = nextIndex > 0, // Use nextIndex instead of currentIndex
HasPrevious = currentIndex > 0, // Use currentIndex to determine if there is a previous task

Copilot uses AI. Check for mistakes.
);

_logger.LogInformation("Found {Count} active locks for user {UserId}", lockedTasks.Count(), userId);
return [.. lockedTasks];
Copy link

Copilot AI Sep 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The collection expression syntax [.. lockedTasks] is modern but may not be familiar to all developers. Consider using lockedTasks.ToList() for better readability.

Suggested change
return [.. lockedTasks];
return lockedTasks.ToList();

Copilot uses AI. Check for mistakes.
Comment on lines +218 to +219
// Include tasks assigned to user OR unassigned tasks (available for assignment)
.Where(t => t.AssignedToUserId == userId || t.AssignedToUserId == null)
Copy link

Copilot AI Sep 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This query includes both assigned and unassigned tasks for navigation. Consider if unassigned tasks should be included in navigation context, as this could lead to unexpected behavior when users navigate to tasks they cannot work on.

Suggested change
// Include tasks assigned to user OR unassigned tasks (available for assignment)
.Where(t => t.AssignedToUserId == userId || t.AssignedToUserId == null)
// Include only tasks assigned to user
.Where(t => t.AssignedToUserId == userId)

Copilot uses AI. Check for mistakes.
Comment on lines +291 to +301
let tasksForNavigation = assignedTasks.tasks;

// If current task is not in the assigned tasks list (e.g., completed task viewed in preview mode),
// add it to the list so the UI shows correct context, but navigation will still skip completed tasks
const currentTaskInList = tasksForNavigation.find(task => task.id === currentTask.id);
if (!currentTaskInList) {
tasksForNavigation = [...tasksForNavigation, currentTask];
logger.info(`Added current task ${currentTask.id} to navigation context (likely viewing completed task in preview mode)`);
}

this.availableTasks = tasksForNavigation;
Copy link

Copilot AI Sep 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] This logic for handling tasks not in assigned list is complex and appears to be a workaround. Since server-side navigation is now implemented, consider removing this client-side task list manipulation and rely entirely on server navigation context.

Suggested change
let tasksForNavigation = assignedTasks.tasks;
// If current task is not in the assigned tasks list (e.g., completed task viewed in preview mode),
// add it to the list so the UI shows correct context, but navigation will still skip completed tasks
const currentTaskInList = tasksForNavigation.find(task => task.id === currentTask.id);
if (!currentTaskInList) {
tasksForNavigation = [...tasksForNavigation, currentTask];
logger.info(`Added current task ${currentTask.id} to navigation context (likely viewing completed task in preview mode)`);
}
this.availableTasks = tasksForNavigation;
// Use only the server-provided assigned tasks for navigation context
this.availableTasks = assignedTasks.tasks;

Copilot uses AI. Check for mistakes.
Comment on lines +53 to +56
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (string.IsNullOrEmpty(userId))
{
return Unauthorized("User ID claim not found in token.");
Copy link

Copilot AI Sep 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] This user ID extraction pattern is repeated across all three controller methods. Consider extracting this into a base controller method or using a custom authorization attribute to reduce code duplication.

Copilot uses AI. Check for mistakes.
@Cemonix Cemonix merged commit 6b020aa into main Sep 6, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants