diff --git a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs index ddf7b19e29..1f3e4fc367 100644 --- a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs @@ -171,6 +171,7 @@ int GetSelfAssessmentActivityDelegatesExportCount(string searchString, string so ActivitySummaryCompetencySelfAssesment? GetActivitySummaryCompetencySelfAssesment(int CandidateAssessmentSupervisorVerificationsId); bool IsUnsupervisedSelfAssessment(int selfAssessmentId); bool IsCentreSelfAssessment(int selfAssessmentId, int centreId); + bool HasMinimumOptionalCompetencies(int selfAssessmentId, int delegateUserId); } public partial class SelfAssessmentDataService : ISelfAssessmentDataService @@ -729,5 +730,24 @@ public bool IsCentreSelfAssessment(int selfAssessmentId, int centreId) ); return ResultCount > 0; } + + public bool HasMinimumOptionalCompetencies(int selfAssessmentId, int delegateUserId) + { + return connection.ExecuteScalar( + @"SELECT CASE WHEN COUNT(SAS.ID)>=(SELECT MinimumOptionalCompetencies FROM SelfAssessments WHERE ID = @selfAssessmentId) + THEN 1 ELSE 0 END AS HasMinimumOptionalCompetencies + FROM CandidateAssessmentOptionalCompetencies AS CAOC + INNER JOIN CandidateAssessments AS CA + ON CAOC.CandidateAssessmentID = CA.ID AND CA.SelfAssessmentID = @selfAssessmentId + AND CA.DelegateUserID = @delegateUserId AND CA.RemovedDate IS NULL + INNER JOIN SelfAssessmentStructure AS SAS + ON CAOC.CompetencyID = SAS.CompetencyID AND CAOC.CompetencyGroupID = SAS.CompetencyGroupID + AND SAS.SelfAssessmentID = @selfAssessmentId + INNER JOIN SelfAssessments AS SA + ON SAS.SelfAssessmentID = SA.ID + WHERE (CAOC.IncludedInSelfAssessment = 1)", + new { selfAssessmentId, delegateUserId } + ); + } } } diff --git a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs index dbda054cc3..c1b9803d43 100644 --- a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs +++ b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs @@ -23,10 +23,6 @@ using Microsoft.Extensions.Logging; using GDS.MultiPageFormData.Enums; using DigitalLearningSolutions.Data.Helpers; - using DigitalLearningSolutions.Web.Services; - using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Delegates.ViewDelegate; - using DocumentFormat.OpenXml.EMMA; - using DigitalLearningSolutions.Data.Models.Supervisor; using DigitalLearningSolutions.Data.Models.Common; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.ViewEngines; @@ -426,6 +422,52 @@ public IActionResult AddSelfAssessmentOverviewFilter(SearchSelfAssessmentOvervie return RedirectToAction("FilteredSelfAssessmentGroups", model); } + [ServiceFilter(typeof(IsCentreAuthorizedSelfAssessment))] + [Route("LearningPortal/SelfAssessment/{selfAssessmentId}/{vocabulary}/AddOptional")] + public IActionResult AddOptionalCompetencies(int selfAssessmentId, string vocabulary) + { + var delegateUserId = User.GetUserIdKnownNotNull(); + var assessment = selfAssessmentService.GetSelfAssessmentForCandidateById(delegateUserId, selfAssessmentId); + if (assessment == null) + { + logger.LogWarning( + $"Attempt to display self assessment overview for user {delegateUserId} with no self assessment" + ); + return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); + } + + var optionalCompetencies = selfAssessmentService.GetCandidateAssessmentOptionalCompetencies(selfAssessmentId, delegateUserId); + if (optionalCompetencies.Any()) + { + if (!selfAssessmentService.HasMinimumOptionalCompetencies(selfAssessmentId, delegateUserId)) + { + var model = new AddOptionalCompetenciesViewModel { SelfAssessment = assessment }; + return View("SelfAssessments/AddOptionalCompetencies", model); + } + } + + return RedirectToAction("SelfAssessmentOverview", new { selfAssessmentId, vocabulary }); + } + + [HttpPost] + [Route("LearningPortal/SelfAssessment/{selfAssessmentId}/{vocabulary}/AddOptional")] + public IActionResult AddOptionalCompetencies(string vocabulary, int selfAssessmentId) + { + var delegateUserId = User.GetUserIdKnownNotNull(); + var assessment = selfAssessmentService.GetSelfAssessmentForCandidateById(delegateUserId, selfAssessmentId); + if (assessment == null) + { + logger.LogWarning( + $"Attempt to display self assessment overview for user {delegateUserId} with no self assessment" + ); + return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); + } + + TempData["FromAddOptional"] = "true"; + + return RedirectToAction("ManageOptionalCompetencies", new { selfAssessmentId, vocabulary }); + } + [ServiceFilter(typeof(IsCentreAuthorizedSelfAssessment))] [Route("LearningPortal/SelfAssessment/{selfAssessmentId}/{vocabulary}/{competencyGroupId}")] [Route("LearningPortal/SelfAssessment/{selfAssessmentId}/{vocabulary}")] @@ -479,7 +521,7 @@ public IActionResult SelfAssessmentOverview(int selfAssessmentId, string vocabul CompetencyGroups = competencies.GroupBy(competency => competency.CompetencyGroup), PreviousCompetencyNumber = Math.Max(competencies.Count(), 1), NumberOfOptionalCompetencies = optionalCompetencies.Count(), - NumberOfSelfAssessedOptionalCompetencies = optionalCompetencies.Count(x => x.IncludedInSelfAssessment ) , + NumberOfSelfAssessedOptionalCompetencies = optionalCompetencies.Count(x => x.IncludedInSelfAssessment), SupervisorSignOffs = supervisorSignOffs, SearchViewModel = searchViewModel }; @@ -1463,6 +1505,13 @@ public IActionResult ManageOptionalCompetencies(int selfAssessmentId) CompetencyGroups = optionalCompetencies.GroupBy(competency => competency.CompetencyGroup), IncludedSelfAssessmentStructureIds = includedSelfAssessmentStructureIds, }; + + if (TempData["FromAddOptional"] != null) + { + ViewBag.FromAddOptionalPage = "true"; + TempData.Remove("FromAddOptional"); + } + return View("SelfAssessments/ManageOptionalCompetencies", model); } @@ -1645,7 +1694,7 @@ public IActionResult WithdrawSupervisorSignOffRequest( ); } - + [Route("/LearningPortal/selfAssessments/{CandidateAssessmentId:int}/{vocabulary}/Certificate")] public IActionResult CompetencySelfAssessmentCertificate(int CandidateAssessmentId, string vocabulary) @@ -1654,12 +1703,12 @@ public IActionResult CompetencySelfAssessmentCertificate(int CandidateAssessment var adminId = User.GetAdminId(); var userId = User.GetUserIdKnownNotNull(); var competencymaindata = selfAssessmentService.GetCompetencySelfAssessmentCertificate(CandidateAssessmentId); - if ((competencymaindata == null)|| ( competencymaindata.LearnerId != User.GetUserIdKnownNotNull()) || (CandidateAssessmentId == 0) || (userId != competencymaindata.LearnerId)) + if ((competencymaindata == null) || (competencymaindata.LearnerId != User.GetUserIdKnownNotNull()) || (CandidateAssessmentId == 0) || (userId != competencymaindata.LearnerId)) { return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); } var delegateUserId = competencymaindata.LearnerId; - var recentResults = selfAssessmentService.GetMostRecentResults(competencymaindata.SelfAssessmentID, competencymaindata.LearnerDelegateAccountId).ToList(); + var recentResults = selfAssessmentService.GetMostRecentResults(competencymaindata.SelfAssessmentID, competencymaindata.LearnerDelegateAccountId).ToList(); var supervisorSignOffs = selfAssessmentService.GetSupervisorSignOffsForCandidateAssessment(competencymaindata.SelfAssessmentID, delegateUserId); if (!CertificateHelper.CanViewCertificate(recentResults, supervisorSignOffs)) { @@ -1723,7 +1772,7 @@ public async Task DownloadCertificate(int candidateAssessmentId, if (vocabulary == "Proficiencies") { var userId = User.GetUserIdKnownNotNull(); - if(userId != competencymaindata.LearnerId ) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); + if (userId != competencymaindata.LearnerId) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); } if (vocabulary == "ProfileAssessment") diff --git a/DigitalLearningSolutions.Web/Services/SelfAssessmentService.cs b/DigitalLearningSolutions.Web/Services/SelfAssessmentService.cs index cacd2f17b9..ff7ef8d53a 100644 --- a/DigitalLearningSolutions.Web/Services/SelfAssessmentService.cs +++ b/DigitalLearningSolutions.Web/Services/SelfAssessmentService.cs @@ -3,9 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; - using AngleSharp.Attributes; using DigitalLearningSolutions.Data.DataServices.SelfAssessmentDataService; - using DigitalLearningSolutions.Data.Models.Centres; using DigitalLearningSolutions.Data.Models.Common.Users; using DigitalLearningSolutions.Data.Models.External.Filtered; using DigitalLearningSolutions.Data.Models.Frameworks; @@ -150,6 +148,7 @@ public int GetSelfAssessmentActivityDelegatesExportCount(string searchString, st bool IsUnsupervisedSelfAssessment(int selfAssessmentId); IEnumerable GetCandidateAssessments(int delegateUserId, int selfAssessmentId); bool IsCentreSelfAssessment(int selfAssessmentId, int centreId); + bool HasMinimumOptionalCompetencies(int selfAssessmentId, int delegateUserId); } @@ -554,5 +553,10 @@ public bool IsCentreSelfAssessment(int selfAssessmentId, int centreId) { return selfAssessmentDataService.IsCentreSelfAssessment(selfAssessmentId, centreId); } + + public bool HasMinimumOptionalCompetencies(int selfAssessmentId, int delegateUserId) + { + return selfAssessmentDataService.HasMinimumOptionalCompetencies(selfAssessmentId, delegateUserId); + } } } diff --git a/DigitalLearningSolutions.Web/ViewModels/LearningPortal/SelfAssessments/AddOptionalCompetenciesViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/LearningPortal/SelfAssessments/AddOptionalCompetenciesViewModel.cs new file mode 100644 index 0000000000..d8957898db --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/LearningPortal/SelfAssessments/AddOptionalCompetenciesViewModel.cs @@ -0,0 +1,14 @@ +using DigitalLearningSolutions.Data.Models.SelfAssessments; +using DigitalLearningSolutions.Web.Helpers; + +namespace DigitalLearningSolutions.Web.ViewModels.LearningPortal.SelfAssessments +{ + public class AddOptionalCompetenciesViewModel + { + public CurrentSelfAssessment SelfAssessment { get; set; } + public string VocabPlural() + { + return FrameworkVocabularyHelper.VocabularyPlural(SelfAssessment.Vocabulary); + } + } +} diff --git a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/AddOptionalCompetencies.cshtml b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/AddOptionalCompetencies.cshtml new file mode 100644 index 0000000000..fc9f2e351d --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/AddOptionalCompetencies.cshtml @@ -0,0 +1,46 @@ +@using DigitalLearningSolutions.Web.ViewModels.LearningPortal.SelfAssessments +@model AddOptionalCompetenciesViewModel +@{ + ViewData["Title"] = "Add optional " + Model.VocabPlural().ToLower(); + Layout = "SelfAssessments/_Layout"; +} +@section breadcrumbs { +
  • + @(Model.SelfAssessment.Name) introduction +
  • +
  • Add optional @(Model.VocabPlural().ToLower())?
  • +} + +@section mobilebacklink +{ +

    + + Back to @Model.SelfAssessment.Name + +

    +} +
    +
    +
    +

    Add optional @Model.VocabPlural().ToLower() to your assessment?

    +
    +
    +

    During your assessment you will need to add one or more optional proficiencies to your assessment to be certified within your role

    +
    + + + What are optional proficiencies? + + +
    +

    Optional proficiencies refer to specific skills or competencies that are not part of the core requirements but may be added based on your role, specialisation, or the needs of your workplace.

    +
    +
    +

    These proficiencies might be different depending on your role or organization so you may need to discuss this with your educator or manager

    + + + Remind me later +
    +
    +
    diff --git a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/ManageOptionalCompetencies.cshtml b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/ManageOptionalCompetencies.cshtml index 7806c53130..6a0e4cbe51 100644 --- a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/ManageOptionalCompetencies.cshtml +++ b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/ManageOptionalCompetencies.cshtml @@ -1,13 +1,26 @@ @using DigitalLearningSolutions.Web.ViewModels.LearningPortal.SelfAssessments +@using DigitalLearningSolutions.Web.Extensions @model ManageOptionalCompetenciesViewModel @{ var errorHasOccurred = !ViewData.ModelState.IsValid; Layout = "SelfAssessments/_Layout"; ViewData["Title"] = "Self Assessment - Optional Proficiencies"; ViewData["SelfAssessmentTitle"] = @Model.SelfAssessment.Name; + var backLinkData = Html.GetRouteValues(); } - -@section breadcrumbs { +@if (ViewBag.FromAddOptionalPage != null) +{ + @section breadcrumbs { +
  • +
    + +
    +
  • + } +} +else +{ + @section breadcrumbs {
  • @(Model.SelfAssessment.Name) introduction
  • @@ -15,17 +28,32 @@ @Model.VocabPlural() home
  • Manage optional @Model.VocabPlural()
  • + } } + @section mobilebacklink { -

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

    + @if (ViewBag.FromAddOptionalPage != null) + { +

    + + Back to add optional @Model.VocabPlural().ToLower()? + +

    + } + else + { +

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

    + } } @@ -96,14 +124,28 @@ @section scripts { diff --git a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/SelfAssessmentDescription.cshtml b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/SelfAssessmentDescription.cshtml index b71095a9c8..d5c2dd92ca 100644 --- a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/SelfAssessmentDescription.cshtml +++ b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/SelfAssessmentDescription.cshtml @@ -17,7 +17,7 @@
  • @Model.Name introduction
  • } @section mobilebacklink - { +{

    Back to Current activities @@ -37,7 +37,7 @@

    } @@ -66,7 +66,7 @@ else } else { - View @Model.VocabPlural + View @Model.VocabPlural @if (Model.UserBookmark != null && bookmarkIsRelevant) { Continue where I left off diff --git a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/_Layout.cshtml b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/_Layout.cshtml index a96cfa0f6f..a92ae4ce53 100644 --- a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/_Layout.cshtml +++ b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/_Layout.cshtml @@ -78,7 +78,10 @@