Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a56aaee
refactor: move work entry configuration to mapping folder
MDI74 Feb 16, 2026
a41a0d6
docs(readme): bring test coverage score up to date
Feb 16, 2026
74bc1e7
refactor: #27: add TrackingEntryBase with shared fields and set type …
MDI74 Feb 16, 2026
842b197
Merge branch 'feature/#27-add-unwell-type' of github.com:TourmalineCo…
MDI74 Feb 16, 2026
58fdf34
docs(readme): bring test coverage score up to date
Feb 16, 2026
07427d3
feat: #27: add unwell type
MDI74 Feb 17, 2026
4e6ad8a
feat: #27: add create, update unwell entries and update getWorkEntrie…
MDI74 Feb 17, 2026
a061f1a
refactor: #27: rename EventType to EntryType
katariniss Feb 17, 2026
f41df1a
refactor: #27: rename WorkEntry to TaskEntry & TrackingEntry to Track…
katariniss Feb 17, 2026
7ecd9c9
refactor: #27: change GetWorkEntriesByPeriodResponse
katariniss Feb 17, 2026
57783e7
refactor: #27: rename TrackingEntryBaseMapping to TrackedEntryBaseMap…
MDI74 Feb 17, 2026
9e3aac3
refactor: #27: rename test name
katariniss Feb 17, 2026
df45e9b
Merge branch 'feature/#27-add-unwell-type' of github.com:TourmalineCo…
katariniss Feb 17, 2026
ccd381e
refactor: #27: rename WorkEntries to TaskEntries
MDI74 Feb 17, 2026
179c7bd
feat: #27: add AddTrackedEntryBaseAndUnwellEntry and RenameWorkEntryT…
MDI74 Feb 17, 2026
4ab6d31
docs(readme): Update Mermaid DB Schema Diagram in README.md (siren-gen)
Feb 17, 2026
8e85812
docs(readme): bring test coverage score up to date
Feb 17, 2026
0fc5efe
refactor: #27: remove test which checks ck_work_entries_type_not_zero…
MDI74 Feb 18, 2026
42f3c3b
test: #27: add autorization tests for unwell entries endpoints
MDI74 Feb 18, 2026
0c85b74
refactor: #27: change feature name in tracked-entries-happy-path test
MDI74 Feb 18, 2026
0a3ae47
fix: #27: fix unwell-entries-no-permissions-lead-to-unauthorized test
MDI74 Feb 18, 2026
ff456f0
refactor: #27: rename createWorkEntryCommand to _createUnwellEntryCom…
MDI74 Feb 18, 2026
e59691f
refactor: #27: remove redundant empty lines
MDI74 Feb 18, 2026
6023b60
refactor: #27: rename updateWorkEntryCommandParams to updateUnwellEnt…
MDI74 Feb 18, 2026
2bb2167
Apply suggestion from @fpandyz
fpandyz Feb 18, 2026
a0370d0
refactor: #27: return title to the previous place
MDI74 Feb 18, 2026
67ec85b
Merge branch 'master' into feature/#27-add-unwell-type
MDI74 Feb 18, 2026
d98040c
refactor: #27: rename CreateUnwellEntryHandler to CreateUnwellEntryAsync
MDI74 Feb 18, 2026
f2c3403
Merge branch 'feature/#27-add-unwell-type' of github.com:TourmalineCo…
MDI74 Feb 18, 2026
7ab9472
docs(readme): bring test coverage score up to date
Feb 18, 2026
0dd1ed4
refactor: #27: rename unwell-entries-no-permissions-lead-to-unauthorized
MDI74 Feb 20, 2026
e1e45dc
refactor: #27: remove a redundant test that checks the same thing as …
Feb 20, 2026
29e5fb7
Merge branch 'master' into feature/#27-add-unwell-type
MDI74 Feb 20, 2026
b2531cf
docs(readme): bring test coverage score up to date
Feb 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Api/DependencyInjection.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Api.ExternalDeps.AssignmentsApi;
using Api.Features.Tracking.CreateUnwellEntry;
using Api.Features.Tracking.CreateWorkEntry;
using Api.Features.Tracking.GetWorkEntriesByPeriod;
using Api.Features.Tracking.UpdateUnwellEntry;
using Api.Features.Tracking.UpdateWorkEntry;
using Application;
using Application.Commands;
Expand Down Expand Up @@ -32,10 +34,14 @@ public static void AddApplication(this IServiceCollection services, IConfigurati
services.AddTransient<IAssignmentsApi, AssignmentsApi>();
services.AddTransient<CreateWorkEntryHandler>();
services.AddTransient<CreateWorkEntryCommand>();
services.AddTransient<CreateUnwellEntryHandler>();
services.AddTransient<CreateUnwellEntryCommand>();
services.AddTransient<GetWorkEntriesByPeriodHandler>();
services.AddTransient<GetWorkEntriesByPeriodQuery>();
services.AddTransient<UpdateWorkEntryHandler>();
services.AddTransient<UpdateWorkEntryCommand>();
services.AddTransient<UpdateUnwellEntryHandler>();
services.AddTransient<UpdateUnwellEntryCommand>();
services.AddTransient<HardDeleteEntityCommand>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Application.Commands;

namespace Api.Features.Tracking.CreateUnwellEntry;

public class CreateUnwellEntryHandler
{
private readonly CreateUnwellEntryCommand _createUnwellEntryCommand;


public CreateUnwellEntryHandler(
CreateUnwellEntryCommand createUnwellEntryCommand
)
{
_createUnwellEntryCommand = createUnwellEntryCommand;
}

public async Task<CreateUnwellResponse> HandleAsync(
CreateUnwellEntryRequest createUnwellEntryRequest
)
{
var createUnwellEntryCommandParams = new CreateUnwellEntryCommandParams
{
StartTime = createUnwellEntryRequest.StartTime,
EndTime = createUnwellEntryRequest.EndTime,
Comment thread
fpandyz marked this conversation as resolved.
};

var newUnwellEntryId = await _createUnwellEntryCommand.ExecuteAsync(createUnwellEntryCommandParams);

return new CreateUnwellResponse
{
NewUnwellEntryId = newUnwellEntryId
Comment thread
fpandyz marked this conversation as resolved.
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;

namespace Api.Features.Tracking.CreateUnwellEntry;

public class CreateUnwellEntryRequest
{
[Required]
public required DateTime StartTime { get; set; }

[Required]
public required DateTime EndTime { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Api.Features.Tracking.CreateUnwellEntry;

public class CreateUnwellResponse
{
public required long NewUnwellEntryId { get; set; }
}

Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Application.Commands;
using Application.ExternalDeps.AssignmentsApi;
using Core.Entities;

namespace Api.Features.Tracking.CreateWorkEntry;

Expand Down Expand Up @@ -33,7 +32,6 @@ CreateWorkEntryRequest createWorkEntryRequest
ProjectId = project.Id,
TaskId = createWorkEntryRequest.TaskId,
Description = createWorkEntryRequest.Description,
Type = EventType.Task, // TODO: after add other event types remove hardcode
};

var newWorkEntryId = await _createWorkEntryCommand.ExecuteAsync(createWorkEntryCommandParams);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Application.Queries;
using Core.Entities;

namespace Api.Features.Tracking.GetWorkEntriesByPeriod;

Expand All @@ -18,27 +19,43 @@ public async Task<GetWorkEntriesByPeriodResponse> HandleAsync(
DateOnly endDate
)
{
var workEntriesByPeriod = await _getWorkEntriesByPeriodQuery.GetByPeriodAsync(
var workEntriesByPeriod = await _getWorkEntriesByPeriodQuery.GetByPeriodAsync<TrackedEntryBase>(
startDate,
endDate
);

var workEntries = workEntriesByPeriod
.OfType<TaskEntry>()
Comment thread
MDI74 marked this conversation as resolved.
.Select(
x => new WorkEntryDto
{
Id = x.Id,
StartTime = x.StartTime,
EndTime = x.EndTime,
Type = x.Type,
Title = x.Title,
ProjectId = x.ProjectId,
TaskId = x.TaskId,
Description = x.Description
})
.ToList();

var unwellEntries = workEntriesByPeriod
.OfType<UnwellEntry>()
.Select(
x => new UnwellEntryDto
{
Id = x.Id,
StartTime = x.StartTime,
EndTime = x.EndTime,
Type = x.Type,
})
.ToList();

return new GetWorkEntriesByPeriodResponse
{
WorkEntries = workEntriesByPeriod
.Select(workEntry =>
new WorkEntryDto
{
Id = workEntry.Id,
Title = workEntry.Title,
StartTime = workEntry.StartTime,
EndTime = workEntry.EndTime,
ProjectId = workEntry.ProjectId,
TaskId = workEntry.TaskId,
Description = workEntry.Description,
}
)
.ToList()
WorkEntries = workEntries,
UnwellEntries = unwellEntries
};
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
using Core.Entities;

namespace Api.Features.Tracking.GetWorkEntriesByPeriod;

public class GetWorkEntriesByPeriodResponse
{
public required List<WorkEntryDto> WorkEntries { get; set; }

public required List<UnwellEntryDto> UnwellEntries { get; set; }
Comment thread
fpandyz marked this conversation as resolved.
}

public class WorkEntryDto
Expand All @@ -15,9 +19,22 @@ public class WorkEntryDto

public required DateTime EndTime { get; set; }

public required EntryType Type { get; set; }

public required long ProjectId { get; set; }

public required string TaskId { get; set; }

public required string Description { get; set; }
}

public class UnwellEntryDto
{
public required long Id { get; set; }

public required DateTime StartTime { get; set; }

public required DateTime EndTime { get; set; }

public required EntryType Type { get; set; }
}
39 changes: 32 additions & 7 deletions Api/Features/Tracking/TrackingController.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.ComponentModel.DataAnnotations;
using Api.Features.Tracking.CreateUnwellEntry;
using Api.Features.Tracking.CreateWorkEntry;
using Api.Features.Tracking.GetWorkEntriesByPeriod;
using Api.Features.Tracking.UpdateUnwellEntry;
using Api.Features.Tracking.UpdateWorkEntry;
using Application.Commands;
using Application.ExternalDeps.AssignmentsApi;
Expand All @@ -13,12 +15,12 @@ namespace Api.Features.Tracking;

[Authorize]
[ApiController]
[Route("api/time/tracking/work-entries")]
[Route("api/time/tracking")]
public class TrackingController : ControllerBase
{
[EndpointSummary("Get work entries by period")]
[RequiresPermission(UserClaimsProvider.CanManagePersonalTimeTracker)]
[HttpGet]
[HttpGet("work-entries")]
public Task<GetWorkEntriesByPeriodResponse> GetWorkEntriesByPeriodAsync(
[Required][FromQuery] DateOnly startDate,
[Required][FromQuery] DateOnly endDate,
Expand All @@ -30,7 +32,7 @@ [FromServices] GetWorkEntriesByPeriodHandler getWorkEntriesByPeriodHandler

[EndpointSummary("Create a work entry")]
[RequiresPermission(UserClaimsProvider.CanManagePersonalTimeTracker)]
[HttpPost]
[HttpPost("work-entries")]
public Task<CreateWorkEntryResponse> CreateWorkEntryAsync(
[Required][FromBody] CreateWorkEntryRequest createWorkEntryRequest,
[FromServices] CreateWorkEntryHandler createWorkEntryHandler
Expand All @@ -39,9 +41,20 @@ [FromServices] CreateWorkEntryHandler createWorkEntryHandler
return createWorkEntryHandler.HandleAsync(createWorkEntryRequest);
}

[EndpointSummary("Create an unwell entry")]
[RequiresPermission(UserClaimsProvider.CanManagePersonalTimeTracker)]
[HttpPost("unwell-entries")]
public Task<CreateUnwellResponse> CreateUnwellEntryAsync(
[Required][FromBody] CreateUnwellEntryRequest createUnwellRequest,
[FromServices] CreateUnwellEntryHandler createUnwellEntryHandler
)
{
return createUnwellEntryHandler.HandleAsync(createUnwellRequest);
}

[EndpointSummary("Update a work entry")]
[RequiresPermission(UserClaimsProvider.CanManagePersonalTimeTracker)]
[HttpPost("{workEntryId}")]
[HttpPost("work-entries/{workEntryId}")]
public Task UpdateWorkEntryAsync(
[Required][FromRoute] long workEntryId,
[Required][FromBody] UpdateWorkEntryRequest updateWorkEntryRequest,
Expand All @@ -51,9 +64,21 @@ [FromServices] UpdateWorkEntryHandler updateWorkEntryHandler
return updateWorkEntryHandler.HandleAsync(workEntryId, updateWorkEntryRequest);
}

[EndpointSummary("Update an unwell entry")]
[RequiresPermission(UserClaimsProvider.CanManagePersonalTimeTracker)]
[HttpPost("unwell-entries/{unwellEntryId}")]
public Task UpdateUnwellEntryAsync(
[Required][FromRoute] long unwellEntryId,
[Required][FromBody] UpdateUnwellEntryRequest updateUnwellEntryRequest,
[FromServices] UpdateUnwellEntryHandler updateUnwellEntryHandler
)
{
return updateUnwellEntryHandler.HandleAsync(unwellEntryId, updateUnwellEntryRequest);
}

[EndpointSummary("Get employee projects by period")]
[RequiresPermission(UserClaimsProvider.CanManagePersonalTimeTracker)]
[HttpGet("projects")]
[HttpGet("work-entries/projects")]
public async Task<ProjectsResponse> GetEmployeeProjectsByPeriodAsync(
[Required][FromQuery] DateOnly startDate,
[Required][FromQuery] DateOnly endDate,
Expand All @@ -68,15 +93,15 @@ [FromServices] IAssignmentsApi assignmentsApi

[EndpointSummary("Deletes specific work entry")]
[RequiresPermission(UserClaimsProvider.AUTO_TESTS_ONLY_IsWorkEntriesHardDeleteAllowed)]
[HttpDelete("{workEntryId}/hard-delete")]
[HttpDelete("work-entries/{workEntryId}/hard-delete")]
Comment thread
fpandyz marked this conversation as resolved.
public async Task<object> HardDeleteWorkEntryAsync(
[Required][FromRoute] long workEntryId,
[FromServices] HardDeleteEntityCommand hardDeleteEntityCommand
)
{
return new
{
isDeleted = await hardDeleteEntityCommand.ExecuteAsync<WorkEntry>(workEntryId)
isDeleted = await hardDeleteEntityCommand.ExecuteAsync<TrackedEntryBase>(workEntryId)
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Application.Commands;

namespace Api.Features.Tracking.UpdateUnwellEntry;

public class UpdateUnwellEntryHandler
{
private readonly UpdateUnwellEntryCommand _updateUnwellEntryCommand;

public UpdateUnwellEntryHandler(
UpdateUnwellEntryCommand updateUnwellEntryCommand
)
{
_updateUnwellEntryCommand = updateUnwellEntryCommand;
}

public async Task HandleAsync(
long unwellEntryId,
UpdateUnwellEntryRequest updateUnwellEntryRequest
)
{
var updateUnwellEntryCommandParams = new UpdateUnwellEntryCommandParams
{
Id = unwellEntryId,
StartTime = updateUnwellEntryRequest.StartTime,
EndTime = updateUnwellEntryRequest.EndTime,
};

await _updateUnwellEntryCommand.ExecuteAsync(updateUnwellEntryCommandParams);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;

namespace Api.Features.Tracking.UpdateUnwellEntry;

public class UpdateUnwellEntryRequest
{
[Required]
public required DateTime StartTime { get; set; }

[Required]
public required DateTime EndTime { get; set; }
}
16 changes: 7 additions & 9 deletions Api/Features/Tracking/UpdateWorkEntry/UpdateWorkEntryHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using Application.Commands;
using Application.ExternalDeps.AssignmentsApi;
using Core.Entities;

namespace Api.Features.Tracking.UpdateWorkEntry;

Expand All @@ -20,21 +19,20 @@ IAssignmentsApi assignmentsApi

public async Task HandleAsync(
long workEntryId,
UpdateWorkEntryRequest updateEntryRequest
UpdateWorkEntryRequest updateWorkEntryRequest
)
{
var project = await _assignmentsApi.GetEmployeeProjectAsync(updateEntryRequest.ProjectId);
var project = await _assignmentsApi.GetEmployeeProjectAsync(updateWorkEntryRequest.ProjectId);

var updateWorkEntryCommandParams = new UpdateWorkEntryCommandParams
{
Id = workEntryId,
Title = updateEntryRequest.Title,
StartTime = updateEntryRequest.StartTime,
EndTime = updateEntryRequest.EndTime,
Title = updateWorkEntryRequest.Title,
StartTime = updateWorkEntryRequest.StartTime,
EndTime = updateWorkEntryRequest.EndTime,
ProjectId = project.Id,
TaskId = updateEntryRequest.TaskId,
Description = updateEntryRequest.Description,
Type = EventType.Task, // TODO: after add other event types remove hardcode
TaskId = updateWorkEntryRequest.TaskId,
Comment thread
fpandyz marked this conversation as resolved.
Description = updateWorkEntryRequest.Description,
};

await _updateWorkEntryCommand.ExecuteAsync(updateWorkEntryCommandParams);
Expand Down
Loading