diff --git a/LearningHub.Nhs.WebUI/Models/SideMenu/SideNavViewModel.cs b/LearningHub.Nhs.WebUI/Models/SideMenu/SideNavViewModel.cs
new file mode 100644
index 000000000..28ae8f060
--- /dev/null
+++ b/LearningHub.Nhs.WebUI/Models/SideMenu/SideNavViewModel.cs
@@ -0,0 +1,21 @@
+namespace LearningHub.Nhs.WebUI.Models.SideMenu
+{
+ using System.Collections.Generic;
+ using Microsoft.AspNetCore.Routing;
+
+ ///
+ /// Defines the .
+ ///
+ public class SideNavViewModel
+ {
+ ///
+ /// Gets or sets the Groups.
+ ///
+ public List Groups { get; set; } = [];
+
+ ///
+ /// Gets or sets the RouteData.
+ ///
+ public RouteValueDictionary RouteData { get; set; } = [];
+ }
+}
diff --git a/LearningHub.Nhs.WebUI/Models/SideMenu/SideNavigationConfiguration.cs b/LearningHub.Nhs.WebUI/Models/SideMenu/SideNavigationConfiguration.cs
new file mode 100644
index 000000000..9f95fc173
--- /dev/null
+++ b/LearningHub.Nhs.WebUI/Models/SideMenu/SideNavigationConfiguration.cs
@@ -0,0 +1,114 @@
+namespace LearningHub.Nhs.WebUI.Models.SideMenu
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using Microsoft.AspNetCore.Routing;
+
+ ///
+ /// Defines the .
+ ///
+ public static class SideNavigationConfiguration
+ {
+ ///
+ /// GetGroupedMenus.
+ ///
+ /// IEnumerable.
+ public static IEnumerable GetGroupedMenus()
+ {
+ return new List
+ {
+ new SideNavigationGroup
+ {
+ GroupTitle = "Account",
+ Items = new List
+ {
+ new SideNavigationItem
+ {
+ Text = "Personal details",
+ Controller = "Account",
+ Action = "PersonalDetails",
+ IsActive = route => MatchRoute(route, "Account", "PersonalDetails"),
+ },
+ new SideNavigationItem
+ {
+ Text = "My employment",
+ Controller = "Account",
+ Action = "MyEmployment",
+ IsActive = route => MatchRoute(route, "Account", "MyEmployment"),
+ },
+ new SideNavigationItem
+ {
+ Text = "Security",
+ Controller = "Account",
+ Action = "Security",
+ IsActive = route => MatchRoute(route, "Account", "Security"),
+ },
+ new SideNavigationItem
+ {
+ Text = "Notification",
+ Controller = "Account",
+ Action = "Notification",
+ IsActive = route => MatchRoute(route, "Account", "Notification"),
+ },
+ },
+ },
+ new SideNavigationGroup
+ {
+ GroupTitle = "Activity",
+ Items = new List
+ {
+ new SideNavigationItem
+ {
+ Text = "Recent",
+ Controller = "Activity",
+ Action = "Recent",
+ IsActive = route => MatchRoute(route, "Activity", "Recent"),
+ },
+ new SideNavigationItem
+ {
+ Text = "Bookmark",
+ Controller = "Activity",
+ Action = "Bookmark",
+ IsActive = route => MatchRoute(route, "Activity", "Bookmark"),
+ },
+ new SideNavigationItem
+ {
+ Text = "Certificates",
+ Controller = "Activity",
+ Action = "Certificates",
+ IsActive = route => MatchRoute(route, "Activity", "Certificates"),
+ },
+ new SideNavigationItem
+ {
+ Text = "Learning history",
+ Controller = "Activity",
+ Action = "Learninghistory",
+ IsActive = route => MatchRoute(route, "Activity", "Learninghistory"),
+ },
+ },
+ },
+ };
+ }
+
+ ///
+ /// GetMenuGroupByTitle.
+ ///
+ /// title.
+ /// string.
+ public static SideNavigationGroup? GetMenuGroupByTitle(string title)
+ {
+ return GetGroupedMenus().FirstOrDefault(g =>
+ string.Equals(g.GroupTitle, title, StringComparison.OrdinalIgnoreCase));
+ }
+
+ private static bool MatchRoute(RouteValueDictionary route, string controller, string action)
+ {
+ var currentController = route["controller"]?.ToString();
+ var currentAction = route["action"]?.ToString();
+
+ return string.Equals(currentController, controller, StringComparison.OrdinalIgnoreCase) &&
+ string.Equals(currentAction, action, StringComparison.OrdinalIgnoreCase);
+ }
+ }
+}
diff --git a/LearningHub.Nhs.WebUI/Models/SideMenu/SideNavigationGroup.cs b/LearningHub.Nhs.WebUI/Models/SideMenu/SideNavigationGroup.cs
new file mode 100644
index 000000000..ff35ed8f7
--- /dev/null
+++ b/LearningHub.Nhs.WebUI/Models/SideMenu/SideNavigationGroup.cs
@@ -0,0 +1,20 @@
+namespace LearningHub.Nhs.WebUI.Models.SideMenu
+{
+ using System.Collections.Generic;
+
+ ///
+ /// Defines the .
+ ///
+ public class SideNavigationGroup
+ {
+ ///
+ /// Gets or sets a value indicating GroupTitle.
+ ///
+ public string GroupTitle { get; set; } = string.Empty;
+
+ ///
+ /// Gets or sets a Items.
+ ///
+ public List Items { get; set; } = [];
+ }
+}
diff --git a/LearningHub.Nhs.WebUI/Models/SideMenu/SideNavigationItem.cs b/LearningHub.Nhs.WebUI/Models/SideMenu/SideNavigationItem.cs
new file mode 100644
index 000000000..156e90893
--- /dev/null
+++ b/LearningHub.Nhs.WebUI/Models/SideMenu/SideNavigationItem.cs
@@ -0,0 +1,31 @@
+namespace LearningHub.Nhs.WebUI.Models.SideMenu
+{
+ using System;
+ using Microsoft.AspNetCore.Routing;
+
+ ///
+ /// Defines the .
+ ///
+ public class SideNavigationItem
+ {
+ ///
+ /// Gets or sets a value indicating Text.
+ ///
+ public string Text { get; set; } = string.Empty;
+
+ ///
+ /// Gets or sets a value indicating Controller.
+ ///
+ public string Controller { get; set; } = string.Empty;
+
+ ///
+ /// Gets or sets a value indicating Action.
+ ///
+ public string Action { get; set; } = "Index";
+
+ ///
+ /// Gets or sets a value indicating IsActiven.
+ ///
+ public Func IsActive { get; set; }
+ }
+}
diff --git a/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss b/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss
index 4cb2822fe..79762964b 100644
--- a/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss
+++ b/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss
@@ -254,6 +254,94 @@ button[data-toggle="modal"] {
li.autosuggestion-option:last-of-type {
border-bottom: none !important;
}
+
+
+/* side navigation styles */
+.side-nav__list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ border-right: 1px solid #d8dde0;
+}
+
+.side-nav__item {
+ padding: 12px 16px;
+ border-bottom: 1px solid #d8dde0;
+}
+
+.side-nav__item--active a {
+ font-weight: bold;
+ text-decoration: underline;
+}
+
+.side-nav__toggle,
+.side-nav__close {
+ display: none;
+ background: #e8f0f7;
+ color: #005eb8;
+ border: none;
+ font-size: 16px;
+ padding: 10px 16px;
+ border-radius: 25px;
+ margin: 10px;
+ cursor: pointer;
+}
+
+.menu-icon {
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ width: 20px;
+ height: 20px;
+ box-sizing: border-box;
+ padding: 0;
+ border: 1px solid #d8dde0;
+ border-radius: 50%;
+ background-color: white;
+ transition: background-color 0.3s ease;
+}
+
+
+.side-nav__container {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 80%;
+ max-width: 320px;
+ height: 100%;
+ background-color: #e8f0f7;
+ border-right: 40px solid #005eb8;
+ box-sizing: border-box;
+ transform: translateX(-100%);
+ transition: transform 0.3s ease-in-out;
+ z-index: 1000;
+ box-shadow: 4px 0 10px rgba(0, 0, 0, 0.1);
+ display: flex;
+ flex-direction: column;
+ overflow: visible;
+}
+
+
+.side-nav__close {
+ position: relative;
+ right: -35px;
+ z-index: 2;
+ align-self: flex-end;
+ text-align: right;
+}
+
+
+.nav-toggle:checked + label.side-nav__toggle + .side-nav__container {
+ transform: translateX(0);
+}
+
+
+.nav-toggle:checked + label .menu-icon {
+ transform: rotate(180deg);
+}
+
+
+
/* large desktop */
@media (min-width: px2rem(990)) {
@@ -271,6 +359,26 @@ li.autosuggestion-option:last-of-type {
}
}
+@media (min-width: px2rem(768)) {
+ .nav-toggle,
+ .side-nav__toggle,
+ .side-nav__close {
+ display: none !important;
+ }
+
+ .side-nav__container {
+ position: static;
+ transform: none !important;
+ height: auto;
+ box-shadow: none;
+ display: block;
+ width: auto;
+ background-color: #ffffff;
+ border-right: none;
+ }
+}
+
+
/* small desktop */
@media (max-width: px2rem(989)) {
@@ -495,6 +603,20 @@ li.autosuggestion-option:last-of-type {
.nhsuk-width-container.nhsuk-header__container.app-width-container {
padding-bottom: 0;
}
+
+
+ .side-nav__toggle, .side-nav__close {
+ display: inline-block;
+ }
+
+ .side-nav__close {
+ background-color: transparent;
+ }
+
+ .nav-toggle:checked + label.side-nav__toggle .menu-icon,
+ .nav-toggle:checked + label.side-nav__toggle + .side-nav__container .side-nav__close .menu-icon {
+ background-color: #e8f0f7;
+ }
}
/* mobile */
@@ -596,5 +718,5 @@ li.autosuggestion-option:last-of-type {
.autosuggestion-menu {
top: 100%;
- }
+ }
}
\ No newline at end of file
diff --git a/LearningHub.Nhs.WebUI/ViewComponents/SideNavViewComponent.cs b/LearningHub.Nhs.WebUI/ViewComponents/SideNavViewComponent.cs
new file mode 100644
index 000000000..232e54349
--- /dev/null
+++ b/LearningHub.Nhs.WebUI/ViewComponents/SideNavViewComponent.cs
@@ -0,0 +1,36 @@
+namespace LearningHub.Nhs.WebUI.ViewComponents
+{
+ using System.Collections.Generic;
+ using System.Linq;
+ using LearningHub.Nhs.WebUI.Models.SideMenu;
+ using Microsoft.AspNetCore.Mvc;
+ using Microsoft.AspNetCore.Mvc.Rendering;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public class SideNavViewComponent : ViewComponent
+ {
+ ///
+ /// The Invoke.
+ ///
+ /// group Title.
+ /// A representing the result of the synchronous operation.
+ public IViewComponentResult Invoke(string? groupTitle = null)
+ {
+ var routeData = this.ViewContext.RouteData.Values;
+ var groups = string.IsNullOrEmpty(groupTitle)
+ ? SideNavigationConfiguration.GetGroupedMenus().ToList()
+ : SideNavigationConfiguration.GetMenuGroupByTitle(groupTitle) is { } singleGroup
+ ? new List { singleGroup }
+ : new List();
+
+ var viewModel = new SideNavViewModel
+ {
+ Groups = groups,
+ RouteData = routeData,
+ };
+ return this.View(viewModel);
+ }
+ }
+}
diff --git a/LearningHub.Nhs.WebUI/Views/Shared/Components/SideNav/Default.cshtml b/LearningHub.Nhs.WebUI/Views/Shared/Components/SideNav/Default.cshtml
new file mode 100644
index 000000000..bef06e64d
--- /dev/null
+++ b/LearningHub.Nhs.WebUI/Views/Shared/Components/SideNav/Default.cshtml
@@ -0,0 +1,33 @@
+@using LearningHub.Nhs.WebUI.Models.SideMenu
+@model SideNavViewModel
+
+@foreach (var group in Model.Groups)
+{
+
+
+
+
+
+
+
+
+
+
+
+}