diff --git a/app/assets/stylesheets/base/breakpoints.scss b/app/assets/stylesheets/base/breakpoints.scss
index 3b80daf5cf..180daab2ff 100644
--- a/app/assets/stylesheets/base/breakpoints.scss
+++ b/app/assets/stylesheets/base/breakpoints.scss
@@ -1,4 +1,6 @@
$small: 640px;
$medium: 1024px;
-$mobile: $medium;
-$large: 1200px;
\ No newline at end of file
+$large: 1200px;
+
+// If you adjust this breakpoint, you also need to adjust the breakpoint value in app/javascript/controllers/sidebar-controller.js
+$mobile: 768px;
diff --git a/app/assets/stylesheets/base/variables.scss b/app/assets/stylesheets/base/variables.scss
index db380c5fdb..ab60d692c9 100644
--- a/app/assets/stylesheets/base/variables.scss
+++ b/app/assets/stylesheets/base/variables.scss
@@ -1,2 +1,7 @@
$primary: #00447c;
$red: #dc3545;
+
+// ======== Sidebar colors
+$sidebar-inactive: #9AA4CA;
+$sidebar-active: #365CF5;
+$sidebar-dark: #1A2142;
diff --git a/app/assets/stylesheets/shared/sidebar.scss b/app/assets/stylesheets/shared/sidebar.scss
index 1ee4ca688c..b7a044a55d 100644
--- a/app/assets/stylesheets/shared/sidebar.scss
+++ b/app/assets/stylesheets/shared/sidebar.scss
@@ -53,6 +53,37 @@
overflow-y: auto;
}
+.logo-div {
+ height: 80px;
+}
+
+// Stops weird text wrapping when opening and closing sidebar
+.nav-item {
+ width: 250px;
+}
+
+.nav-item > a {
+ color: globals.$sidebar-inactive !important;
+ height: 44px;
+
+ &:hover {
+ color: globals.$sidebar-active !important;
+
+ span {
+ color: globals.$sidebar-dark !important;
+ }
+ }
+}
+
+.nav-item.active > a {
+ color: globals.$sidebar-dark !important;
+}
+
+.btn-sidebar {
+ color: #fff !important;
+ background-color: globals.$sidebar-active !important;
+}
+
.sidebar-wrapper .sidebar-menu {
margin-top: 24px;
.list-group-item {
@@ -151,3 +182,32 @@
margin-top: 72px;
}
}
+
+// These overrides are for larger than mobile screens
+@media only screen and (min-width: screen-sizes.$mobile) {
+ .sidebar-nav-wrapper {
+ -webkit-transform: translateX(0px) !important;
+ -moz-transform: translateX(0px) !important;
+ -ms-transform: translateX(0px) !important;
+ -o-transform: translateX(0px) !important;
+ transform: translateX(0px) !important;
+
+ &.active {
+ width: 66px;
+
+ -webkit-transform: translateX(0px) !important;
+ -moz-transform: translateX(0px) !important;
+ -ms-transform: translateX(0px) !important;
+ -o-transform: translateX(0px) !important;
+ transform: translateX(0px) !important;
+ }
+ }
+
+ .main-wrapper {
+ margin-left: 250px !important;
+
+ &.active {
+ margin-left: 66px !important;
+ }
+ }
+}
diff --git a/app/components/sidebar/group_component.html.erb b/app/components/sidebar/group_component.html.erb
new file mode 100644
index 0000000000..017193d822
--- /dev/null
+++ b/app/components/sidebar/group_component.html.erb
@@ -0,0 +1,21 @@
+
+
+
+
+
+ <%= @title %>
+
+
+
diff --git a/app/components/sidebar/group_component.rb b/app/components/sidebar/group_component.rb
new file mode 100644
index 0000000000..db75ef20d7
--- /dev/null
+++ b/app/components/sidebar/group_component.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class Sidebar::GroupComponent < ViewComponent::Base
+ renders_many :links, Sidebar::LinkComponent
+
+ # @param title [String] the title/label for the link
+ # @param icon [String] the lni icon, pass just the name of the icon (ie. for lni-star --> icon: "star")
+ # @param render_check [Boolean] whether or not to display the link
+ def initialize(title:, icon:, render_check: true)
+ @title = title
+ @icon = icon
+ @render_check = render_check
+ @identifier = title.downcase.tr(" ", "-")
+ @class = "#{@identifier} collapsed"
+ end
+
+ # If there are no links or all links fail their render_check, then don't render this group
+ # @return [Boolean]
+ def render?
+ @render_check && !links.empty? && !links.select(&:render?).empty?
+ end
+end
diff --git a/app/components/sidebar/link_component.html.erb b/app/components/sidebar/link_component.html.erb
new file mode 100644
index 0000000000..e1907d1c74
--- /dev/null
+++ b/app/components/sidebar/link_component.html.erb
@@ -0,0 +1,8 @@
+
+ <%= link_to @path do %>
+ <% if @icon %>
+
+ <% end %>
+
+ <% end %>
+
diff --git a/app/components/sidebar/link_component.rb b/app/components/sidebar/link_component.rb
new file mode 100644
index 0000000000..d1f0f745b8
--- /dev/null
+++ b/app/components/sidebar/link_component.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class Sidebar::LinkComponent < ViewComponent::Base
+ include SidebarHelper
+
+ # @param title [String] the title/label for the link
+ # @param path [String] the path to navigate to
+ # @param icon [String] the lni icon, pass just the name of the icon (ie. for lni-star --> icon: "star")
+ # @param nav_item [Boolean] whether or not the link should have the nav-item class
+ # @param render_check [Boolean] whether or not to display the link
+ def initialize(title:, path:, icon: nil, nav_item: true, render_check: true)
+ @title = title
+ @icon = icon
+ @path = path
+ @nav_item = nav_item
+ @render_check = render_check
+ end
+
+ # Must be moved to this method in order to use the SidebarHelper
+ def before_render
+ @class = @nav_item ? "nav-item #{active_class(@path)}" : ""
+ end
+
+ # @return [Boolean]
+ def render?
+ @render_check
+ end
+end
diff --git a/app/helpers/sidebar_helper.rb b/app/helpers/sidebar_helper.rb
index 98385042a1..e4be743388 100644
--- a/app/helpers/sidebar_helper.rb
+++ b/app/helpers/sidebar_helper.rb
@@ -15,10 +15,6 @@ def menu_item(label:, path:, visible: false)
link_to label, path, class: "list-group-item #{active_class(path)}" if visible
end
- def get_case_contact_link(casa_case)
- case_contacts_path(casa_case_id: casa_case.id)
- end
-
private # private doesn't work in modules. It's here for semantic purposes
def active_class(link_path)
diff --git a/app/javascript/application.js b/app/javascript/application.js
index c661eaf523..234037cf49 100644
--- a/app/javascript/application.js
+++ b/app/javascript/application.js
@@ -23,13 +23,11 @@ require('./src/dashboard')
require('./src/emancipations')
require('./src/import')
require('./src/password_confirmation')
-require('./src/plainadmin')
require('./src/read_more')
require('./src/reimbursements')
require('./src/reports')
require('./src/require_communication_preference')
require('./src/select')
-require('./src/sidebar')
require('./src/tooltip')
require('./src/time_zone')
require('./src/session_timeout_poller.js')
diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js
index 1edcefed63..78f16b4556 100644
--- a/app/javascript/controllers/index.js
+++ b/app/javascript/controllers/index.js
@@ -5,11 +5,19 @@
import { application } from './application'
import DismissController from './dismiss_controller'
+
import ExtendedNestedFormController from './extended_nested_form_controller'
+
import HelloController from './hello_controller'
-import RevealController from 'stimulus-reveal-controller'
+import NavbarController from './navbar_controller'
+
+import SidebarController from './sidebar_controller'
+
+import SidebarGroupController from './sidebar_group_controller'
application.register('dismiss', DismissController)
application.register('extended-nested-form', ExtendedNestedFormController)
application.register('hello', HelloController)
-application.register('reveal', RevealController)
+application.register('navbar', NavbarController)
+application.register('sidebar', SidebarController)
+application.register('sidebar-group', SidebarGroupController)
diff --git a/app/javascript/controllers/navbar_controller.js b/app/javascript/controllers/navbar_controller.js
new file mode 100644
index 0000000000..7588a97d6b
--- /dev/null
+++ b/app/javascript/controllers/navbar_controller.js
@@ -0,0 +1,10 @@
+import { Controller } from '@hotwired/stimulus'
+
+export default class extends Controller {
+ static outlets = ['sidebar']
+
+ click () {
+ // This simulates a click action on the sidebar-controller
+ this.sidebarOutlet.click()
+ }
+}
diff --git a/app/javascript/controllers/sidebar_controller.js b/app/javascript/controllers/sidebar_controller.js
new file mode 100644
index 0000000000..b0bfa3fc41
--- /dev/null
+++ b/app/javascript/controllers/sidebar_controller.js
@@ -0,0 +1,61 @@
+import { Controller } from '@hotwired/stimulus'
+
+export default class extends Controller {
+ static targets = ['sidebar', 'menu', 'logo', 'linkTitle', 'groupList']
+ static values = {
+ open: Boolean,
+ breakpoint: { type: Number, default: 768 }
+ }
+
+ static outlets = ['sidebar-group']
+
+ click () {
+ this.openValue = !this.openValue
+ this.toggleSidebar()
+ if (this.isNotMobile()) {
+ this.toggleLinks()
+ const mainWrapper = document.querySelector('.main-wrapper')
+ mainWrapper.classList.toggle('active')
+ } else {
+ this.toggleOverlay()
+ }
+ }
+
+ hoverOn () {
+ this.toggleHover()
+ }
+
+ hoverOff () {
+ this.toggleHover()
+ }
+
+ toggleHover () {
+ if (!this.openValue && this.isNotMobile()) {
+ this.toggleSidebar()
+ this.toggleLinks()
+ }
+ }
+
+ toggleSidebar () {
+ this.sidebarTarget.classList.toggle('active')
+ }
+
+ toggleLinks () {
+ this.linkTitleTargets.forEach((target) => {
+ target.classList.toggle('d-none')
+ })
+ this.groupListTargets.forEach((list) => {
+ list.classList.toggle('nav-item-has-children')
+ })
+ this.logoTarget.classList.toggle('d-none')
+ }
+
+ toggleOverlay () {
+ const overlay = document.querySelector('.overlay')
+ overlay.classList.toggle('active')
+ }
+
+ isNotMobile () {
+ return window.innerWidth >= this.breakpointValue
+ }
+}
diff --git a/app/javascript/controllers/sidebar_group_controller.js b/app/javascript/controllers/sidebar_group_controller.js
new file mode 100644
index 0000000000..dbadb2356d
--- /dev/null
+++ b/app/javascript/controllers/sidebar_group_controller.js
@@ -0,0 +1,19 @@
+import { Controller } from '@hotwired/stimulus'
+
+export default class extends Controller {
+ static targets = ['title', 'list', 'link']
+
+ connect () {
+ this.toggleShow()
+ }
+
+ // Expands list if a link is active
+ toggleShow () {
+ this.linkTargets.forEach((link) => {
+ if (link.classList.contains('active')) {
+ this.titleTarget.classList.remove('collapsed')
+ this.listTarget.classList.add('show')
+ }
+ })
+ }
+}
diff --git a/app/javascript/src/plainadmin.js b/app/javascript/src/plainadmin.js
deleted file mode 100644
index b90adb662e..0000000000
--- a/app/javascript/src/plainadmin.js
+++ /dev/null
@@ -1,37 +0,0 @@
-(function () {
- /* ========= sidebar toggle ======== */
- const sidebarNavWrapper = document.querySelector('.sidebar-nav-wrapper')
- const mainWrapper = document.querySelector('.main-wrapper')
- const menuToggleButton = document.querySelector('#menu-toggle')
- const menuToggleButtonIcon = document.querySelector('#menu-toggle i')
- const overlay = document.querySelector('.overlay')
-
- if (menuToggleButton) {
- menuToggleButton.addEventListener('click', () => {
- sidebarNavWrapper.classList.toggle('active')
- overlay.classList.add('active')
- mainWrapper.classList.toggle('active')
-
- if (document.body.clientWidth > 1200) {
- if (menuToggleButtonIcon.classList.contains('lni-chevron-left')) {
- menuToggleButtonIcon.classList.remove('lni-chevron-left')
- menuToggleButtonIcon.classList.add('lni-menu')
- } else {
- menuToggleButtonIcon.classList.remove('lni-menu')
- menuToggleButtonIcon.classList.add('lni-chevron-left')
- }
- } else {
- if (menuToggleButtonIcon.classList.contains('lni-chevron-left')) {
- menuToggleButtonIcon.classList.remove('lni-chevron-left')
- menuToggleButtonIcon.classList.add('lni-menu')
- }
- }
- })
- }
-
- overlay?.addEventListener('click', () => {
- sidebarNavWrapper.classList.remove('active')
- overlay.classList.remove('active')
- mainWrapper.classList.remove('active')
- })
-})()
diff --git a/app/javascript/src/sidebar.js b/app/javascript/src/sidebar.js
deleted file mode 100644
index 460b122ebe..0000000000
--- a/app/javascript/src/sidebar.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/* global $ */
-
-function toggleSidebar () {
- const isOpen = $('#sidebar-js').hasClass('sidebar-open')
- if (isOpen) {
- $('#sidebar-js').removeClass('sidebar-open')
- } else {
- $('#sidebar-js').addClass('sidebar-open')
- }
-}
-
-$(() => { // JQuery's callback for the DOM loading
- $('#toggle-sidebar-js, #sidebar-js').on('click', toggleSidebar)
-
- // Show group actions dropdown expanded when any of the child is active
- if ($('#ddmenu_55 li').children('a').hasClass('active')) {
- $('#ddmenu_55').addClass('show')
- } else {
- $('#ddmenu_55').removeClass('show')
- }
-})
diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb
index 86391d74b9..0dea4889d0 100644
--- a/app/views/layouts/_header.html.erb
+++ b/app/views/layouts/_header.html.erb
@@ -4,15 +4,9 @@
-
+
diff --git a/app/views/layouts/_other_duties_nav_item.html.erb b/app/views/layouts/_other_duties_nav_item.html.erb
deleted file mode 100644
index 8bdd7e43b8..0000000000
--- a/app/views/layouts/_other_duties_nav_item.html.erb
+++ /dev/null
@@ -1,6 +0,0 @@
-
- <%= link_to other_duties_path do %>
-
- Other Duties
- <% end %>
-
diff --git a/app/views/layouts/_sidebar.html.erb b/app/views/layouts/_sidebar.html.erb
index 2824e56923..fcd04bc085 100644
--- a/app/views/layouts/_sidebar.html.erb
+++ b/app/views/layouts/_sidebar.html.erb
@@ -1,211 +1,69 @@
-