Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cfodev 994 -- added PRI tab #443

Merged
merged 6 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
29 changes: 29 additions & 0 deletions src/Application/Features/PRIs/Commands/AddPRI.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Cfo.Cats.Application.Common.Security;
using Cfo.Cats.Application.Common.Validators;
using Cfo.Cats.Application.Features.Locations.DTOs;
using Cfo.Cats.Application.Features.PRIs.DTOs;
using Cfo.Cats.Application.SecurityConstants;
using Cfo.Cats.Domain.Entities.PRIs;

Expand Down Expand Up @@ -156,6 +157,18 @@ public Validator()
.WithMessage("Expected date of release must not be more than three months in the future");
}
}
private class Mapper : Profile
{
public Mapper()
{
CreateMap<PRIDto, PriReleaseDto>(MemberList.None)
.ForMember(target => target.ExpectedRegion,
options => options.MapFrom(source => source.ExpectedReleaseRegion))
.ForMember(target => target.ExpectedOn,
options => options.MapFrom(source => source.ExpectedReleaseDate.ToDateTime(TimeOnly.MinValue)))
;
}
}
}

public class PriMeetingDto
Expand Down Expand Up @@ -208,5 +221,21 @@ public Validator()
.WithMessage("You must provide a reason");
}
}
private class Mapper : Profile
{
public Mapper()
{
CreateMap<PRIDto, PriMeetingDto>(MemberList.None)
.ForMember(target => target.AttendedOn,
options => options.MapFrom(source => source.MeetingAttendedOn.ToDateTime(TimeOnly.MinValue)))
.ForMember(target => target.CustodyAttendedInPerson,
options => options.MapFrom(source => (source.CustodyAttendedInPerson ? ConfirmationStatus.Yes : ConfirmationStatus.No)))
.ForMember(target => target.CommunityAttendedInPerson,
options => options.MapFrom(source => (source.CommunityAttendedInPerson ? ConfirmationStatus.Yes : ConfirmationStatus.No)))
.ForMember(target => target.ParticipantAttendedInPerson,
options => options.MapFrom(source => (source.ParticipantAttendedInPerson ? ConfirmationStatus.Yes : ConfirmationStatus.No)));

}
}
}
}
30 changes: 16 additions & 14 deletions src/Application/Features/PRIs/DTOs/PRIDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,38 @@ public class PRIDto
[Description("Participant Id")]
public required string ParticipantId { get; set; }

[Description("Expected Date Of Release")]
public required DateOnly ExpectedReleaseDate { get; set; }

[Description("Actual Date Of Release")]
public DateOnly? ActualReleaseDate { get; set; }

public DateTime? AcceptedOn { get; private set; }

[Description("Expected Date Of Release")]
public required DateOnly ExpectedReleaseDate { get; set; }
public DateTime? AcceptedOn { get; set; }

public required LocationDto ExpectedReleaseRegion { get; set; }
public required LocationDto CustodyLocation { get; set; }

public string? AssignedTo { get; private set; }
public string? AssignedTo { get; set; }

public bool IsCompleted { get; }
public DateOnly MeetingAttendedOn { get; set; }
public string? ReasonParticipantDidNotAttendInPerson { get; set; }
public string? ReasonCommunityDidNotAttendInPerson { get; set; }
public string? ReasonCustodyDidNotAttendInPerson { get; set; }
public bool CustodyAttendedInPerson => string.IsNullOrEmpty(ReasonCustodyDidNotAttendInPerson);
public bool CommunityAttendedInPerson => string.IsNullOrEmpty(ReasonCommunityDidNotAttendInPerson);
public bool ParticipantAttendedInPerson => string.IsNullOrEmpty(ReasonParticipantDidNotAttendInPerson);

private class Mapper : Profile
{
public Mapper()
{
CreateMap<Domain.Entities.PRIs.PRI, PRIDto>(MemberList.None)
.ForMember(target => target.Id, options => options.MapFrom(source => source.Id))
.ForMember(target => target.ParticipantId,
options => options.MapFrom(source => source.ParticipantId))
.ForMember(target => target.ActualReleaseDate,
options => options.MapFrom(source => source.ActualReleaseDate))
.ForMember(target => target.ExpectedReleaseDate,
options => options.MapFrom(source => source.ExpectedReleaseDate))
.ForMember(target => target.ExpectedReleaseRegion,
options => options.MapFrom(source => source.ExpectedReleaseRegion))
.ForMember(target => target.IsCompleted,
options => options.MapFrom(source => source.IsCompleted));
.ForMember(target => target.CustodyLocation,
options => options.MapFrom(source => source.CustodyLocation))
;
}
}
}
50 changes: 50 additions & 0 deletions src/Application/Features/PRIs/Queries/GetParticipantPRI.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using Cfo.Cats.Application.Common.Security;
using Cfo.Cats.Application.Common.Validators;
using Cfo.Cats.Application.Features.Participants.DTOs;
using Cfo.Cats.Application.Features.PRIs.DTOs;
using Cfo.Cats.Application.SecurityConstants;
using Cfo.Cats.Domain.Entities.Participants;
using static Cfo.Cats.Application.Features.PRIs.Commands.AddPRI;
namespace Cfo.Cats.Application.Features.PRIs.Queries;

