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
23 changes: 14 additions & 9 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
## _Merging this PR_
- use the squash-merge strategy for PRs targeting a release-X branch
- use a merge-commit or rebase strategy for PRs targeting the stable branch
- use the squash-merge strategy for PRs targeting `main`
- use a merge-commit or rebase strategy for PRs targeting `staging` and `production`

## Description
[//]: # (Summarize changes and include links related issue. List any new dependencies)
[//]: # (Summarize changes and include links related issue)
[//]: # (List any new dependencies or relevant ADRs)

## Type of change
[//]: # (e.g., Bug fix, New feature, Code clean-up, Dependency update)
[//]: # (e.g., Bug fix, New feature, Documentation, Code clean-up, Dependency update)

## Checklist before requesting review
[//]: # (Remove any items that are not applicable)
- [ ] I have performed a self-review of my code
- [ ] I have run the code that is being changed under ideal conditions, and it doesn't fail
- [ ] I have updated the documentation (or not applicable)
- [ ] I have added spec tests (or not applicable)
- [ ] I have provided testing instructions in this PR or the related issue (or not applicable)
- [ ] I performed a self-review of my code
- [ ] I ran the OP review skill
- [ ] I ran the code that is being changed under ideal conditions, and it doesn't fail
- [ ] Any major architectural changes are supported by an approved ADR (Architectural Decision Record)
- [ ] I updated the documentation (or not applicable)
- [ ] I added spec tests (or not applicable)
- [ ] I provided testing instructions in this PR or the related issue (or not applicable)

[//]: # (NOTE: system tests may fail if there is no branch on the hmis-frontend that matches the Source or Target branch of this PR. This is expected)
8 changes: 8 additions & 0 deletions .overcommit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ PreCommit:
exclude:
- 'db/schema.rb' # Ignore trailing whitespace in generated files
- 'db/structure.rb'
PrePush:
ProtectedBranches:
enabled: true
destructive_only: false # false = block all pushes
branches:
- main
- production
- staging
#
#PostCheckout:
# ALL: # Special hook name that customizes all hooks of this type
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,5 @@ group :test do
gem 'rails-controller-testing'
gem 'rspec-mocks'
gem 'shoulda-matchers'
gem 'webmock'
end
10 changes: 10 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ GEM
coffee-script-source (1.12.2)
concurrent-ruby (1.3.8)
connection_pool (3.0.2)
crack (1.0.1)
bigdecimal
rexml
crass (1.0.7)
csv (3.3.5)
daemons (1.4.1)
Expand Down Expand Up @@ -695,6 +698,10 @@ GEM
actionview (>= 8.0.0)
bindex (>= 0.4.0)
railties (>= 8.0.0)
webmock (3.26.2)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
websocket-driver (0.8.2)
base64
websocket-extensions (>= 0.1.0)
Expand Down Expand Up @@ -861,6 +868,7 @@ DEPENDENCIES
virtus
warning
web-console
webmock
whenever
with_advisory_lock
xlsxtream
Expand Down Expand Up @@ -950,6 +958,7 @@ CHECKSUMS
coffee-script-source (1.12.2) sha256=e12b16fd8927fbbf8b87cb2e9a85a6cf457c6881cc7ff8b1af15b31f70da07a4
concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
crass (1.0.7) sha256=94868719948664c89ddcaf0a37c65048413dfcb1c869470a5f7a7ceb5390b295
csv (3.3.5) sha256=6e5134ac3383ef728b7f02725d9872934f523cb40b961479f69cf3afa6c8e73f
daemons (1.4.1) sha256=8fc76d76faec669feb5e455d72f35bd4c46dc6735e28c420afb822fac1fa9a1d
Expand Down Expand Up @@ -1133,6 +1142,7 @@ CHECKSUMS
warden (1.2.9) sha256=46684f885d35a69dbb883deabf85a222c8e427a957804719e143005df7a1efd0
warning (1.5.0) sha256=0f12c49fea0c06757778eefdcc7771e4fd99308901e3d55c504d87afdd718c53
web-console (4.3.0) sha256=e13b71301cdfc2093f155b5aa3a622db80b4672d1f2f713119cc7ec7ac6a6da4
webmock (3.26.2) sha256=774556f2ea6371846cca68c01769b2eac0d134492d21f6d0ab5dd643965a4c90
websocket-driver (0.8.2) sha256=97c556b019bf3410b4961002ac501621e9322d3f8a7bc02161a09301cc4c4146
websocket-extensions (0.1.5) sha256=1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241
whenever (1.1.2) sha256=af139a25cf6b5c7d2122686724c0b9a4a7495135a1f79a404e29f4cce48abd83
Expand Down
44 changes: 38 additions & 6 deletions app/assets/javascripts/site_menu.js.coffee
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
$ ->

# The drawer menu (see objects/_menu.scss) is fixed-position and needs to
# start below the header, but the non-production/impersonation banners
# above the header (layouts/_header_warnings.haml) vary in height, and
# neither the banners nor the header are fixed/sticky — they scroll away
# with the page. So the offset isn't a constant: it's the banner+header
# height at rest, shrinking to 0 as the page scrolls past where they were,
# otherwise the drawer would leave a gap where they used to be.
restingMenuOffsetTop = 0

measureRestingMenuOffsetTop = ->
header = document.querySelector('.o-header--page')
return unless header
restingMenuOffsetTop = header.getBoundingClientRect().bottom + window.scrollY

applyMenuOffsetTop = ->
offset = Math.max(0, restingMenuOffsetTop - window.scrollY)
document.documentElement.style.setProperty('--nav-side-offset-top', "#{offset}px")

measureRestingMenuOffsetTop()
applyMenuOffsetTop()
$(window).on 'resize', ->
measureRestingMenuOffsetTop()
applyMenuOffsetTop()

# Toggle menu (move on/off canvas) on small screens
$('.js-toggle-menu').on 'click', (event) ->
event.preventDefault()
Expand All @@ -11,11 +35,19 @@ $ ->
$('body,html').animate { scrollTop: 0 }, 500

lastPoint = 0
scrollTicking = false
$(window).scroll ->
action = 'removeClass'
scrollY = @scrollY
if scrollY > @innerHeight && lastPoint > scrollY
action = 'addClass'
return if scrollTicking
scrollTicking = true
window.requestAnimationFrame ->
scrollTicking = false

applyMenuOffsetTop()

action = 'removeClass'
scrollY = window.scrollY
if scrollY > window.innerHeight && lastPoint > scrollY
action = 'addClass'

$('.js-back-to-top')[action]('active')
lastPoint = scrollY
$('.js-back-to-top')[action]('active')
lastPoint = scrollY
5 changes: 4 additions & 1 deletion app/assets/stylesheets/layouts/_main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
transition: .35s max-width ease-in-out;
max-width: 100%;
flex: 0 0 100%;
@include media-breakpoint-up(lg) {
// Give back the width the on-canvas menu takes. Must stay in lockstep with
// the drawer breakpoint in objects/_menu.scss — see $nav-side-on-canvas in
// _settings/_variables.scss.
@include media-breakpoint-up($nav-side-on-canvas) {
flex: 0 0 calc(100vw - #{$nav-side-width});
max-width: calc(100vw - #{$nav-side-width});
}
Expand Down
4 changes: 3 additions & 1 deletion app/assets/stylesheets/modules/_header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
padding: space(4);
margin-right: rem(-16px);
font-size: rem(20px);
@include media-breakpoint-up(lg) {
// Hide the drawer toggle once the menu is on-canvas. See
// $nav-side-on-canvas in _settings/_variables.scss.
@include media-breakpoint-up($nav-side-on-canvas) {
display: none;
}
}
Expand Down
15 changes: 11 additions & 4 deletions app/assets/stylesheets/objects/_menu.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,21 @@
z-index: $zIndex-4;
}

@include media-breakpoint-down(md) {
// Below this breakpoint the menu is a drawer. See $nav-side-on-canvas in
// _settings/_variables.scss before changing this.
@include media-breakpoint-down($nav-side-on-canvas) {
width: 45vw;
position: fixed;
right: 0;
top: 0;
// Falls back to $header-height until JS measures the real offset — the
// non-production/impersonation banners above the header (see
// layouts/_header_warnings.haml) vary in height. See site_menu.js.coffee.
top: var(--nav-side-offset-top, #{$header-height});
bottom: 0;
padding-top: calc(#{$header-height} + #{space(6)});
min-height: 100vh;
// Overrides the base rule's min-height: 100% — once fixed, that resolves
// against the viewport and silently overrides `top` above.
min-height: 0;
padding-top: space(6);
transition: 0.35s transform ease-in-out;

&.off-canvas {
Expand Down
6 changes: 6 additions & 0 deletions app/assets/stylesheets/settings/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ $header-height: 60px;
$nav-side-width: 185px;
$nav-side-collapsed-width: 65px;

// Breakpoint at and above which the side menu sits on-canvas (always visible,
// no toggle); below it the menu is a drawer. Used with media-breakpoint-down()
// in objects/_menu.scss and media-breakpoint-up() in layouts/_main.scss and
// modules/_header.scss so the three cannot drift apart.
$nav-side-on-canvas: lg;

// Footer
$footer-logo-height: 85px;
$footer-logo-padding: 15px;
Expand Down
50 changes: 50 additions & 0 deletions app/controllers/admin/match_decision_reasons_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
###
# Copyright Green River Data Group, Inc.
#
# License detail: https://github.com/greenriver/boston-cas/blob/stable/LICENSE.md
###

# frozen_string_literal: true

module Admin
class MatchDecisionReasonsController < ApplicationController
before_action :require_can_manage_config!
before_action :load_match_decision_reason, only: [:edit, :update]

def index
@match_decision_reasons = MatchDecisionReasons::Base.order(:name)
end

def new
@match_decision_reason = MatchDecisionReasons::Base.new
end

def create
@match_decision_reason = MatchDecisionReasons::Base.new(match_decision_reason_params)
if @match_decision_reason.save
redirect_to admin_match_decision_reasons_path, notice: 'Reason created.'
else
render :new
end
end

def edit
end

def update
if @match_decision_reason.update(match_decision_reason_params)
redirect_to admin_match_decision_reasons_path, notice: 'Reason updated.'
else
render :edit
end
end

private def load_match_decision_reason
@match_decision_reason = MatchDecisionReasons::Base.find(params[:id])
end

private def match_decision_reason_params
params.require(:match_decision_reason).permit(:name, :referral_result, :active)
end
end
end
46 changes: 46 additions & 0 deletions app/controllers/admin/match_decision_steps_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
###
# Copyright Green River Data Group, Inc.
#
# License detail: https://github.com/greenriver/boston-cas/blob/stable/LICENSE.md
###

# frozen_string_literal: true

module Admin
class MatchDecisionStepsController < ApplicationController
include Admin::ManagesMatchDecisionReasonAssignments

before_action :require_can_manage_config!
before_action :load_route
before_action :load_step

def edit
@rows = match_decision_reason_rows(route: @route, decision_type: @step.decision_type)
end

def update
@step.update(default_referral_result_params)
kinds = @step.supports_declines? ? MatchDecisionReasonAssignment::KINDS : [MatchDecisionReasonAssignment::KIND_CANCEL]
sync_match_decision_reason_assignments!(route: @route, decision_type: @step.decision_type, assignments_params: assignments_params, kinds: kinds)
redirect_to edit_admin_match_route_match_decision_step_path(@route, @step), notice: 'Step reasons updated.'
end

private def load_route
@route = MatchRoutes::Base.find(params[:match_route_id])
end

private def load_step
@step = MatchDecisionStep.where(route: @route).find(params[:id])
end

private def default_referral_result_params
params.fetch(:match_decision_step, {}).permit(:default_referral_result)
end

private def assignments_params
return {} unless params[:assignments].present?

params.require(:assignments).permit(decline: {}, cancel: {}).to_h
end
end
end
11 changes: 11 additions & 0 deletions app/controllers/admin/match_routes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
# License detail: https://github.com/greenriver/boston-cas/blob/stable/LICENSE.md
###

# frozen_string_literal: true

module Admin
class MatchRoutesController < ApplicationController
before_action :require_can_manage_config!
before_action :load_match_route, only: [:edit, :update]
before_action :load_steps, only: [:edit]

def index
@routes = MatchRoutes::Base.all.order(id: :desc)
@active_routes, @inactive_routes = @routes.to_a.partition(&:active)
end

def edit
Expand All @@ -25,6 +29,13 @@ def load_match_route
@route = MatchRoutes::Base.find params[:id].to_i
end

private def load_steps
@steps = MatchDecisionStep.where(route: @route).sort_by do |step|
@route.class.match_steps[step.decision_type] || @route.class.match_steps_for_reporting[step.decision_type] || Float::INFINITY
end
@reason_counts = MatchDecisionReasonAssignment.where(route: @route).with_active_reason.group(:decision_type, :kind).count
end

def update_params
params.require(:match_route).permit(
:active,
Expand Down
Loading
Loading