Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,10 @@ public void GetCourseStatisticsAtCentreForCategoryID_should_return_course_statis
DelegateCount = 25,
AllAttempts = 49,
AttemptsPassed = 34,
CompletedCount = 5
CompletedCount = 5,
HideInLearnerPortal = false,
CategoryName = "Office 2007",
LearningMinutes = "N/A"
};

result.Should().HaveCount(260);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,14 @@ public IEnumerable<CourseStatistics> GetCourseStatisticsAtCentreForCategoryId(in
{DelegateCountQuery},
{CompletedCountQuery},
{AllAttemptsQuery},
{AttemptsPassedQuery}
{AttemptsPassedQuery},
cu.HideInLearnerPortal,
cc.CategoryName,
cu.LearningTimeMins AS LearningMinutes
FROM dbo.Customisations AS cu
INNER JOIN dbo.CentreApplications AS ca ON ca.ApplicationID = cu.ApplicationID
INNER JOIN dbo.Applications AS ap ON ap.ApplicationID = ca.ApplicationID
INNER JOIN dbo.CourseCategories AS cc ON cc.CourseCategoryID = ap.CourseCategoryID
WHERE (ap.CourseCategoryID = @categoryId OR @categoryId = 0)
AND (cu.CentreID = @centreId OR (cu.AllCentres = 1 AND ca.Active = 1))
AND ca.CentreID = @centreId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ public class CourseStatistics
public int InProgressCount => DelegateCount - CompletedCount;
public int AllAttempts { get; set; }
public int AttemptsPassed { get; set; }
public bool HideInLearnerPortal { get; set; }
public string CategoryName { get; set; }
public string LearningMinutes { get; set; }

public string CourseName => string.IsNullOrWhiteSpace(CustomisationName)
? ApplicationName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
<None Remove="Scripts\spec\learningMenu\fullscreen.spec.ts" />
<None Remove="Scripts\spec\setCompleteByDateSpec.ts" />
<None Remove="Scripts\supervisor\staffList.ts" />
<None Remove="Scripts\trackingSystem\centreCourseSetup.ts" />
<None Remove="Scripts\trackingSystem\reports.ts" />
</ItemGroup>

Expand Down Expand Up @@ -96,6 +97,7 @@
<TypeScriptCompile Include="Scripts\spec\sort.spec.ts" />
<TypeScriptCompile Include="Scripts\supervisor\staffList.ts" />
<TypeScriptCompile Include="Scripts\trackingSystem\centreAdministrators.ts" />
<TypeScriptCompile Include="Scripts\trackingSystem\centreCourseSetup.ts" />
<TypeScriptCompile Include="Scripts\trackingSystem\reports.ts" />
</ItemGroup>

Expand Down
1 change: 1 addition & 0 deletions DigitalLearningSolutions.Web/Helpers/ConfigHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public static class ConfigHelper
public const string DefaultConnectionStringName = "DefaultConnection";
public const string UnitTestConnectionStringName = "UnitTestConnection";
public const string CurrentSystemBaseUrlName = "CurrentSystemBaseUrl";
public const string AppRootPathName = "AppRootPath";
public const string MapsApiKey = "MapsAPIKey";

public static IConfigurationRoot GetAppConfig()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace DigitalLearningSolutions.Web.Helpers.FilterOptions
{
using DigitalLearningSolutions.Data.Models.Courses;
using DigitalLearningSolutions.Web.Models.Enums;
using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage;

public static class CourseStatusFilterOptions
{
private const string Group = "Status";

public static readonly FilterOptionViewModel IsInactive = new FilterOptionViewModel(
"Inactive",
Group + FilteringHelper.Separator + nameof(CourseStatistics.Active) + FilteringHelper.Separator + "false",
FilterStatus.Warning
);

public static readonly FilterOptionViewModel IsActive = new FilterOptionViewModel(
"Active",
Group + FilteringHelper.Separator + nameof(CourseStatistics.Active) + FilteringHelper.Separator + "true",
FilterStatus.Success
);

public static readonly FilterOptionViewModel IsHiddenInLearningPortal = new FilterOptionViewModel(
"Hidden in Learning Portal",
Group + FilteringHelper.Separator + nameof(CourseStatistics.HideInLearnerPortal) +
FilteringHelper.Separator + "true",
FilterStatus.Warning
);

public static readonly FilterOptionViewModel IsNotHiddenInLearningPortal = new FilterOptionViewModel(
"Visible in Learning Portal",
Group + FilteringHelper.Separator + nameof(CourseStatistics.HideInLearnerPortal) +
FilteringHelper.Separator + "true",
FilterStatus.Success
);
}
}
30 changes: 29 additions & 1 deletion DigitalLearningSolutions.Web/Helpers/FilterableTagHelper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace DigitalLearningSolutions.Web.Helpers
{
using System.Collections.Generic;
using DigitalLearningSolutions.Data.Models.Courses;
using DigitalLearningSolutions.Data.Models.User;
using DigitalLearningSolutions.Web.Helpers.FilterOptions;
using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage;
Expand All @@ -10,7 +11,7 @@ public static class FilterableTagHelper
public static IEnumerable<SearchableTagViewModel> GetCurrentTagsForAdminUser(AdminUser adminUser)
{
var tags = new List<SearchableTagViewModel>();

if (adminUser.IsLocked)
{
tags.Add(new SearchableTagViewModel(AdminAccountStatusFilterOptions.IsLocked));
Expand Down Expand Up @@ -52,5 +53,32 @@ public static IEnumerable<SearchableTagViewModel> GetCurrentTagsForAdminUser(Adm

return tags;
}

public static IEnumerable<SearchableTagViewModel> GetCurrentTagsForCourseStatistics(
CourseStatistics courseStatistics
)
{
var tags = new List<SearchableTagViewModel>();

if (courseStatistics.Active)
{
tags.Add(new SearchableTagViewModel(CourseStatusFilterOptions.IsActive));
}
else
{
tags.Add(new SearchableTagViewModel(CourseStatusFilterOptions.IsInactive));
}

if (courseStatistics.HideInLearnerPortal)
{
tags.Add(new SearchableTagViewModel(CourseStatusFilterOptions.IsHiddenInLearningPortal));
}
else
{
tags.Add(new SearchableTagViewModel(CourseStatusFilterOptions.IsNotHiddenInLearningPortal));
}

return tags;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const copyCourseLinkClass = 'copy-course-button';
const copyLinkIdPrefix = 'copy-course-';
const launchCourseButtonIdPrefix = 'launch-course-';

setUpCourseLinkClipboardCopiers();

function setUpCourseLinkClipboardCopiers() {
const copyCourseLinks = Array.from(document.getElementsByClassName(copyCourseLinkClass));

copyCourseLinks.forEach(
(currentLink) => {
const linkId = currentLink.id;
const customisationId = linkId.slice(copyLinkIdPrefix.length);
currentLink.addEventListener('click', () => copyLaunchCourseLinkToClipboard(customisationId));
},
);
}

function copyLaunchCourseLinkToClipboard(customisationId: string) {
const launchCourseButtonId = launchCourseButtonIdPrefix + customisationId;
const launchCourseButton = document.getElementById(launchCourseButtonId) as HTMLAnchorElement;
const link = launchCourseButton.href;
const succeeded = copyTextToClipboard(link);
const alertMessage = succeeded
? `Copied the text: ${link}`
: `Copy not supported or blocked. Try manually selecting and copying: ${link}`;

alert(alertMessage);
}

function copyTextToClipboard(textToCopy: string): boolean {
try {
navigator.clipboard.writeText(textToCopy);
return true;
} catch (e) {
return copyTextToClipboardFallback(textToCopy);
}
}

function copyTextToClipboardFallback(textToCopy: string): boolean {
const hiddenInput = document.body.appendChild(document.createElement('input'));
hiddenInput.value = textToCopy;
hiddenInput.select();
hiddenInput.setSelectionRange(0, textToCopy.length);
let succeeded: boolean;

try {
succeeded = document.execCommand('copy');
} catch (e) {
succeeded = false;
}

document.body.removeChild(hiddenInput);
return succeeded;
}
8 changes: 8 additions & 0 deletions DigitalLearningSolutions.Web/Styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ ul > li > ul > li {
display: block;
}

.js-only-inline {
display: none;
}

.js-enabled .js-only-inline {
display: inline;
}

.small-edit-button, a.small-edit-button {
margin-bottom: nhsuk-spacing(2);
margin-top: nhsuk-spacing(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,3 @@
.hidden {
display: none !important;
}

.js-only-inline {
display: none;
}

.js-enabled .js-only-inline {
display: inline;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
@import "~nhsuk-frontend/packages/core/all";
@import "../shared/cardWithButtons";
@import "../shared/searchableElements/searchableElements";

.copy-course-button{
background: none;
border: none;
padding: 0;
color: $nhsuk-link-color;
@extend .nhsuk-u-font-size-19;
text-decoration: underline;

&:hover{
color: $nhsuk-link-hover-color;
text-decoration: none;
cursor: pointer;
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,39 @@
namespace DigitalLearningSolutions.Web.ViewModels.TrackingSystem.CourseSetup
{
using System;
using DigitalLearningSolutions.Data.Models.Courses;
using DigitalLearningSolutions.Web.Helpers;
using DigitalLearningSolutions.Web.ViewModels.Common.SearchablePage;

public class SearchableCourseStatisticsViewModel
public class SearchableCourseStatisticsViewModel : BaseFilterableViewModel
{
public SearchableCourseStatisticsViewModel(CourseStatistics courseStatistics)
{
CustomisationId = courseStatistics.CustomisationId;
CentreId = courseStatistics.CentreId;
Active = courseStatistics.Active;
DelegateCount = courseStatistics.DelegateCount;
CompletedCount = courseStatistics.CompletedCount;
InProgressCount = courseStatistics.InProgressCount;
PassRate = courseStatistics.PassRate;
CourseName = courseStatistics.CourseName;
CategoryName = courseStatistics.CategoryName;
LearningMinutes = courseStatistics.LearningMinutes;
Tags = FilterableTagHelper.GetCurrentTagsForCourseStatistics(courseStatistics);
}

public int CustomisationId { get; set; }
public int CentreId { get; set; }
public bool Active { get; set; }
public int DelegateCount { get; set; }
public int CompletedCount { get; set; }
public int InProgressCount { get; set; }
public string CourseName { get; set; }
public double PassRate { get; set; }
public string CategoryName { get; set; }
public string LearningMinutes { get; set; }

public string EmailHref => GenerateEmailHref();

private string GenerateEmailHref()
{
var launchCourseLink =
$"{ConfigHelper.GetAppConfig()[ConfigHelper.AppRootPathName]}/LearningMenu/{CustomisationId}";
var subject = Uri.EscapeDataString($"Digital Learning Solutions Course Link - {CourseName}");
var content = Uri.EscapeDataString($"To start your {CourseName} course, go to {launchCourseLink}");
return $"mailto:?subject={subject}&body={content}";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
@model CourseSetupViewModel

<link rel="stylesheet" href="@Url.Content("~/css/trackingSystem/courseSetup.css")" asp-append-version="true">

@{
ViewData["Title"] = "Centre course setup";
ViewData["Application"] = "Tracking System";
Expand All @@ -25,10 +24,14 @@
} else {
<div id="searchable-elements">
@foreach (var course in Model.Courses) {
<partial name="_CentreCourseCard" model="course"/>
<partial name="_CentreCourseCard" model="course" />
}
</div>
}

</div>
</div>

@section scripts {
<script src="@Url.Content("~/js/trackingSystem/centreCourseSetup.js")" asp-append-version="true"></script>
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,57 @@
@Model.CourseName
</span>
</summary>

<div class="nhsuk-details__text">
<partial name="SearchablePage/_FilterableTags" model="@Model.Tags" />

<dl class="nhsuk-summary-list">
<div class="nhsuk-summary-list__row">
<dt class="nhsuk-summary-list__key">
Category
</dt>
<dd class="nhsuk-summary-list__value">
@Model.CategoryName
</dd>
</div>

<div class="nhsuk-summary-list__row">
<dt class="nhsuk-summary-list__key">
Learning minutes
</dt>
<dd class="nhsuk-summary-list__value">
@Model.LearningMinutes
</dd>
</div>

<div class="nhsuk-summary-list__row">
<dt class="nhsuk-summary-list__key">
Total delegates
</dt>
<dd class="nhsuk-summary-list__value">
@Model.DelegateCount
</dd>
</div>

<div class="nhsuk-summary-list__row">
<dt class="nhsuk-summary-list__key">
In progress
</dt>
<dd class="nhsuk-summary-list__value">
@Model.InProgressCount
</dd>
</div>

</dl>

<p class="nhsuk-u-margin-bottom-0">Want to share the course?</p>
<a href="@Model.EmailHref">Generate email</a>
<button class="js-only-block copy-course-button nhsuk-link--no-visited-state"
id="copy-course-@Model.CustomisationId"
role="button">
Copy course link
</button>
<p class="non-js-only">To share this course with others, click Launch Course and copy the URL to the clipboard.</p>

</div>
</details>
Expand All @@ -17,6 +67,7 @@
<div class="nhsuk-grid-column-full nhsuk-u-margin-left-4">
<a class="nhsuk-button nhsuk-u-margin-right-2 nhsuk-u-margin-bottom-2"
role="button"
id="launch-course-@Model.CustomisationId"
asp-controller="LearningMenu"
asp-action="Index"
asp-route-customisationId="@Model.CustomisationId">
Expand Down