public static class GetParticipantPRI
{
[RequestAuthorize(Policy = SecurityPolicies.AuthorizedUser)]
public class Query : IRequest<Result<PRIDto>>
{
public required string ParticipantId { get; set; }
}
class Handler(IUnitOfWork unitOfWork, IMapper mapper) : IRequestHandler<Query, Result<PRIDto>>
{
public async Task<Result<PRIDto>> Handle(Query request, CancellationToken cancellationToken)
{
await Task.CompletedTask;

var pri = await unitOfWork.DbContext.PRIs
.Include(x => x.ExpectedReleaseRegion)
.Include(x => x.CustodyLocation)
.Where(x => x.ParticipantId == request.ParticipantId)
.OrderByDescending(x => x.Created)
.FirstOrDefaultAsync(cancellationToken);

if (pri is null)
{
return Result<PRIDto>.Failure(["Pri not found."]);
}

return mapper.Map<PRIDto>(pri);
}
}
public class Validator : AbstractValidator<Query>
{
public Validator()
{
RuleFor(x => x.ParticipantId)
.NotEmpty()
.Length(9)
.Matches(ValidationConstants.AlphaNumeric)
.WithMessage(ValidationConstants.AlphaNumericMessage);

}
}
}
193 changes: 193 additions & 0 deletions src/Server.UI/Pages/Participants/Components/CasePRI.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
@using Cfo.Cats.Domain.Common.Enums
@using Cfo.Cats.Infrastructure.Services.Identity
@using Humanizer
@using Cfo.Cats.Application.Features.PRIs.DTOs;
@using Cfo.Cats.Application.Features.PRIs.Queries;
@using static Cfo.Cats.Application.Features.PRIs.Commands.AddPRI

@inherits CatsComponentBase
@inject UserService userService;

<style>
.mud-list-item-child:hover,
.mud-list-item-child:focus {
background-color: initial !important;
}
</style>

