@@ -62,6 +62,37 @@ public async Task<IActionResult> GetAll([FromQuery] string? projectId = null)
6262 }
6363 }
6464
65+ // GET /api/subtasks/project/{projectId} - Get all subtasks for a specific project
66+ [ HttpGet ( "project/{projectId}" ) ]
67+ [ Authorize ( Policy = "ProjectRead" ) ]
68+ public async Task < IActionResult > GetByProject ( string projectId )
69+ {
70+ if ( string . IsNullOrWhiteSpace ( projectId ) )
71+ return BadRequest ( new { message = "ProjectId is required." } ) ;
72+
73+ try
74+ {
75+ // If client, verify they have access to the project
76+ if ( User . IsInRole ( "Client" ) )
77+ {
78+ var db = _mongoDbService . GetDatabase ( ) ;
79+ var projectsCollection = db . GetCollection < FlowModels . Project > ( "project" ) ;
80+ var userId = User . FindFirst ( System . Security . Claims . ClaimTypes . NameIdentifier ) ? . Value ;
81+
82+ var project = await projectsCollection . Find ( p => p . Id == projectId ) . FirstOrDefaultAsync ( ) ;
83+ if ( project == null || project ? . Permissions == null || ( userId != null && ! project . Permissions . ContainsKey ( userId ) ) )
84+ return StatusCode ( 403 , new { message = "You do not have permission to view tasks from this project." } ) ;
85+ }
86+
87+ var subTasks = await _subTasksCollection . Find ( st => st . ProjectId == projectId ) . ToListAsync ( ) ;
88+ return Ok ( subTasks ?? new List < SubTaskModel > ( ) ) ;
89+ }
90+ catch ( Exception ex )
91+ {
92+ return StatusCode ( 500 , new { message = "Failed to fetch subtasks for project." , detail = ex . Message } ) ;
93+ }
94+ }
95+
6596 // GET /api/subtasks/me - Get subtasks for the currently authenticated user
6697 [ HttpGet ( "me" ) ]
6798 public async Task < IActionResult > GetForCurrentUser ( )
@@ -165,6 +196,22 @@ public async Task<IActionResult> Create([FromBody] CreateSubTaskDto subTaskDto)
165196 if ( string . IsNullOrWhiteSpace ( subTaskDto . ProjectId ) )
166197 return BadRequest ( new { message = "ProjectId is required." } ) ;
167198
199+ // Parse dates from string format (YYYY-MM-DD)
200+ DateTime ? startDate = null ;
201+ DateTime ? endDate = null ;
202+
203+ if ( ! string . IsNullOrWhiteSpace ( subTaskDto . StartDate ) )
204+ {
205+ if ( DateTime . TryParse ( subTaskDto . StartDate , out var parsedStart ) )
206+ startDate = DateTime . SpecifyKind ( parsedStart , DateTimeKind . Utc ) ;
207+ }
208+
209+ if ( ! string . IsNullOrWhiteSpace ( subTaskDto . EndDate ) )
210+ {
211+ if ( DateTime . TryParse ( subTaskDto . EndDate , out var parsedEnd ) )
212+ endDate = DateTime . SpecifyKind ( parsedEnd , DateTimeKind . Utc ) ;
213+ }
214+
168215 var subTask = new SubTaskModel
169216 {
170217 Id = ObjectId . GenerateNewId ( ) . ToString ( ) ,
@@ -177,8 +224,8 @@ public async Task<IActionResult> Create([FromBody] CreateSubTaskDto subTaskDto)
177224 Category = subTaskDto . Category ,
178225 CreatedBy = subTaskDto . CreatedBy ,
179226 AssignedTo = subTaskDto . AssignedTo ?? new List < string > ( ) ,
180- StartDate = subTaskDto . StartDate ,
181- EndDate = subTaskDto . EndDate ,
227+ StartDate = startDate ,
228+ EndDate = endDate ,
182229 CreatedAt = DateTime . UtcNow
183230 } ;
184231
@@ -232,6 +279,22 @@ public async Task<IActionResult> Update(string id, [FromBody] UpdateSubTaskDto s
232279 if ( existingSubTask == null )
233280 return NotFound ( new { message = "SubTask not found." } ) ;
234281
282+ // Parse dates from string format (YYYY-MM-DD)
283+ DateTime ? startDate = null ;
284+ DateTime ? endDate = null ;
285+
286+ if ( ! string . IsNullOrWhiteSpace ( subTaskDto . StartDate ) )
287+ {
288+ if ( DateTime . TryParse ( subTaskDto . StartDate , out var parsedStart ) )
289+ startDate = DateTime . SpecifyKind ( parsedStart , DateTimeKind . Utc ) ;
290+ }
291+
292+ if ( ! string . IsNullOrWhiteSpace ( subTaskDto . EndDate ) )
293+ {
294+ if ( DateTime . TryParse ( subTaskDto . EndDate , out var parsedEnd ) )
295+ endDate = DateTime . SpecifyKind ( parsedEnd , DateTimeKind . Utc ) ;
296+ }
297+
235298 var updatedSubTask = new SubTaskModel
236299 {
237300 Id = id ,
@@ -244,8 +307,8 @@ public async Task<IActionResult> Update(string id, [FromBody] UpdateSubTaskDto s
244307 Category = subTaskDto . Category ,
245308 CreatedBy = subTaskDto . CreatedBy ,
246309 AssignedTo = subTaskDto . AssignedTo ?? new List < string > ( ) ,
247- StartDate = subTaskDto . StartDate ,
248- EndDate = subTaskDto . EndDate ,
310+ StartDate = startDate ,
311+ EndDate = endDate ,
249312 CreatedAt = existingSubTask . CreatedAt ,
250313 Comments = existingSubTask . Comments
251314 } ;
@@ -333,8 +396,22 @@ public async Task<IActionResult> Patch(string id, [FromBody] Dictionary<string,
333396 updateDefs . Add ( Builders < SubTaskModel > . Update . Set ( st => st . Priority , value ? . ToString ( ) ) ) ;
334397 break ;
335398 case "categoryid" :
336- updateDefs . Add ( Builders < SubTaskModel > . Update . Set ( st => st . CategoryId , value ? . ToString ( ) ) ) ;
337- break ;
399+ {
400+ var categoryIdValue = value ? . ToString ( ) ;
401+ // Treat "uncategorized" (or empty) as clearing the category
402+ if ( string . IsNullOrWhiteSpace ( categoryIdValue ) || categoryIdValue . Equals ( "uncategorized" , StringComparison . OrdinalIgnoreCase ) )
403+ {
404+ updateDefs . Add ( Builders < SubTaskModel > . Update . Set ( st => st . CategoryId , null ) ) ;
405+ updateDefs . Add ( Builders < SubTaskModel > . Update . Set ( st => st . Category , null ) ) ;
406+ }
407+ else
408+ {
409+ if ( ! ObjectId . TryParse ( categoryIdValue , out _ ) )
410+ return BadRequest ( new { message = "CategoryId must be a valid ObjectId or 'uncategorized'." } ) ;
411+ updateDefs . Add ( Builders < SubTaskModel > . Update . Set ( st => st . CategoryId , categoryIdValue ) ) ;
412+ }
413+ break ;
414+ }
338415 case "assignedto" :
339416 if ( value is JsonElement element && element . ValueKind == JsonValueKind . Array )
340417 {
@@ -465,8 +542,8 @@ public class CreateSubTaskDto
465542 public string ? CategoryId { get ; set ; }
466543 public string ? CreatedBy { get ; set ; }
467544 public List < string > ? AssignedTo { get ; set ; }
468- public DateTime ? StartDate { get ; set ; }
469- public DateTime ? EndDate { get ; set ; }
545+ public string ? StartDate { get ; set ; }
546+ public string ? EndDate { get ; set ; }
470547 }
471548
472549 // DTO for updating subtasks
@@ -481,8 +558,8 @@ public class UpdateSubTaskDto
481558 public string ? CategoryId { get ; set ; }
482559 public string ? CreatedBy { get ; set ; }
483560 public List < string > ? AssignedTo { get ; set ; }
484- public DateTime ? StartDate { get ; set ; }
485- public DateTime ? EndDate { get ; set ; }
561+ public string ? StartDate { get ; set ; }
562+ public string ? EndDate { get ; set ; }
486563 }
487564 }
488565}
0 commit comments