From cec7b4c69ffcc3e8f7393de7b2d0d8494267e475 Mon Sep 17 00:00:00 2001 From: Auldrin-Possa Date: Thu, 26 Feb 2026 16:37:16 +0000 Subject: [PATCH 1/7] TD-IssueFix-Send for review and publish task --- .../CompetencyAssessmentDataService.cs | 12 +- .../DataServices/FrameworkDataService.cs | 6 +- .../CompetencyAssessments.cs | 4 + .../Services/FrameworkNotificationService.cs | 9 +- .../PublishReviewViewModel.cs | 2 +- .../ManageCompetencyAssessment.cshtml | 106 ++++++++++-------- .../Views/Frameworks/Index.cshtml | 6 +- 7 files changed, 83 insertions(+), 62 deletions(-) diff --git a/DigitalLearningSolutions.Data/DataServices/CompetencyAssessmentDataService.cs b/DigitalLearningSolutions.Data/DataServices/CompetencyAssessmentDataService.cs index c285a4c28d..df0ef4ffa8 100644 --- a/DigitalLearningSolutions.Data/DataServices/CompetencyAssessmentDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/CompetencyAssessmentDataService.cs @@ -183,7 +183,7 @@ FROM NRPRoles private const string SelfAssessmentBaseTables = @"SelfAssessments AS sa LEFT OUTER JOIN - SelfAssessmentCollaborators AS sac ON sac.SelfAssessmentID = sa.ID AND sac.AdminID = @adminId"; + SelfAssessmentCollaborators AS sac ON sac.SelfAssessmentID = sa.ID AND sac.AdminID = @adminId AND sac.IsDeleted = 0"; private const string SelfAssessmentTables = @" LEFT OUTER JOIN @@ -1177,10 +1177,14 @@ public void RemoveCollaboratorFromCompetencyAssessment(int competencyAssessmentI CASE WHEN sc.CanModify = 1 THEN 'Contributor' ELSE 'Reviewer' END AS CompetencyAssessmentRole, sa.[Name] AS CompetencyAssessmentName, (SELECT Forename + ' ' + Surname + (CASE WHEN Active = 1 THEN '' ELSE ' (Inactive)' END) AS Expr1 FROM AdminUsers AS au1 WHERE (AdminID = @invitedByAdminId)) AS InvitedByName, - (SELECT Email FROM AdminUsers AS au2 WHERE (AdminID = @invitedByAdminId)) AS InvitedByEmail + (SELECT Email FROM AdminUsers AS au2 WHERE (AdminID = @invitedByAdminId)) AS InvitedByEmail, + sr.ID AS SelfAssessmentReviewID FROM SelfAssessmentCollaborators AS sc - INNER JOIN SelfAssessments AS sa ON sc.SelfAssessmentID = sa.ID - INNER JOIN AdminUsers AS au ON sc.AdminID = au.AdminID + INNER JOIN SelfAssessments AS sa ON sc.SelfAssessmentID = sa.ID + INNER JOIN AdminUsers AS au ON sc.AdminID = au.AdminID + LEFT JOIN SelfAssessmentReviews AS sr + ON sr.SelfAssessmentID = sa.ID AND sr.SelfAssessmentCollaboratorID = sc.ID + AND sr.ReviewComplete IS NULL AND sr.Archived IS NULL WHERE (sc.ID = @id) AND (sc.IsDeleted=0)", new { invitedByAdminId, id } ).FirstOrDefault(); diff --git a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs index 2c7d07013a..42d7cdc60d 100644 --- a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs @@ -2284,7 +2284,7 @@ FROM FrameworkCollaborators (SELECT COUNT(*) FROM SelfAssessments) AS CompetencyAssessmentCount, (SELECT COUNT(*) FROM SelfAssessments AS RP LEFT OUTER JOIN - SelfAssessmentCollaborators AS RPC ON RPC.SelfAssessmentID = RP.ID AND RPC.AdminID = @adminId + SelfAssessmentCollaborators AS RPC ON RPC.SelfAssessmentID = RP.ID AND RPC.AdminID = @adminId AND RPC.IsDeleted = 0 WHERE (RP.CreatedByAdminID = @adminId) OR (@adminId IN (SELECT AdminID @@ -2299,7 +2299,7 @@ public IEnumerable GetDashboardToDoItems(int adminId) return connection.Query( @"SELECT FW.ID AS FrameworkID, - 0 AS SelfAssessmentID, + 0 AS CompetencyAssessmentID, FW.FrameworkName AS ItemName, AU.Forename + ' ' + AU.Surname + (CASE WHEN AU.Active = 1 THEN '' ELSE ' (Inactive)' END) AS RequestorName, FWR.SignOffRequired, @@ -2319,7 +2319,7 @@ UNION ALL RPR.ReviewRequested AS Requested FROM SelfAssessmentReviews AS RPR INNER JOIN SelfAssessments AS RP ON RPR.SelfAssessmentID = RP.ID - INNER JOIN SelfAssessmentCollaborators AS RPC ON RPR.SelfAssessmentCollaboratorID = RPC.ID + INNER JOIN SelfAssessmentCollaborators AS RPC ON RPR.SelfAssessmentCollaboratorID = RPC.ID AND RPC.IsDeleted = 0 INNER JOIN AdminUsers AS AU ON RP.CreatedByAdminID = AU.AdminID WHERE (RPC.AdminID = @adminId) AND (RPR.ReviewComplete IS NULL) AND (RPR.Archived IS NULL)", new { adminId } diff --git a/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessments.cs b/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessments.cs index ac127b95a7..e6e0bed7aa 100644 --- a/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessments.cs +++ b/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessments.cs @@ -1530,6 +1530,10 @@ public IActionResult PublishWithoutReview(int competencyAssessmentId) [Route("/CompetencyAssessments/{competencyAssessmentId}/PublishWithoutReview")] public IActionResult PublishWithoutReview(PublishWithoutReviewViewModel publish) { + if (!ModelState.IsValid) + { + return View(publish); + } var adminId = GetAdminID(); var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(publish.CompetencyAssessmentID, adminId); var result = ValidateCompetencyAssessmentAndRole(publish.CompetencyAssessmentID, adminId, "Publish Without Review", competencyAssessmentBase); diff --git a/DigitalLearningSolutions.Web/Services/FrameworkNotificationService.cs b/DigitalLearningSolutions.Web/Services/FrameworkNotificationService.cs index d63447a265..b5d98402fe 100644 --- a/DigitalLearningSolutions.Web/Services/FrameworkNotificationService.cs +++ b/DigitalLearningSolutions.Web/Services/FrameworkNotificationService.cs @@ -150,7 +150,7 @@ public void SendReviewRequestForCompetencyAssessment(int id, int invitedByAdminI { throw new NotificationDataException($"No record found when trying to fetch collaboratorNotification Data. id: {id}, invitedByAdminId: {invitedByAdminId}"); } - var competencyAssessmentUrl = GetCompetencyAssessmentUrl(collaboratorNotification.SelfAssessmentID, "Review"); + var competencyAssessmentUrl = GetCompetencyAssessmentUrl(collaboratorNotification.SelfAssessmentID, "Review", collaboratorNotification.SelfAssessmentReviewID); string emailSubjectLine = (reminder ? " REMINDER: " : "") + "Competency Assessment Review Request - Digital Learning Solutions"; string signOffRequired = required ? "You are required to sign-off this competency assessment before it can be published." : "You are not required to sign-off this competency assessment before it is published."; var builder = new BodyBuilder @@ -168,10 +168,13 @@ public string GetFrameworkUrl(int frameworkId, string tab) frameworkUrl.Path += $"Framework/{frameworkId}/{tab}/"; return frameworkUrl.Uri.ToString(); } - public string GetCompetencyAssessmentUrl(int selfAssessmentID, string actionName) + public string GetCompetencyAssessmentUrl(int selfAssessmentID, string actionName, int? id = null) { var competencyAssessmentUrl = GetDLSUriBuilder(); - competencyAssessmentUrl.Path += $"CompetencyAssessments/{selfAssessmentID}/{actionName}"; + + competencyAssessmentUrl.Path += id != null + ? $"CompetencyAssessments/{selfAssessmentID}/{id}/{actionName}" + : $"CompetencyAssessments/{selfAssessmentID}/{actionName}"; return competencyAssessmentUrl.Uri.ToString(); } public string GetCurrentActivitiesUrl() diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/PublishReviewViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/PublishReviewViewModel.cs index 5ca2d9d2b3..79aeef2b8d 100644 --- a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/PublishReviewViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/PublishReviewViewModel.cs @@ -15,7 +15,7 @@ public PublishReviewViewModel(int competencyAssessmentID, string competencyAsses CompetencyAssessmentName = competencyAssessmentName; SelfAssessmentReviews = selfAssessmentReviews; CompetencyAssessmentBase = competencyAssessmentBase; - CanPublish = selfAssessmentReviews?.All(x => x.SignedOff) ?? true; + CanPublish = selfAssessmentReviews?.Where(x => x.SignOffRequired).All(x => x.SignedOff) ?? true; } public int CompetencyAssessmentID { get; set; } public string CompetencyAssessmentName { get; set; } = string.Empty; diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageCompetencyAssessment.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageCompetencyAssessment.cshtml index 5cfd64bf85..ada73dd4d0 100644 --- a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageCompetencyAssessment.cshtml +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/ManageCompetencyAssessment.cshtml @@ -36,6 +36,11 @@
@Model.CompetencyAssessmentName
+ @if (Model.SelfAssessmentReviewID != null && Model.SelfAssessmentCommentID is null) + { +
+
+ }
Change assessment name @@ -51,14 +56,16 @@ @(Model.PublishStatusID == 1 ? "Draft" : Model.PublishStatusID == 2 ? "In review" : "Published")
-
- @if (Model.SelfAssessmentReviewID != null && Model.SelfAssessmentCommentID is null) - { + @if (Model.SelfAssessmentReviewID != null && Model.SelfAssessmentCommentID is null) + { +
+ Submit my review review - } -
+ + + } @if (Model.HasCompetencies) {
@@ -73,7 +80,7 @@ diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Index.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Index.cshtml index 22b3983e71..de1fb0d506 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Index.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Index.cshtml @@ -21,7 +21,7 @@ }

Frameworks Dashboard

-

Welcome @Model.Username. You are accessing as a @(Model.IsFrameworkDeveloper ? "Framework Developer" : Model.IsFrameworkContributor ? "Framework Contributor" : "Framework Viewer") and a @(Model.IsWorkforceManager | Model.IsFrameworkDeveloper ? "Self Assessment Developer" : "Self Assessment Viewer").

+

Welcome @Model.Username. You are accessing as a @(Model.IsFrameworkDeveloper ? "Framework Developer" : Model.IsFrameworkContributor ? "Framework Contributor" : "Framework Viewer") and a @(Model.IsWorkforceManager | Model.IsFrameworkDeveloper ? "Self Assessment Developer" : "Self Assessment Viewer").

Your to do list

@if (Model.DashboardToDoItems.Count() > 0) { @@ -29,7 +29,7 @@ @foreach (var toDoItem in Model.DashboardToDoItems) {
  • - @if (toDoItem.FrameworkID != null) + @if (toDoItem.FrameworkID != 0) { @(toDoItem.SignOffRequired ? "Approve " : "Review ") @toDoItem.ItemName for @toDoItem.RequestorName (requested @toDoItem.Requested.ToShortDateString()) @@ -37,7 +37,7 @@ } else { - + @(toDoItem.SignOffRequired ? "Approve " : "Review ") @toDoItem.ItemName for @toDoItem.RequestorName (requested @toDoItem.Requested.ToShortDateString()) } From 163d79b7669950980a4b548e62a0b5305c97b084 Mon Sep 17 00:00:00 2001 From: Auldrin-Possa Date: Mon, 2 Mar 2026 16:19:48 +0000 Subject: [PATCH 2/7] TD-6912-Use UserID comparison to prevent duplicate working group entries --- .../DataServices/CompetencyAssessmentDataService.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/DigitalLearningSolutions.Data/DataServices/CompetencyAssessmentDataService.cs b/DigitalLearningSolutions.Data/DataServices/CompetencyAssessmentDataService.cs index c285a4c28d..e9c8b05ee7 100644 --- a/DigitalLearningSolutions.Data/DataServices/CompetencyAssessmentDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/CompetencyAssessmentDataService.cs @@ -1488,8 +1488,10 @@ INNER JOIN AdminUsers au ON fc.AdminID = au.AdminID WHERE fc.FrameworkID = @frameworkId AND fc.IsDeleted = 0 - AND fc.AdminID NOT IN (SELECT AdminID FROM SelfAssessmentCollaborators WHERE SelfAssessmentID = @selfAssessmentId AND IsDeleted = 0) - AND fc.AdminID NOT IN (SELECT CreatedByAdminId FROM SelfAssessmentFrameworks WHERE SelfAssessmentId = @selfAssessmentId AND FrameworkID = @frameworkId AND RemovedDate IS NULL);", + AND NOT EXISTS (SELECT 1 FROM AdminAccounts AS aa JOIN SelfAssessmentCollaborators AS sac ON sac.AdminID = aa.ID + WHERE aa.UserID = au.AdminUserID AND sac.SelfAssessmentID = @selfAssessmentId AND sac.IsDeleted = 0) + AND NOT EXISTS (SELECT 1 FROM AdminAccounts AS aa JOIN SelfAssessments AS sa ON sa.CreatedByAdminID = aa.ID + WHERE aa.UserID = au.AdminUserID AND sa.ID = @selfAssessmentId);", new { selfAssessmentId, frameworkId } ); } From f3bb932b9a4644d87825473e2d1057fe3096fe26 Mon Sep 17 00:00:00 2001 From: Auldrin-Possa Date: Wed, 11 Mar 2026 16:04:13 +0000 Subject: [PATCH 3/7] TD-6835 - Added reviewer role check to prevent editing of competency assessment --- .../CompetencyAssessments.cs | 35 +++--- .../ManagesupervisionViewModel.cs | 16 ++- .../OptionsLabelsViewModel.cs | 4 +- .../WorkingGroupCollaboratorsViewModel.cs | 1 + .../AddCompetenciesSelectFramework.cshtml | 5 +- .../CompetencyAssessmentOptions.cshtml | 103 ++++++++++++------ .../CompetencyAssessmentWorkingGroup.cshtml | 92 +++++++++------- .../CompetencyAssessments/EditBranding.cshtml | 9 +- .../EditDescription.cshtml | 9 +- .../EditRoleProfileLinks.cshtml | 48 +++++--- .../EditVocabulary.cshtml | 9 +- .../ManageCompetencyAssessment.cshtml | 11 +- .../ManageCompetencyRoleRequirements.cshtml | 36 ++++-- .../ManageOptionalCompetencies.cshtml | 42 ++++--- .../ManageSupervisionSettings.cshtml | 83 ++++++++------ .../SelectFrameworkSources.cshtml | 102 +++++++++-------- .../ViewSelectedCompetencies.cshtml | 35 +++--- 17 files changed, 394 insertions(+), 246 deletions(-) diff --git a/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessments.cs b/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessments.cs index e6e0bed7aa..3efda80253 100644 --- a/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessments.cs +++ b/DigitalLearningSolutions.Web/Controllers/CompetencyAssessmentsController/CompetencyAssessments.cs @@ -209,6 +209,7 @@ public IActionResult EditRoleProfileLinks(int competencyAssessmentId = 0, string { competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); } + if (competencyAssessmentBase.UserRole < 2) actionName = "Summary"; var professionalGroups = competencyAssessmentService.GetNRPProfessionalGroups(); var subGroups = competencyAssessmentService.GetNRPSubGroups(competencyAssessmentBase.NRPProfessionalGroupID); var roles = competencyAssessmentService.GetNRPRoles(competencyAssessmentBase.NRPSubGroupID); @@ -878,7 +879,8 @@ public IActionResult AssessmentWorkingGroup(int competencyAssessmentId, string a CompetencyAssessmentTaskStatus = taskStatus.WorkingGroupTaskStatus, UserEmail = null, Error = false, - ActionName = actionName + ActionName = actionName, + UserRole = competencyAssessmentBase.UserRole }; if (TempData["CompetencyAssessmentError"] != null) { @@ -955,6 +957,7 @@ public IActionResult ConfigureOptions(int competencyAssessmentId) data.ReviewerCommentsLabelText = competencyAssessmentBase.ReviewerCommentsLabel?.Trim(); data.IsSupervisionSwitchedOn = competencyAssessmentBase.SupervisorSelfAssessmentReview && competencyAssessmentBase.SupervisorResultsReview; data.IsSignpostedLearning = competencyAssessmentService.HasCompetencyWithSignpostedLearning(competencyAssessmentId); + data.UserRole = competencyAssessmentBase.UserRole; var taskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); data.SelfAssessmentOptionsTaskStatus = taskStatus.SelfAssessmentOptionsTaskStatus; @@ -962,7 +965,7 @@ public IActionResult ConfigureOptions(int competencyAssessmentId) SetOptionsLabelsData(data); var step = (int)OptionLabel.Declaration; - if (taskStatus.SelfAssessmentOptionsTaskStatus != null) + if (taskStatus.SelfAssessmentOptionsTaskStatus != null || competencyAssessmentBase.UserRole < 2) step = (int)OptionLabel.Summary; ValidateStep(data, ref step); @@ -980,6 +983,10 @@ public IActionResult OptionsLabels(int competencyAssessmentId, int step) if (result.StatusCode != 200) return result; + var competencyAssessmentBase = competencyAssessmentService.GetCompetencyAssessmentBaseById(competencyAssessmentId, adminId); + if (competencyAssessmentBase.UserRole < 2) + step = (int)OptionLabel.Summary; + var data = GetOptionsLabelslData(); if (ValidateStep(data, ref step)) @@ -1223,12 +1230,13 @@ public IActionResult SupervisorRoles(int competencyAssessmentId) competencyAssessmentBase.SignOffSupervisorStatement, competencyAssessmentBase.SignOffRequestorStatement, this.config.GetLearnerDefaultText(), - this.config.GetSupervisorDefaultText()); + this.config.GetSupervisorDefaultText(), + competencyAssessmentBase.UserRole); SetManagesupervisionData(model); var competencyAssessmentTaskStatus = competencyAssessmentService.GetCompetencyAssessmentTaskStatus(competencyAssessmentId, null); - if (competencyAssessmentTaskStatus.SupervisorRolesTaskStatus != null) + if (competencyAssessmentTaskStatus.SupervisorRolesTaskStatus != null || competencyAssessmentBase.UserRole < 2) return RedirectToAction("ManageSupervisionSettings", "CompetencyAssessments", new { @@ -1428,17 +1436,20 @@ public IActionResult ManageSupervisionSettings(int competencyAssessmentId, strin if (actionName == "SupervisorRoles") { var model = new ManagesupervisionViewModel(competencyAssessmentId, baseData.CompetencyAssessmentName, - baseData.SupervisorResultsReview, - baseData.SupervisorSelfAssessmentReview, - baseData.SignOffSupervisorStatement, - baseData.SignOffRequestorStatement, - this.config.GetLearnerDefaultText(), - this.config.GetSupervisorDefaultText()); + baseData.SupervisorResultsReview, + baseData.SupervisorSelfAssessmentReview, + baseData.SignOffSupervisorStatement, + baseData.SignOffRequestorStatement, + this.config.GetLearnerDefaultText(), + this.config.GetSupervisorDefaultText(), + baseData.UserRole); + model.TaskCompleteChecked = competencyAssessmentTaskStatus.SupervisorRolesTaskStatus; SetManagesupervisionData(model); return View(model); } var data = GetManagesupervisionData(); + data.UserRole = baseData.UserRole; var dataModel = new ManagesupervisionViewModel(competencyAssessmentId, data, this.config.GetLearnerDefaultText(), this.config.GetSupervisorDefaultText()); dataModel.TaskCompleteChecked = competencyAssessmentTaskStatus.SupervisorRolesTaskStatus; return View(dataModel); @@ -1785,10 +1796,6 @@ private StatusCodeResult ValidateCompetencyAssessmentAndRole(int competencyAsses logger.LogWarning($"Failed to load {pageName} page for competencyAssessmentId: {competencyAssessmentId} adminId: {adminId}"); return StatusCode(500); } - if (competencyAssessmentBase.UserRole < 2) - { - return StatusCode(403); - } } else { diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManagesupervisionViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManagesupervisionViewModel.cs index 9a2b783f6e..ac52e8137e 100644 --- a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManagesupervisionViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/ManagesupervisionViewModel.cs @@ -34,14 +34,16 @@ public ManagesupervisionViewModel(int competencyAssessmentId, SupervisorDeclaration.DefaultText = supervisorDefaultText.Replace("{{CompetencyAssessmentName}}", CompetencyAssessmentName); LearnerDeclaration = model.LearnerDeclaration; LearnerDeclaration.DefaultText = learnerDefaultText.Replace("{{CompetencyAssessmentName}}", CompetencyAssessmentName); + UserRole = model.UserRole; } public ManagesupervisionViewModel(int competencyAssessmentId, string competencyAssessmentName, - bool supervisorResultsReview, - bool SupervisorSelfAssessmentReview, - string? signOffSupervisorStatement, - string? signOffRequestorStatement, - string learnerDefaultText, - string supervisorDefaultText) + bool supervisorResultsReview, + bool SupervisorSelfAssessmentReview, + string? signOffSupervisorStatement, + string? signOffRequestorStatement, + string learnerDefaultText, + string supervisorDefaultText, + int userRole) { CompetencyAssessmentId = competencyAssessmentId; CompetencyAssessmentName = competencyAssessmentName; @@ -58,6 +60,7 @@ public ManagesupervisionViewModel(int competencyAssessmentId, string competencyA LearnerDeclaration.DefaultText = learnerDefaultText.Replace("{{CompetencyAssessmentName}}", CompetencyAssessmentName); LearnerDeclaration.CompetencyAssessmentName = competencyAssessmentName; LearnerDeclaration.CompetencyAssessmentId = competencyAssessmentId; + UserRole = userRole; } public SupervisedSelfAssessmentSignoffViewModel Signoff { get; set; } = new SupervisedSelfAssessmentSignoffViewModel(); public SupervisorSignoffDeclarationViewModel SupervisorDeclaration { get; set; } = new SupervisorSignoffDeclarationViewModel(); @@ -65,6 +68,7 @@ public ManagesupervisionViewModel(int competencyAssessmentId, string competencyA public bool? TaskCompleteChecked { get; set; } public int CompetencyAssessmentId { get; set; } public string CompetencyAssessmentName { get; set; } = string.Empty; + public int UserRole { get; set; } } } diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/OptionsLabelsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/OptionsLabelsViewModel.cs index b1e3cd3a9a..ac83ad5641 100644 --- a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/OptionsLabelsViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/OptionsLabelsViewModel.cs @@ -26,6 +26,7 @@ public OptionsLabelsViewModel(OptionsLabelsViewModel optionsLabels) SelfAssessmentOptionsTaskStatus = optionsLabels.SelfAssessmentOptionsTaskStatus; IsSupervisionSwitchedOn = optionsLabels.IsSupervisionSwitchedOn; IsSignpostedLearning = optionsLabels.IsSignpostedLearning; + UserRole = optionsLabels.UserRole; } public int FrameworkId { get; set; } @@ -47,7 +48,8 @@ public OptionsLabelsViewModel(OptionsLabelsViewModel optionsLabels) public string? Vocabulary { get; set; } public bool? SelfAssessmentOptionsTaskStatus { get; set; } public bool Error { get; set; } - + public int UserRole { get; set; } + } public enum OptionLabel { diff --git a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/WorkingGroupCollaboratorsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/WorkingGroupCollaboratorsViewModel.cs index 6af2e05f64..e4b1a72656 100644 --- a/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/WorkingGroupCollaboratorsViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/CompetencyAssessments/WorkingGroupCollaboratorsViewModel.cs @@ -12,6 +12,7 @@ public class WorkingGroupCollaboratorsViewModel public string? UserEmail { get; set; } public bool Error { get; set; } public string? ActionName { get; set; } + public int UserRole { get; set; } } } diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/AddCompetenciesSelectFramework.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/AddCompetenciesSelectFramework.cshtml index 1bb44950b3..d65981ca12 100644 --- a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/AddCompetenciesSelectFramework.cshtml +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/AddCompetenciesSelectFramework.cshtml @@ -50,5 +50,8 @@ - + @if (Model.UserRole > 1) + { + + } diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentOptions.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentOptions.cshtml index 714c86c0cb..c168d1f2ca 100644 --- a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentOptions.cshtml +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/CompetencyAssessmentOptions.cshtml @@ -43,6 +43,19 @@
  • Manage Competency Assessment
  • @foreach (var item in steps) { + @if (Model.UserRole < 2) + { + + @if (Model.CurrentStep == item.Step) + { +
  • + @item.Text +
  • + break; + } + + continue; + }
  • @if (Model.CurrentStep == item.Step) { @@ -51,7 +64,7 @@ } else { - @item.Text + @item.Text }
  • } @@ -287,11 +300,14 @@
    @(Model.IncludeLearnerDeclarationPrompt ? "Yes" : "No")
    -
    - - Change - -
    + @if (Model.UserRole > 1) + { +
    + + Change + +
    + } } @if (Model.IsSignpostedLearning) @@ -303,11 +319,14 @@
    @(Model.IncludesSignposting ? "Yes" : "No")
    -
    - - Change - -
    + @if (Model.UserRole > 1) + { +
    + + Change + +
    + } }
    @@ -317,11 +336,14 @@
    @(Model.LinearNavigation ? "Yes" : "No")
    -
    - - Change - -
    + @if (Model.UserRole > 1) + { +
    + + Change + +
    + }
    @@ -330,11 +352,14 @@
    @(Model.UseDescriptionExpanders ? "Yes" : "No")
    -
    - - Change - -
    + @if (Model.UserRole > 1) + { +
    + + Change + +
    + }
    @@ -343,11 +368,14 @@
    @(Model.QuestionLabel ? "Yes - " + Model.QuestionLabelText : "No")
    -
    - - Change - -
    + @if (Model.UserRole > 1) + { +
    + + Change + +
    + }
    @if (Model.IsSupervisionSwitchedOn) { @@ -358,11 +386,14 @@
    @(Model.ReviewerCommentsLabel ? "Yes - " + Model.ReviewerCommentsLabelText : "No")
    -
    - - Change - -
    + @if (Model.UserRole > 1) + { +
    + + Change + +
    + } } @@ -376,10 +407,12 @@ Back } - - + @if (Model.UserRole > 1) + { + + }
    - -
    -
    - + @if (Model.UserRole > 1) + { + +
    +
    + -
    - Provide the email address of a user with a registered DLS admin account to add as a Contributor (to help create your competency assessment) or Reviewer (to review your competency assessment and mark it as ready for publication). +
    + Provide the email address of a user with a registered DLS admin account to add as a Contributor (to help create your competency assessment) or Reviewer (to review your competency assessment and mark it as ready for publication). +
    + @if (Model.Error) + { + + Error: @errMsg + + } +
    - @if (Model.Error) +
    + + + + } + + + @if (Model.UserRole > 1) + { +
    + @if (Model.ActionName == "CollaboratorReview") + { + + Done + + } + else { - - Error: @errMsg - + } -
    -
    - - - -
    - @if (Model.ActionName == "CollaboratorReview") - { - - Done - - } - else - { - - } + } + diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditBranding.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditBranding.cshtml index 2230c98c3e..2ea90d43d3 100644 --- a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditBranding.cshtml +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/EditBranding.cshtml @@ -65,9 +65,12 @@ - + @if (Model.UserRole > 1) + { + + } } @if (Model.SubGroupId != null && Model.ActionName == "Summary") @@ -65,11 +71,14 @@
    @Model.RoleName
    -
    - - Change role profile link - -
    + @if (Model.UserRole > 1) + { +
    + + Change role profile link + +
    + }
    } @@ -244,9 +253,12 @@ else if (Model.ActionName == "Summary") - + @if (Model.UserRole > 1) + { + + } }
    @@ -58,9 +61,12 @@ No. Do not include filters. } -
    - Change include requirements filters -
    + @if (Model.UserRole > 1) + { +
    + Change include requirements filters +
    + }
    @@ -69,9 +75,12 @@
    @Model.CountCompetencyRequirements @Model.VocabularySingular.ToLower() assessment questions with role requirements set.
    -
    - Change include requirements filters -
    + @if (Model.UserRole > 1) + { +
    + Change include requirements filters +
    + }
    @@ -81,9 +90,12 @@ - + @if (Model.UserRole > 1) + { + + }
    @if (Model.SelectedCompetencyIds.Any()) { @@ -66,9 +69,12 @@
    @(Model.MinimumOptionalCompetencies == null | Model.MinimumOptionalCompetencies == 0 ? "No minimum set" : Model.MinimumOptionalCompetencies.ToString())
    -
    - Change minimum optional @Model.VocabularyPlural.ToLower() -
    + @if (Model.UserRole > 1) + { +
    + Change minimum optional @Model.VocabularyPlural.ToLower() +
    + }
    @@ -77,9 +83,12 @@
    @Html.Raw(Model.ManageOptionalCompetenciesPrompt == null ? "No learner prompt specified" : Model.ManageOptionalCompetenciesPrompt.ToString())
    -
    - Change learner prompt -
    + @if (Model.UserRole > 1) + { +
    + Change learner prompt +
    + }
    } @@ -92,9 +101,12 @@ - + @if (Model.UserRole > 1) + { + + } @if (@Model.Signoff.SignoffText == "Yes") { @@ -80,16 +86,19 @@
    -
    - -
    + @if (Model.UserRole > 1) + { +
    + +
    + }
    @@ -113,16 +122,19 @@
    -
    - -
    + @if (Model.UserRole > 1) + { +
    + +
    + }
    @@ -145,12 +157,13 @@
    - - + @if (Model.UserRole > 1) + { + + } - Cancel
    diff --git a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SelectFrameworkSources.cshtml b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SelectFrameworkSources.cshtml index 92c65a368d..d811de3e96 100644 --- a/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SelectFrameworkSources.cshtml +++ b/DigitalLearningSolutions.Web/Views/CompetencyAssessments/SelectFrameworkSources.cshtml @@ -50,15 +50,18 @@
    @Model.PrimaryFramework.FrameworkName
    -
    - -
    + @if (Model.UserRole > 1) + { +
    + +
    + }
    } @if (Model.AdditionalFrameworks.Count() > 0) @@ -67,30 +70,33 @@ {
    - Additional framework @(i+1) + Additional framework @(i + 1)
    @Model.AdditionalFrameworks.ElementAt(i).FrameworkName
    -
    - -
    + @if (Model.UserRole > 1) + { +
    + +
    + }
    } } @@ -118,28 +124,34 @@ - + @if (Model.UserRole > 1) + { + + } } @if (Model.ActionName == "Summary") { - @if (Model.Frameworks.Any()) + if (Model.UserRole > 1) { - - Add framework - + @if (Model.Frameworks.Any()) + { + + Add framework + + } +
    + + + + + + + +
    } -
    - - - - - - - -
    } } @{ @@ -57,11 +60,14 @@
    @framework.FrameworkName (@framework.AssessmentFrameworkCompetencyCount @Model.VocabularyPlural.ToLower())
    -
    - - Manage @Model.VocabularyPlural.ToLower() - -
    + @if (Model.UserRole > 1) + { +
    + + Manage @Model.VocabularyPlural.ToLower() + +
    + } i++; } @@ -210,9 +216,12 @@ else - + @if (Model.UserRole > 1) + { + + } diff --git a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/_SupervisorSignOffPanel.cshtml b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/_SupervisorSignOffPanel.cshtml new file mode 100644 index 0000000000..1035780ee8 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/_SupervisorSignOffPanel.cshtml @@ -0,0 +1,77 @@ +@model DigitalLearningSolutions.Web.ViewModels.LearningPortal.SelfAssessments.SelfAssessmentOverviewViewModel +@{ + var latestSignoff = Model.SupervisorSignOffs + .Select(s => s.Verified) + .DefaultIfEmpty(DateTime.MinValue) + .Max(); + var latestResult = Model.CompetencyGroups + .SelectMany(g => g.SelectMany(c => c.AssessmentQuestions)) + .Select(q => q.ResultDateTime) + .DefaultIfEmpty(DateTime.MinValue) + .Max(); + bool signedOff = (from record in Model.SupervisorSignOffs + orderby record.ID descending + select record.SignedOff).FirstOrDefault(); + var competencySummaries = from g in Model.CompetencyGroups + let questions = g.SelectMany(c => c.AssessmentQuestions).Where(q => q.Required) + let selfAssessedCount = questions.Count(q => q.Result.HasValue) + let verifiedCount = questions.Count(q => !((q.Result == null || q.Verified == null || q.SignedOff != true) && q.Required)) + select new ViewDataDictionary(ViewData) + { + { "isSupervisorResultsReviewed", Model.SelfAssessment.IsSupervisorResultsReviewed }, + { "questionsCount", questions.Count() }, + { "selfAssessedCount", selfAssessedCount }, + { "verifiedCount", verifiedCount } + }; +} +
    + +

    @Model.SelfAssessment.SignOffRoleName Sign-off

    + (int)c["questionsCount"]) == competencySummaries.Sum(c => (int)c["verifiedCount"]) }, + { "IsOngoingSelfAssessment", latestResult > latestSignoff }})" /> + @if (Model.AllQuestionsVerifiedOrNotRequired) + { + @if (!Model.SupervisorSignOffs.Any()) + { +

    You have not yet requested @Model.SelfAssessment.SignOffRoleName sign-off for this self assessment.

    + } + else if (!Model.SupervisorSignOffs.Where(x => x.Verified == null).Any() && latestResult > latestSignoff) + { +
    +

    + + New self assessment results + New self assessment results + +

    +

    + You have submitted new self assessment results since this self assessment was signed off. + Please resubmit your self assessment for sign off once these results are confirmed. +

    +
    + } + @if (!Model.SupervisorSignOffs.Where(x => x.Verified == null).Any() + && (latestResult > latestSignoff || !signedOff) + && (Model.NumberOfSelfAssessedOptionalCompetencies >= Model.SelfAssessment.MinimumOptionalCompetencies) + ) + { + + Request @Model.SelfAssessment.SignOffRoleName sign-off + + } + } + else + { +

    + All required @Model.SelfAssessment.Vocabulary.ToLower() self-assessments must be completed and confirmed, + before requesting @Model.SelfAssessment.SignOffRoleName sign off of the @Model.SelfAssessment.Name. +

    + } +
    From 6de14c2b2616e00f8219e9301e05f04e312bf66e Mon Sep 17 00:00:00 2001 From: Rohit Shrivastava Date: Tue, 5 May 2026 09:39:15 +0100 Subject: [PATCH 7/7] TD-7032 Adding Hyphen To Sign Off --- .../SelfAssessments/_SupervisorSignOffPanel.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/_SupervisorSignOffPanel.cshtml b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/_SupervisorSignOffPanel.cshtml index 1035780ee8..43335a9c97 100644 --- a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/_SupervisorSignOffPanel.cshtml +++ b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/_SupervisorSignOffPanel.cshtml @@ -49,7 +49,7 @@

    You have submitted new self assessment results since this self assessment was signed off. - Please resubmit your self assessment for sign off once these results are confirmed. + Please resubmit your self assessment for sign-off once these results are confirmed.

    }