@if (_notFound)
{
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Square="true" Class="my-2">No PRI found.</MudAlert>
}
@if (_model is not null)
{
<MudExpansionPanels MultiExpansion="true">

<MudExpansionPanel>
<TitleContent>
<div class="d-flex">
<MudIcon Icon="@Icons.Material.Filled.Map" Color="Color.Primary" class="mr-3"/>
<MudText>Release</MudText>
</div>
</TitleContent>
<ChildContent>
<MudForm Model="priRelease!" Validation="@(Validator.ValidateValue(priRelease!))" ReadOnly Disabled>
<MudSelect @bind-Value="priRelease!.CustodyLocation"
Label="Discharge Location"
For="() => priRelease!.CustodyLocation">
<MudSelectItem Value="@priRelease!.CustodyLocation">@priRelease!.CustodyLocation!.Name</MudSelectItem>
</MudSelect>
<MudSelect @bind-Value="priRelease!.ExpectedRegion"
Label="Expected release region"
For="() => priRelease!.ExpectedRegion">
<MudSelectItem Value="@priRelease!.ExpectedRegion">@priRelease!.ExpectedRegion!.Name</MudSelectItem>
</MudSelect>
<MudDatePicker @bind-Date="priRelease!.ExpectedOn"
For="() => priRelease!.ExpectedOn"
Label="Expected date of release"
MinDate="DateTime.Today"
MaxDate="DateTime.Today.AddMonths(3)"
Class="mt-2" />
</MudForm>
</ChildContent>
</MudExpansionPanel>

<MudExpansionPanel>
<TitleContent>
<div class="d-flex">
<MudIcon Icon="@Icons.Material.Filled.Person" Color="Color.Primary" class="mr-3"/>
<MudText>Assignment</MudText>
</div>
</TitleContent>
<ChildContent>
<MudForm ReadOnly Disabled>
<MudTextField Value="userService.GetDisplayName(_model!.AssignedTo!)"
For="() => _model!.AssignedTo"
Label="Community Support Worker"
MaxLength="6"
Class="mb-2" />
</MudForm>
</ChildContent>
</MudExpansionPanel>

<MudExpansionPanel>
<TitleContent>
<div class="d-flex">
<MudIcon Icon="@Icons.Material.Filled.MeetingRoom" Color="Color.Primary" class="mr-3"/>
<MudText>Pre-Release Meeting</MudText>
</div>
</TitleContent>
<ChildContent>
<MudForm Model="preMeeting" Validation="Validator.ValidateValue(preMeeting)" ReadOnly Disabled>
<MudText Class="mb-1" Typo="Typo.body2">Date pre-release meeting took place between the Custody Support Worker, the Community Support Worker (potentially yourself), and the Participant</MudText>
<MudDatePicker @bind-Date="preMeeting!.AttendedOn"
For="() => preMeeting!.AttendedOn"
Label="Date pre-release meeting took place"
Class="mb-6"/>

@* Custody *@
<MudText Typo="Typo.body1">Did the Custody Support Worker attend this meeting in person?</MudText>
<MudToggleGroup @bind-Value="preMeeting!.CustodyAttendedInPerson" SelectionMode="SelectionMode.SingleSelection" CheckMark>
<MudToggleItem Value="ConfirmationStatus.Yes">@ConfirmationStatus.Yes.Name</MudToggleItem>
<MudToggleItem Value="ConfirmationStatus.No">@ConfirmationStatus.No.Name</MudToggleItem>
</MudToggleGroup>
<MudTextField ReadOnly Underline="false" For="() => preMeeting!.CustodyAttendedInPerson" hidden />

@if (preMeeting!.CustodyAttendedInPerson == ConfirmationStatus.No)
{
<MudTextField @bind-Value="preMeeting!.ReasonCustodyDidNotAttendInPerson"
For="() => preMeeting!.ReasonCustodyDidNotAttendInPerson"
Label="Reason for not attending the meeting in person"
Lines="5"
Class="mt-2" />
}

<MudDivider Class="my-6" />

@* Community *@
<MudText Typo="Typo.body1">Did the Community Support Worker attend this meeting in person?</MudText>
<MudToggleGroup @bind-Value="preMeeting!.CommunityAttendedInPerson" SelectionMode="SelectionMode.SingleSelection" CheckMark>
<MudToggleItem Value="ConfirmationStatus.Yes">@ConfirmationStatus.Yes.Name</MudToggleItem>
<MudToggleItem Value="ConfirmationStatus.No">@ConfirmationStatus.No.Name</MudToggleItem>
</MudToggleGroup>
<MudTextField ReadOnly Underline="false" For="() => preMeeting!.CommunityAttendedInPerson" hidden />

@if (preMeeting!.CommunityAttendedInPerson == ConfirmationStatus.No)
{
<MudTextField @bind-Value="preMeeting!.ReasonCommunityDidNotAttendInPerson"
For="() => preMeeting!.ReasonCommunityDidNotAttendInPerson"
Label="Reason for not attending the meeting in person"
Lines="5"
Class="mt-2" />
}

<MudDivider Class="my-6" />

@* Participant *@
<MudText Typo="Typo.body1">Did the Participant attend this meeting in person?</MudText>
<MudToggleGroup @bind-Value="preMeeting!.ParticipantAttendedInPerson" SelectionMode="SelectionMode.SingleSelection" CheckMark>
<MudToggleItem Value="ConfirmationStatus.Yes">@ConfirmationStatus.Yes.Name</MudToggleItem>
<MudToggleItem Value="ConfirmationStatus.No">@ConfirmationStatus.No.Name</MudToggleItem>
</MudToggleGroup>
<MudTextField ReadOnly Underline="false" For="() => preMeeting!.ParticipantAttendedInPerson" hidden />

@if (preMeeting!.ParticipantAttendedInPerson == ConfirmationStatus.No)
{
<MudTextField @bind-Value="preMeeting!.ReasonParticipantDidNotAttendInPerson"
For="() => preMeeting!.ReasonParticipantDidNotAttendInPerson"
Label="Reason for not attending the meeting in person"
Lines="5"
Class="mt-2" />
}
</MudForm>
</ChildContent>
</MudExpansionPanel>

</MudExpansionPanels>

}


@code {
[Parameter]
[EditorRequired]
public string ParticipantId { get; set; } = default!;

private PRIDto? _model;
private PriReleaseDto? priRelease;
private PriMeetingDto? preMeeting;

private bool _notFound = false;

protected override async Task OnInitializedAsync()
{
_model = null;
try
{
var result = await GetNewMediator().Send(new GetParticipantPRI.Query()
{
ParticipantId = ParticipantId
});

if (result.Succeeded && result.Data is not null)
{
_model = result.Data;
priRelease = Mapper.Map<PriReleaseDto>(_model);
preMeeting = Mapper.Map<PriMeetingDto>(_model);
}
else
{
_notFound = true;
}
}
catch (NotFoundException)
{
_notFound = true;
}
finally
{
await base.OnInitializedAsync();
}

}
}
3 changes: 3 additions & 0 deletions src/Server.UI/Pages/Participants/Participant.razor
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@
<MudTabPanel Text="Assessments">
<CaseAssessment ParticipantId="@Id" />
</MudTabPanel>
<MudTabPanel Text="PRI">
<CasePRI ParticipantId="@Id" OnUpdate="Refresh" />
</MudTabPanel>
<MudTabPanel Text="Bio">
<CaseBio ParticipantId="@Id" />
</MudTabPanel>
Expand Down