feat: add EstimatedMinutes field and general PUT update endpoint for tasks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-26 22:10:48 -05:00
parent e12f78c479
commit 58d57509e5
7 changed files with 321 additions and 0 deletions

View File

@@ -51,6 +51,7 @@ public class TasksController(ITaskRepository taskRepo, ILogger<TasksController>
Description = request.Description,
Category = request.Category,
ParentTaskId = request.ParentTaskId,
EstimatedMinutes = request.EstimatedMinutes,
Status = WorkTaskStatus.Pending
};
@@ -161,6 +162,22 @@ public class TasksController(ITaskRepository taskRepo, ILogger<TasksController>
return Ok(ApiResponse<WorkTask>.Ok(task));
}
[HttpPut("{id:int}")]
public async Task<IActionResult> Update(int id, [FromBody] UpdateTaskRequest request)
{
var task = await taskRepo.GetByIdAsync(id);
if (task is null)
return NotFound(ApiResponse.Fail("Task not found"));
if (request.Title is not null) task.Title = request.Title;
if (request.Description is not null) task.Description = request.Description;
if (request.Category is not null) task.Category = request.Category;
if (request.EstimatedMinutes.HasValue) task.EstimatedMinutes = request.EstimatedMinutes;
await taskRepo.UpdateAsync(task);
return Ok(ApiResponse<WorkTask>.Ok(task));
}
[HttpDelete("{id:int}")]
public async Task<IActionResult> Delete(int id)
{

View File

@@ -6,4 +6,5 @@ public class CreateTaskRequest
public string? Description { get; set; }
public string? Category { get; set; }
public int? ParentTaskId { get; set; }
public int? EstimatedMinutes { get; set; }
}

View File

@@ -0,0 +1,9 @@
namespace TaskTracker.Core.DTOs;
public class UpdateTaskRequest
{
public string? Title { get; set; }
public string? Description { get; set; }
public string? Category { get; set; }
public int? EstimatedMinutes { get; set; }
}

View File

@@ -12,6 +12,7 @@ public class WorkTask
public DateTime CreatedAt { get; set; }
public DateTime? StartedAt { get; set; }
public DateTime? CompletedAt { get; set; }
public int? EstimatedMinutes { get; set; }
public int? ParentTaskId { get; set; }
public WorkTask? ParentTask { get; set; }

View File

@@ -0,0 +1,262 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using TaskTracker.Infrastructure.Data;
#nullable disable
namespace TaskTracker.Infrastructure.Migrations
{
[DbContext(typeof(TaskTrackerDbContext))]
[Migration("20260227031015_AddEstimatedMinutes")]
partial class AddEstimatedMinutes
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "10.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("TaskTracker.Core.Entities.AppMapping", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Category")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("FriendlyName")
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("MatchType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("Pattern")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.HasKey("Id");
b.ToTable("AppMappings");
b.HasData(
new
{
Id = 1,
Category = "Engineering",
FriendlyName = "SolidWorks",
MatchType = "ProcessName",
Pattern = "SLDWORKS"
},
new
{
Id = 2,
Category = "Email",
FriendlyName = "Outlook",
MatchType = "ProcessName",
Pattern = "OUTLOOK"
},
new
{
Id = 3,
Category = "General",
FriendlyName = "Notepad",
MatchType = "ProcessName",
Pattern = "notepad"
},
new
{
Id = 4,
Category = "LaserCutting",
FriendlyName = "PEP System",
MatchType = "UrlContains",
Pattern = "pep"
},
new
{
Id = 5,
Category = "Engineering",
FriendlyName = "SolidWorks",
MatchType = "TitleContains",
Pattern = "solidworks"
});
});
modelBuilder.Entity("TaskTracker.Core.Entities.ContextEvent", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("AppName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("Source")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<DateTime>("Timestamp")
.HasColumnType("datetime2");
b.Property<string>("Url")
.HasMaxLength(2000)
.HasColumnType("nvarchar(2000)");
b.Property<string>("WindowTitle")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("nvarchar(1000)");
b.Property<int?>("WorkTaskId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("Timestamp");
b.HasIndex("WorkTaskId");
b.ToTable("ContextEvents");
});
modelBuilder.Entity("TaskTracker.Core.Entities.TaskNote", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Content")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<int>("WorkTaskId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("WorkTaskId");
b.ToTable("Notes");
});
modelBuilder.Entity("TaskTracker.Core.Entities.WorkTask", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Category")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<DateTime?>("CompletedAt")
.HasColumnType("datetime2");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime2");
b.Property<string>("Description")
.HasColumnType("nvarchar(max)");
b.Property<int?>("EstimatedMinutes")
.HasColumnType("int");
b.Property<int?>("ParentTaskId")
.HasColumnType("int");
b.Property<DateTime?>("StartedAt")
.HasColumnType("datetime2");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(500)
.HasColumnType("nvarchar(500)");
b.HasKey("Id");
b.HasIndex("ParentTaskId");
b.ToTable("Tasks");
});
modelBuilder.Entity("TaskTracker.Core.Entities.ContextEvent", b =>
{
b.HasOne("TaskTracker.Core.Entities.WorkTask", "WorkTask")
.WithMany("ContextEvents")
.HasForeignKey("WorkTaskId");
b.Navigation("WorkTask");
});
modelBuilder.Entity("TaskTracker.Core.Entities.TaskNote", b =>
{
b.HasOne("TaskTracker.Core.Entities.WorkTask", "WorkTask")
.WithMany("Notes")
.HasForeignKey("WorkTaskId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("WorkTask");
});
modelBuilder.Entity("TaskTracker.Core.Entities.WorkTask", b =>
{
b.HasOne("TaskTracker.Core.Entities.WorkTask", "ParentTask")
.WithMany("SubTasks")
.HasForeignKey("ParentTaskId")
.OnDelete(DeleteBehavior.Restrict);
b.Navigation("ParentTask");
});
modelBuilder.Entity("TaskTracker.Core.Entities.WorkTask", b =>
{
b.Navigation("ContextEvents");
b.Navigation("Notes");
b.Navigation("SubTasks");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace TaskTracker.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class AddEstimatedMinutes : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "EstimatedMinutes",
table: "Tasks",
type: "int",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "EstimatedMinutes",
table: "Tasks");
}
}
}

View File

@@ -189,6 +189,9 @@ namespace TaskTracker.Infrastructure.Migrations
b.Property<string>("Description")
.HasColumnType("nvarchar(max)");
b.Property<int?>("EstimatedMinutes")
.HasColumnType("int");
b.Property<int?>("ParentTaskId")
.HasColumnType("int");