diff --git a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CompetencyDataService.cs b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CompetencyDataService.cs index 701fccb03e..2b39a2e29a 100644 --- a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CompetencyDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CompetencyDataService.cs @@ -25,6 +25,8 @@ public partial class SelfAssessmentDataService sv.Verified, sv.Comments, sv.SignedOff, + sv.CandidateAssessmentSupervisorID, + sv.EmailSent, 0 AS UserIsVerifier, COALESCE (rr.LevelRAG, 0) AS ResultRAG FROM SelfAssessmentResults s @@ -229,13 +231,14 @@ public IEnumerable GetMostRecentResults(int selfAssessmentId, int ca return GroupCompetencyAssessmentQuestions(result); } - public IEnumerable GetCandidateAssessmentResultsById(int candidateAssessmentId, int adminId) + public IEnumerable GetCandidateAssessmentResultsById(int candidateAssessmentId, int adminId, int? selfAssessmentResultId = null) { + var resultIdFilter = selfAssessmentResultId.HasValue ? $"ResultID = {selfAssessmentResultId.Value}" : "1=1"; var result = connection.Query( $@"WITH {SpecificAssessmentResults} SELECT {CompetencyFields} FROM {SpecificCompetencyTables} - WHERE (CAOC.IncludedInSelfAssessment = 1) OR (SAS.Optional = 0) + WHERE (CAOC.IncludedInSelfAssessment = 1 OR SAS.Optional = 0) AND {resultIdFilter} ORDER BY SAS.Ordering, CAQ.Ordering", (competency, assessmentQuestion) => { @@ -269,6 +272,39 @@ int adminId return GroupCompetencyAssessmentQuestions(result); } + public IEnumerable GetResultSupervisorVerifications(int selfAssessmentId, int candidateId) + { + const string supervisorFields = @" + LAR.EmailSent, + LAR.Requested AS SupervisorVerificationRequested, + COALESCE(au.Forename + ' ' + au.Surname + (CASE WHEN au.Active = 1 THEN '' ELSE ' (Inactive)' END), sd.SupervisorEmail) AS SupervisorName, + SelfAssessmentResultSupervisorVerificationId AS SupervisorVerificationId, + CandidateAssessmentSupervisorID"; + const string supervisorTables = @" + LEFT OUTER JOIN CandidateAssessmentSupervisors AS cas ON cas.ID = CandidateAssessmentSupervisorID + LEFT OUTER JOIN SupervisorDelegates AS sd ON sd.ID = cas.SupervisorDelegateId AND sd.Removed IS NULL + LEFT OUTER JOIN AdminUsers AS au ON au.AdminID = sd.SupervisorAdminID"; + + var result = connection.Query( + $@"WITH {LatestAssessmentResults} + SELECT {supervisorFields}, {CompetencyFields} + FROM {CompetencyTables} + INNER JOIN SelfAssessments AS SA ON CA.SelfAssessmentID = SA.ID + {supervisorTables} + WHERE (LAR.Verified IS NULL)  + AND ((LAR.Result IS NOT NULL) OR (LAR.SupportingComments IS NOT NULL))  + AND (LAR.Requested IS NOT NULL) + ORDER BY SupervisorVerificationRequested DESC, C.Name", + (competency, assessmentQuestion) => + { + competency.AssessmentQuestions.Add(assessmentQuestion); + return competency; + }, + new { selfAssessmentId, candidateId } + ); + return result; + } + public IEnumerable GetCandidateAssessmentResultsToVerifyById(int selfAssessmentId, int candidateId) { var result = connection.Query( diff --git a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs index abc1eb8084..c97d72b83a 100644 --- a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs @@ -19,11 +19,12 @@ public interface ISelfAssessmentDataService IEnumerable GetMostRecentResults(int selfAssessmentId, int candidateId); - IEnumerable GetCandidateAssessmentResultsById(int candidateAssessmentId, int adminId); + IEnumerable GetCandidateAssessmentResultsById(int candidateAssessmentId, int adminId, int? selfAssessmentResultId = null); IEnumerable GetCandidateAssessmentResultsForReviewById(int candidateAssessmentId, int adminId); IEnumerable GetCandidateAssessmentResultsToVerifyById(int selfAssessmentId, int candidateId); + IEnumerable GetResultSupervisorVerifications(int selfAssessmentId, int candidateId); Competency? GetCompetencyByCandidateAssessmentResultId(int resultId, int candidateAssessmentId, int adminId); diff --git a/DigitalLearningSolutions.Data/Models/SelfAssessments/Competency.cs b/DigitalLearningSolutions.Data/Models/SelfAssessments/Competency.cs index 8812061421..ac1d5491bc 100644 --- a/DigitalLearningSolutions.Data/Models/SelfAssessments/Competency.cs +++ b/DigitalLearningSolutions.Data/Models/SelfAssessments/Competency.cs @@ -20,7 +20,12 @@ public class Competency public bool IncludedInSelfAssessment { get; set; } public DateTime? Verified { get; set; } public DateTime? Requested { get; set; } + public DateTime? EmailSent { get; set; } public bool? SignedOff { get; set; } + public DateTime? SupervisorVerificationRequested { get; set; } + public int? SupervisorVerificationId { get; set; } + public int? CandidateAssessmentSupervisorId { get; set; } + public string? SupervisorName { get; set; } public List AssessmentQuestions { get; set; } = new List(); public IEnumerable CompetencyFlags { get; set; } = new List(); } diff --git a/DigitalLearningSolutions.Data/Services/FrameworkNotificationService.cs b/DigitalLearningSolutions.Data/Services/FrameworkNotificationService.cs index 6580f32645..5b51a703b7 100644 --- a/DigitalLearningSolutions.Data/Services/FrameworkNotificationService.cs +++ b/DigitalLearningSolutions.Data/Services/FrameworkNotificationService.cs @@ -20,8 +20,8 @@ public interface IFrameworkNotificationService void SendSupervisorEnroledDelegate(int adminId, int supervisorDelegateId, int candidateAssessmentId, DateTime? completeByDate); void SendReminderDelegateSelfAssessment(int adminId, int supervisorDelegateId, int candidateAssessmentId); void SendSupervisorMultipleResultsReviewed(int adminId, int supervisorDelegateId, int candidateAssessmentId, int countResults); - void SendDelegateSupervisorNominated(int supervisorDelegateId, int selfAssessmentID, int delegateId); - void SendResultVerificationRequest(int candidateAssessmentSupervisorId, int selfAssessmentID, int resultCount, int delegateId); + void SendDelegateSupervisorNominated(int supervisorDelegateId, int selfAssessmentID, int delegateId, int? selfAssessmentResultId = null); + void SendResultVerificationRequest(int candidateAssessmentSupervisorId, int selfAssessmentID, int resultCount, int delegateId, int? selfAssessmentResultId = null); void SendSignOffRequest(int candidateAssessmentSupervisorId, int selfAssessmentID, int delegateId); void SendProfileAssessmentSignedOff(int supervisorDelegateId, int candidateAssessmentId, string? supervisorComments, bool signedOff, int adminId); } @@ -298,26 +298,17 @@ public void SendSupervisorMultipleResultsReviewed(int adminId, int supervisorDel emailService.SendEmail(new Email(emailSubjectLine, builder, supervisorDelegate.DelegateEmail)); } - public void SendDelegateSupervisorNominated(int supervisorDelegateId, int selfAssessmentID, int delegateId) + public void SendDelegateSupervisorNominated(int supervisorDelegateId, int selfAssessmentID, int delegateId, int? selfAssessmentResultId = null) { var supervisorDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, 0, delegateId); - if (supervisorDelegate == null) + if (supervisorDelegate == null || supervisorDelegate.CandidateID == null || supervisorDelegate.SupervisorAdminID == null) { return; } - if (supervisorDelegate.CandidateID == null) - { - return; - } - var delegateSelfAssessment = supervisorService.GetSelfAssessmentBySupervisorDelegateSelfAssessmentId(selfAssessmentID, (int)supervisorDelegate.ID); + var delegateSelfAssessment = supervisorService.GetSelfAssessmentBySupervisorDelegateSelfAssessmentId(selfAssessmentID, supervisorDelegate.ID); string emailSubjectLine = $"{delegateSelfAssessment.SupervisorRoleTitle} Role Request - Digital Learning Solutions"; var builder = new BodyBuilder(); - if (supervisorDelegate.SupervisorAdminID == null) - { - return; - } - - var profileReviewUrl = GetSupervisorProfileReviewUrl(supervisorDelegateId, delegateSelfAssessment.ID); + var profileReviewUrl = GetSupervisorProfileReviewUrl(supervisorDelegateId, delegateSelfAssessment.ID, selfAssessmentResultId); builder.TextBody = $@"Dear {supervisorDelegate.SupervisorName}, You have been identified by {supervisorDelegate.FirstName} {supervisorDelegate.LastName} ({supervisorDelegate.DelegateEmail}) as their {delegateSelfAssessment.SupervisorRoleTitle} for the activity '{delegateSelfAssessment.RoleName}' in the NHS Health Education England, Digital Learning Solutions (DLS) platform. To supervise this activity, please visit {profileReviewUrl} (sign in using your existing DLS credentials)."; @@ -325,14 +316,18 @@ public void SendDelegateSupervisorNominated(int supervisorDelegateId, int selfAs supervisorService.UpdateNotificationSent(supervisorDelegateId); emailService.SendEmail(new Email(emailSubjectLine, builder, supervisorDelegate.SupervisorEmail)); } - protected string GetSupervisorProfileReviewUrl(int supervisorDelegateId, int delegateSelfAssessmentId) + protected string GetSupervisorProfileReviewUrl(int supervisorDelegateId, int delegateSelfAssessmentId, int? selfAssessmentResultId = null) { var dlsUrlBuilder = GetDLSUriBuilder(); dlsUrlBuilder.Path += $"Supervisor/Staff/{supervisorDelegateId}/ProfileAssessment/{delegateSelfAssessmentId}/Review"; + if (selfAssessmentResultId.HasValue) + { + dlsUrlBuilder.Path += $"/{selfAssessmentResultId}"; + } return dlsUrlBuilder.Uri.ToString(); } - public void SendResultVerificationRequest(int candidateAssessmentSupervisorId, int selfAssessmentID, int resultCount, int delegateId) + public void SendResultVerificationRequest(int candidateAssessmentSupervisorId, int selfAssessmentID, int resultCount, int delegateId, int? selfAssessmentResultId) { var candidateAssessmentSupervisor = supervisorService.GetCandidateAssessmentSupervisorById(candidateAssessmentSupervisorId); int supervisorDelegateId = candidateAssessmentSupervisor.SupervisorDelegateId; @@ -340,7 +335,7 @@ public void SendResultVerificationRequest(int candidateAssessmentSupervisorId, i var supervisorDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, 0, delegateId); var delegateSelfAssessment = supervisorService.GetSelfAssessmentBaseByCandidateAssessmentId(candidateAssessmentSupervisor.CandidateAssessmentID); string emailSubjectLine = $"{delegateSelfAssessment.SupervisorRoleTitle} Self Assessment Results Review Request - Digital Learning Solutions"; - string? profileReviewUrl = GetSupervisorProfileReviewUrl(supervisorDelegateId, candidateAssessmentId); + string? profileReviewUrl = GetSupervisorProfileReviewUrl(supervisorDelegateId, candidateAssessmentId, selfAssessmentResultId); BodyBuilder? builder = new BodyBuilder(); builder.TextBody = $@"Dear {supervisorDelegate.SupervisorName}, {supervisorDelegate.FirstName} {supervisorDelegate.LastName} ({supervisorDelegate.DelegateEmail}) has requested that you review {resultCount.ToString()} of their self assessment results for the activity '{delegateSelfAssessment.RoleName}' in the NHS Health Education England, Digital Learning Solutions (DLS) platform. diff --git a/DigitalLearningSolutions.Data/Services/SelfAssessmentService.cs b/DigitalLearningSolutions.Data/Services/SelfAssessmentService.cs index dec31ab1ae..2cc6b1ec24 100644 --- a/DigitalLearningSolutions.Data/Services/SelfAssessmentService.cs +++ b/DigitalLearningSolutions.Data/Services/SelfAssessmentService.cs @@ -32,11 +32,12 @@ public interface ISelfAssessmentService bool CanDelegateAccessSelfAssessment(int delegateId, int selfAssessmentId); // Competencies - IEnumerable GetCandidateAssessmentResultsById(int candidateAssessmentId, int adminId); + IEnumerable GetCandidateAssessmentResultsById(int candidateAssessmentId, int adminId, int? selfAssessmentResultId = null); IEnumerable GetCandidateAssessmentResultsForReviewById(int candidateAssessmentId, int adminId); IEnumerable GetCandidateAssessmentResultsToVerifyById(int selfAssessmentId, int candidateId); + IEnumerable GetResultSupervisorVerifications(int selfAssessmentId, int candidateId); IEnumerable GetLevelDescriptorsForAssessmentQuestion( int assessmentQuestionId, @@ -170,9 +171,9 @@ public void SetCompleteByDate(int selfAssessmentId, int candidateId, DateTime? c selfAssessmentDataService.SetCompleteByDate(selfAssessmentId, candidateId, completeByDate); } - public IEnumerable GetCandidateAssessmentResultsById(int candidateAssessmentId, int adminId) + public IEnumerable GetCandidateAssessmentResultsById(int candidateAssessmentId, int adminId, int? selfAssessmentResultId = null) { - return selfAssessmentDataService.GetCandidateAssessmentResultsById(candidateAssessmentId, adminId); + return selfAssessmentDataService.GetCandidateAssessmentResultsById(candidateAssessmentId, adminId, selfAssessmentResultId); } public IEnumerable GetCandidateAssessmentResultsForReviewById( @@ -188,6 +189,11 @@ public IEnumerable GetCandidateAssessmentResultsToVerifyById(int sel return selfAssessmentDataService.GetCandidateAssessmentResultsToVerifyById(selfAssessmentId, candidateId); } + public IEnumerable GetResultSupervisorVerifications(int selfAssessmentId, int candidateId) + { + return selfAssessmentDataService.GetResultSupervisorVerifications(selfAssessmentId, candidateId); + } + public IEnumerable GetSupervisorsForSelfAssessmentId( int selfAssessmentId, int candidateId diff --git a/DigitalLearningSolutions.Data/Services/SupervisorService.cs b/DigitalLearningSolutions.Data/Services/SupervisorService.cs index af3aa614c1..8d4f1ad230 100644 --- a/DigitalLearningSolutions.Data/Services/SupervisorService.cs +++ b/DigitalLearningSolutions.Data/Services/SupervisorService.cs @@ -36,6 +36,8 @@ public interface ISupervisorService bool ConfirmSupervisorDelegateById(int supervisorDelegateId, int candidateId, int adminId); bool RemoveSupervisorDelegateById(int supervisorDelegateId, int candidateId, int adminId); bool UpdateSelfAssessmentResultSupervisorVerifications(int selfAssessmentResultSupervisorVerificationId, string? comments, bool signedOff, int adminId); + bool UpdateSelfAssessmentResultSupervisorVerificationsEmailSent(int selfAssessmentResultSupervisorVerificationId); + int RemoveSelfAssessmentResultSupervisorVerificationById(int id); bool RemoveCandidateAssessment(int candidateAssessmentId); void UpdateNotificationSent(int supervisorDelegateId); void UpdateCandidateAssessmentSupervisorVerificationById(int? candidateAssessmentSupervisorVerificationId, string? supervisorComments, bool signedOff); @@ -389,6 +391,33 @@ FROM SelfAssessmentResultSupervisorVerifications INNER JOIN return false; } } + + public bool UpdateSelfAssessmentResultSupervisorVerificationsEmailSent(int selfAssessmentResultSupervisorVerificationId) + { + var numberOfAffectedRows = connection.Execute( + @"UPDATE SelfAssessmentResultSupervisorVerifications + SET EmailSent = getUTCDate() + FROM SelfAssessmentResultSupervisorVerifications + WHERE ID = @selfAssessmentResultSupervisorVerificationId", + new { selfAssessmentResultSupervisorVerificationId } + ); + return numberOfAffectedRows > 0; + } + + public int RemoveSelfAssessmentResultSupervisorVerificationById(int id) + { + var numberOfAffectedRows = connection.Execute( + @"DELETE FROM SelfAssessmentResultSupervisorVerifications WHERE ID = @id", + new { id }); + + if (numberOfAffectedRows < 1) + { + logger.LogWarning( + $"Not deleting supervisor verifications as db update failed. SelfAssessmentResultSupervisorVerification.Id: {id}" + ); + } + return numberOfAffectedRows; + } public IEnumerable GetAvailableRoleProfilesForDelegate(int candidateId, int centreId) { return connection.Query( diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs index 3ec30c5425..c0ce5b3504 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs @@ -165,11 +165,6 @@ public IActionResult CreateNewFramework(string actionname, int frameworkId = 0) ); multiPageFormService.SetMultiPageFormData(sessionNewFramework, MultiPageFormDataFeature.AddNewFramework, TempData); detailFramework = sessionNewFramework.DetailFramework; - multiPageFormService.SetMultiPageFormData( - sessionNewFramework, - MultiPageFormDataFeature.AddNewFramework, - TempData - ); } return View("Developer/Name", detailFramework); } diff --git a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs index 94b005f1e6..2c9f94e39a 100644 --- a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs +++ b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs @@ -815,10 +815,35 @@ public IActionResult StartRequestVerification(int selfAssessmentId) return RedirectToAction("VerificationPickSupervisor", new { selfAssessmentId }); } - [Route("/LearningPortal/SelfAssessment/{selfAssessmentId:int}/Verification/Supervisor")] + [Route("/LearningPortal/SelfAssessment/{selfAssessmentId:int}/ConfirmationRequests")] + public IActionResult ReviewConfirmationRequests(int selfAssessmentId) + { + var candidateId = User.GetCandidateIdKnownNotNull(); + var selfAssessment = selfAssessmentService.GetSelfAssessmentForCandidateById(candidateId, selfAssessmentId); + if (selfAssessment == null) + { + return RedirectToAction("StatusCode", "LearningSolutions", new { code = (int)(HttpStatusCode.NotFound) }); + } + + var competencies = PopulateCompetencyLevelDescriptors( + selfAssessmentService.GetResultSupervisorVerifications(selfAssessmentId, User.GetCandidateIdKnownNotNull()).ToList() + ); + var model = new ReviewConfirmationRequestsViewModel + { + SelfAssessment = selfAssessment, + Competencies = competencies + }; + TempData.Keep(); + return View("SelfAssessments/ReviewConfirmationRequests", model); + } + + [Route("/LearningPortal/SelfAssessment/{selfAssessmentId:int}/ConfirmationRequests/New/ChooseSupervisor")] public IActionResult VerificationPickSupervisor(int selfAssessmentId) { - var sessionRequestVerification = TempData.Peek(); + var sessionRequestVerification = multiPageFormService.GetMultiPageFormData( + MultiPageFormDataFeature.AddSelfAssessmentRequestVerification, + TempData + ); if (sessionRequestVerification == null) { return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); @@ -844,10 +869,13 @@ public IActionResult VerificationPickSupervisor(int selfAssessmentId) } [HttpPost] - [Route("/LearningPortal/SelfAssessment/{selfAssessmentId:int}/Verification/Supervisor")] + [Route("/LearningPortal/SelfAssessment/{selfAssessmentId:int}/ConfirmationRequests/New/ChooseSupervisor")] public IActionResult VerificationPickSupervisor(VerificationPickSupervisorViewModel model) { - var sessionRequestVerification = TempData.Peek(); + var sessionRequestVerification = multiPageFormService.GetMultiPageFormData( + MultiPageFormDataFeature.AddSelfAssessmentRequestVerification, + TempData + ); if (sessionRequestVerification == null) { return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); @@ -883,10 +911,13 @@ public IActionResult VerificationPickSupervisor(VerificationPickSupervisorViewMo return RedirectToAction("VerificationPickResults", new { sessionRequestVerification.SelfAssessmentID }); } - [Route("/LearningPortal/SelfAssessment/{selfAssessmentId:int}/Verification/Results")] + [Route("/LearningPortal/SelfAssessment/{selfAssessmentId:int}/ConfirmationRequests/New/PickResults")] public IActionResult VerificationPickResults(int selfAssessmentId) { - var sessionRequestVerification = TempData.Peek(); + var sessionRequestVerification = multiPageFormService.GetMultiPageFormData( + MultiPageFormDataFeature.AddSelfAssessmentRequestVerification, + TempData + ); if (sessionRequestVerification == null) { return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); @@ -916,10 +947,13 @@ public IActionResult VerificationPickResults(int selfAssessmentId) } [HttpPost] - [Route("/LearningPortal/SelfAssessment/{selfAssessmentId:int}/Verification/Results")] + [Route("/LearningPortal/SelfAssessment/{selfAssessmentId:int}/ConfirmationRequests/New/PickResults")] public IActionResult VerificationPickResults(VerificationPickResultsViewModel model, int selfAssessmentId) { - var sessionRequestVerification = TempData.Peek(); + var sessionRequestVerification = multiPageFormService.GetMultiPageFormData( + MultiPageFormDataFeature.AddSelfAssessmentRequestVerification, + TempData + ); if (sessionRequestVerification == null) { return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); @@ -948,10 +982,13 @@ public IActionResult VerificationPickResults(VerificationPickResultsViewModel mo return RedirectToAction("VerificationSummary", new { sessionRequestVerification.SelfAssessmentID }); } - [Route("/LearningPortal/SelfAssessment/{selfAssessmentId:int}/Verification/Summary")] + [Route("/LearningPortal/SelfAssessment/{selfAssessmentId:int}/ConfirmationRequests/New/Summary")] public IActionResult VerificationSummary(int selfAssessmentId) { - var sessionRequestVerification = TempData.Peek(); + var sessionRequestVerification = multiPageFormService.GetMultiPageFormData( + MultiPageFormDataFeature.AddSelfAssessmentRequestVerification, + TempData + ); if (sessionRequestVerification == null) { return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); @@ -990,10 +1027,35 @@ public IActionResult VerificationSummary(int selfAssessmentId) return View("SelfAssessments/VerificationSummary", model); } + [Route("/LearningPortal/SelfAssessment/{selfAssessmentId:int}/{vocabulary}/Confirmation/{candidateAssessmentSupervisorId}/{selfAssessmentResultId}/{supervisorVerificationId}/Resend")] + public IActionResult ResendSupervisorVerificationRequest(int selfAssessmentId, string vocabulary, int candidateAssessmentSupervisorId, int selfAssessmentResultId, int supervisorVerificationId) + { + + frameworkNotificationService.SendResultVerificationRequest( + candidateAssessmentSupervisorId, + selfAssessmentId, + 1, + User.GetCandidateIdKnownNotNull(), + selfAssessmentResultId + ); + supervisorService.UpdateSelfAssessmentResultSupervisorVerificationsEmailSent(supervisorVerificationId); + return RedirectToAction("ReviewConfirmationRequests", new { selfAssessmentId, vocabulary }); + } + + [Route("/LearningPortal/SelfAssessment/{selfAssessmentId:int}/Verification/{supervisorVerificationId}/Withdraw")] + public IActionResult WithdrawSupervisorVerificationRequest(int selfAssessmentId, int supervisorVerificationId) + { + supervisorService.RemoveSelfAssessmentResultSupervisorVerificationById(supervisorVerificationId); + return RedirectToAction("ReviewConfirmationRequests", new { selfAssessmentId }); + } + [HttpPost] public IActionResult SubmitVerification() { - var sessionRequestVerification = TempData.Peek(); + var sessionRequestVerification = multiPageFormService.GetMultiPageFormData( + MultiPageFormDataFeature.AddSelfAssessmentRequestVerification, + TempData + ); if (sessionRequestVerification == null) { return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); diff --git a/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs b/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs index 38863b9455..ed0b945cf6 100644 --- a/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs +++ b/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs @@ -221,13 +221,13 @@ public IActionResult AllStaffList() } [Route("/Supervisor/Staff/{supervisorDelegateId}/ProfileAssessment/{candidateAssessmentId}/Review")] - public IActionResult ReviewDelegateSelfAssessment(int supervisorDelegateId, int candidateAssessmentId) + [Route("/Supervisor/Staff/{supervisorDelegateId}/ProfileAssessment/{candidateAssessmentId}/Review/{selfAssessmentResultId}")] + public IActionResult ReviewDelegateSelfAssessment(int supervisorDelegateId, int candidateAssessmentId, int? selfAssessmentResultId = null) { var adminId = GetAdminID(); - var superviseDelegate = - supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminID(), 0); + var superviseDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, GetAdminID(), 0); var reviewedCompetencies = PopulateCompetencyLevelDescriptors( - selfAssessmentService.GetCandidateAssessmentResultsById(candidateAssessmentId, adminId).ToList() + selfAssessmentService.GetCandidateAssessmentResultsById(candidateAssessmentId, adminId, selfAssessmentResultId).ToList() ); var delegateSelfAssessment = supervisorService.GetSelfAssessmentByCandidateAssessmentId(candidateAssessmentId, adminId); var model = new ReviewSelfAssessmentViewModel() diff --git a/DigitalLearningSolutions.Web/Helpers/DisplayStringHelper.cs b/DigitalLearningSolutions.Web/Helpers/DisplayStringHelper.cs index 051f5c67df..e58192f6ff 100644 --- a/DigitalLearningSolutions.Web/Helpers/DisplayStringHelper.cs +++ b/DigitalLearningSolutions.Web/Helpers/DisplayStringHelper.cs @@ -104,5 +104,10 @@ public static string Ellipsis(string text, int length) return string.Format("{0}...", text.Trim().Substring(0, length)); } + + public static string RemoveMarkup(string input) + { + return Regex.Replace(input ?? String.Empty, $"<.*?>| ", String.Empty); + } } } diff --git a/DigitalLearningSolutions.Web/Helpers/FrameworkVocabularyHelper.cs b/DigitalLearningSolutions.Web/Helpers/FrameworkVocabularyHelper.cs index 06831b1c15..3190662b9e 100644 --- a/DigitalLearningSolutions.Web/Helpers/FrameworkVocabularyHelper.cs +++ b/DigitalLearningSolutions.Web/Helpers/FrameworkVocabularyHelper.cs @@ -1,40 +1,44 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace DigitalLearningSolutions.Web.Helpers -{ - public class FrameworkVocabularyHelper - { - public static string VocabularySingular(string? vocab) - { - if (vocab == null) - { - return "Capability"; - } - else - { - return vocab; - } - } - public static string VocabularyPlural(string? vocab) - { - if (vocab == null) - { - return "Capabilities"; - } - else - { - if (vocab.EndsWith("y")) - { - return vocab.Substring(0, vocab.Length - 1) + "ies"; - } - else - { - return vocab + "s"; - } - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace DigitalLearningSolutions.Web.Helpers +{ + public class FrameworkVocabularyHelper + { + public static string VocabularySingular(string? vocab) + { + if (vocab == null) + { + return "Capability"; + } + else + { + return vocab; + } + } + public static string VocabularyPlural(string? vocab) + { + if (vocab == null) + { + return "Capabilities"; + } + else + { + if (vocab.EndsWith("y")) + { + return vocab.Substring(0, vocab.Length - 1) + "ies"; + } + else + { + return vocab + "s"; + } + } + } + public static string FirstLetterToUpper(string? text) + { + return text?.Length > 1 ? char.ToUpper(text[0]) + text.Substring(1).ToLower() : string.Empty; + } + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/LearningPortal/SelfAssessments/ReviewConfirmationRequestsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/LearningPortal/SelfAssessments/ReviewConfirmationRequestsViewModel.cs new file mode 100644 index 0000000000..8330712443 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/LearningPortal/SelfAssessments/ReviewConfirmationRequestsViewModel.cs @@ -0,0 +1,27 @@ +using DigitalLearningSolutions.Data.Models.SelfAssessments; +using DigitalLearningSolutions.Web.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace DigitalLearningSolutions.Web.ViewModels.LearningPortal.SelfAssessments +{ + public class ReviewConfirmationRequestsViewModel + { + public CurrentSelfAssessment? SelfAssessment { get; set; } + public IEnumerable Competencies { get; set; } + //public int SupervisorId { get; set; } + public string VocabPlural() + { + if (SelfAssessment != null) + { + return FrameworkVocabularyHelper.VocabularyPlural(SelfAssessment.Vocabulary); + } + else + { + return "Capabilities"; + } + } + } +} diff --git a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/ReviewConfirmationRequests.cshtml b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/ReviewConfirmationRequests.cshtml new file mode 100644 index 0000000000..9aa49b3a83 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/ReviewConfirmationRequests.cshtml @@ -0,0 +1,129 @@ +@using DigitalLearningSolutions.Web.ViewModels.LearningPortal.SelfAssessments +@using DigitalLearningSolutions.Web.Helpers +@model ReviewConfirmationRequestsViewModel +@{ + var errorHasOccurred = !ViewData.ModelState.IsValid; + Layout = "SelfAssessments/_Layout"; + ViewData["Title"] = "Self Assessment"; + ViewData["SelfAssessmentTitle"] = @Model.SelfAssessment.Name; +} + +@section breadcrumbs { +
  • + @(Model.SelfAssessment.Name) introduction +
  • +
  • + @(Model.VocabPlural()) home +
  • +
  • Confirmation requests
  • +} + +@section mobilebacklink +{ +

    + + Back to @Model.VocabPlural() + +

    +} + + +

    @Model.SelfAssessment.Vocabulary confirmation requests

    +@if (Model.Competencies.Any()) +{ +

    Outstanding requests

    + + + + + + + + + + + @foreach (var competency in Model.Competencies) + { + foreach(var question in competency.AssessmentQuestions) + { + + + + + + + } + } + +
    + Competency + + Question + + Response + + Request +
    + Comptenecy + @DisplayStringHelper.RemoveMarkup(competency.Description) + + Question + @DisplayStringHelper.RemoveMarkup(question.Question) + + Response + + + Request + @competency.SupervisorName
    + @competency.SupervisorVerificationRequested?.ToString("dd/MM/yyyy")
    + + @if (competency.EmailSent.HasValue && DateTime.UtcNow.Subtract(competency.EmailSent.Value) > TimeSpan.FromHours(1)) + { + Resend + } + Withdraw + +
    + + +} +else +{ +

    + There are no outstanding confirmation requests. +

    + + New confirmation request + + + Cancel + +} diff --git a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/VerificationPickResults.cshtml b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/VerificationPickResults.cshtml index b7aeba5f66..82e7fb18f8 100644 --- a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/VerificationPickResults.cshtml +++ b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/VerificationPickResults.cshtml @@ -14,30 +14,35 @@
  • @(Model.VocabPlural()) home
  • -
  • Request confirmation
  • +
  • + Confirmation requests +
  • +
  • New
  • } @section mobilebacklink {

    - - Back to @Model.VocabPlural() - + Back to Confirmation requests

    }

    Request @Model.Vocabulary.ToLower() confirmation

    -@if (errorHasOccurred) { +@if (errorHasOccurred) +{ } -@if (Model.CompetencyGroups.Any()) { +@if (Model.CompetencyGroups.Any()) +{

    Choose the @Model.Vocabulary.ToLower() self assessment results that you wish to confirm.

    - @foreach (var competencyGroup in Model.CompetencyGroups) { + @foreach (var competencyGroup in Model.CompetencyGroups) + {
    @@ -45,7 +50,8 @@
    - @if (competencyGroup.Count() > 1) { + @if (competencyGroup.Count() > 1) + {
    Select all @Model.VocabPlural().ToLower() @@ -55,43 +61,45 @@ } - - - - - + + + + + - @foreach (var competency in competencyGroup) { - @foreach (var question in competency.AssessmentQuestions) { - - - - + @foreach (var competency in competencyGroup) + { + @foreach (var question in competency.AssessmentQuestions) + { + + + + - + + } } - }
    - @competencyGroup.First().Vocabulary - - Question - - Response -
    + @competencyGroup.First().Vocabulary + + Question + + Response +
    - @competency.Vocabulary -
    - - -
    -
    - Questions - @question.Question - - Responses - -
    + @competency.Vocabulary +
    + + +
    +
    + Questions + @question.Question + + Responses + +
    } @@ -117,7 +125,9 @@ Cancel
    -} else { +} +else +{

    There are no self assessment results ready for confirmation.

    diff --git a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/VerificationPickSupervisor.cshtml b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/VerificationPickSupervisor.cshtml index c1c098bebe..66409a03f4 100644 --- a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/VerificationPickSupervisor.cshtml +++ b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/VerificationPickSupervisor.cshtml @@ -9,15 +9,16 @@ @section breadcrumbs {
  • @(Model.SelfAssessment.Name) introduction
  • @(Model.VocabPlural()) home
  • -
  • Request confirmation
  • +
  • Confirmation requests
  • +
  • New
  • } @section mobilebacklink {

    - - Back to @Model.VocabPlural() + Back to Confirmation requests

    } @@ -63,7 +64,7 @@

    Can't see the @Model.SelfAssessment.VerificationRoleName you need? Click here to manage supervisors.