Skip to content

Commit

Permalink
Merge branch 'main' into DAH-3122-log-translate-api-usage
Browse files Browse the repository at this point in the history
  • Loading branch information
jimlin-sfgov authored Jan 14, 2025
2 parents 3e0c2aa + efb3c36 commit f56fa14
Show file tree
Hide file tree
Showing 28 changed files with 325 additions and 56 deletions.
2 changes: 2 additions & 0 deletions app/assets/javascripts/config/angularInitialize.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@
if !ShortFormApplicationService.authorizedToProceed(toState, fromState, toSection)
e.preventDefault()
if ShortFormApplicationService.listingIsSale()
if (ShortFormApplicationService.listingIsDalp())
return $state.go('dahlia.short-form-application.dalp-screening', toParams)
return $state.go('dahlia.short-form-application.prerequisites', toParams)
return $state.go('dahlia.short-form-application.name', toParams)

Expand Down
17 changes: 15 additions & 2 deletions app/assets/javascripts/config/angularRoutes.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,17 @@
$translate('page_title.listing_application', {listing: listing.Name})
]
})
.state('dahlia.short-form-application.dalp-screening', {
url: '/dalp-screening'
params:
skipConfirm: { squash: true, value: false }
views:
'container@':
templateUrl: 'short-form/templates/layout.html'
controller: 'ShortFormApplicationController'
'[email protected]':
templateUrl: 'short-form/templates/a4a-dalp-screening.html'
})
# Short form: "You" section
.state('dahlia.short-form-application.prerequisites', {
url: '/prerequisites'
Expand All @@ -657,8 +668,10 @@
infoChanged:
squash: true
onEnter: [
'$stateParams', 'ShortFormApplicationService', 'AccountService', 'AutosaveService'
($stateParams, ShortFormApplicationService, AccountService, AutosaveService) ->
'$state', '$stateParams', 'ShortFormApplicationService', 'AccountService', 'AutosaveService'
($state, $stateParams, ShortFormApplicationService, AccountService, AutosaveService) ->
if ShortFormApplicationService.listingIsDalp() && !ShortFormApplicationService.application.answeredDalpScreening
$state.go('dahlia.short-form-application.dalp-screening', {id: $stateParams.id, skipConfirm: true, lang: $stateParams.lang})
# If applicant tries to go to this page on a rental listing, redirect them back to homepage
if !ShortFormApplicationService.listingIsSale()
$state.go('dahlia.welcome')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ ListingConstantsService = () ->
rightToReturnSunnydale: "Right to Return - Sunnydale"
rightToReturnHuntersView: "Right to Return - Hunters View"
rightToReturnPotrero: "Right to Return - Potrero"
dalpFirstResponder: "DALP First Responders"
dalpEducator: "DALP SFUSD Educators"

# List of preferences that follow the right to return pattern.
Service.rightToReturnPreferences =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ ListingIdentityService = (ListingConstantsService) ->
return false unless listing
listing.Reserved_community_type == ListingConstantsService.RESERVED_TYPES.HABITAT

# Determine if listing is DALP listing that has hard-coded content
Service.isDalpListing = (listing) ->
return false unless listing
listing.Custom_Listing_Type == 'Downpayment Assistance Loan Program'

# Business logic for determining if a listing is open
# `due date` should be a datetime, to include precise hour of deadline
Service.isOpen = (listing) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,10 @@ ShortFormApplicationController = (

########## END CUSTOM SCREENING LOGIC ##########

$scope.afterDalpScreening = ->
ShortFormApplicationService.application.answeredDalpScreening = true
ShortFormNavigationService.goToApplicationPage('dahlia.short-form-application.prerequisites')

$scope.addressInputInvalid = (identifier = '') ->
return true if $scope.addressValidationError(identifier)
$scope.inputInvalid('address1', identifier) ||
Expand Down Expand Up @@ -661,6 +665,15 @@ ShortFormApplicationController = (
ShortFormNavigationService.goToSection('Preferences')
return

if match == 'householdMatch' && ShortFormApplicationService.listingIsDalp()
$scope.goToNextReservedPageIfAvailable()
return

if match == 'incomeMatch' && ShortFormApplicationService.listingIsDalp()
ShortFormApplicationService.completeSection('Income')
ShortFormNavigationService.goToSection('Review')
return

ShortFormApplicationService.checkHouseholdEligibility($scope.listing)
.then( (response) ->
eligibility = response.data
Expand Down Expand Up @@ -807,6 +820,9 @@ ShortFormApplicationController = (
$scope.listingIsSale = ->
ShortFormApplicationService.listingIsSale()

$scope.listingIsDalp = ->
ShortFormApplicationService.listingIsDalp()

$scope.listingIsHabitat = ->
ShortFormApplicationService.listingIsHabitat()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ ShortFormApplicationService = (
documents:
'Loan pre-approval': {}
'Homebuyer education certificate': {}
'Dalp educator proof': {}
'Dalp first responder proof': {}
preferences:
liveInSf: null
workInSf: null
Expand Down Expand Up @@ -96,6 +98,9 @@ ShortFormApplicationService = (
customEducatorJobClassificationNumber: null
isAnyoneAVeteran: null
hasHomeAndCommunityBasedServices: null
dalpEducator: null
dalpFirstResponder: null
answeredDalpScreening: null

Service.currentCustomProofPreference = {}
Service.currentRentBurdenAddress = {}
Expand Down Expand Up @@ -202,10 +207,16 @@ ShortFormApplicationService = (
# make sure all validatedForms in previous section == true
_.every(validated['Income'], (i) -> i)
when 'Review'
Service.userCanAccessSection('Preferences') &&
completed.Preferences &&
# make sure all validatedForms in previous section == true
_.every(validated['Preferences'], (i) -> i)
if (Service.listingIsDalp())
Service.userCanAccessSection('Income') &&
completed.Income &&
# make sure all validatedForms in previous section == true
_.every(validated['Income'], (i) -> i)
else
Service.userCanAccessSection('Preferences') &&
completed.Preferences &&
# make sure all validatedForms in previous section == true
_.every(validated['Preferences'], (i) -> i)
else
false

Expand Down Expand Up @@ -1140,6 +1151,9 @@ ShortFormApplicationService = (
Service.listingIsSale = ->
ListingIdentityService.isSale(Service.listing)

Service.listingIsDalp = ->
ListingIdentityService.isDalpListing(Service.listing)

Service.listingIsHabitat = ->
ListingIdentityService.isHabitatListing(Service.listing)

Expand All @@ -1152,6 +1166,9 @@ ShortFormApplicationService = (
ListingDataService.listing.Custom_Listing_Type
)

Service.listingIsDalp = ->
ListingDataService.listing.Custom_Listing_Type == 'Downpayment Assistance Loan Program'

Service.listingHasHomeAndCommunityBasedServicesUnits = (listing) ->
listing.Custom_Listing_Type == ListingConstantsService.HCBS_PRIORITY_NAME

Expand Down
12 changes: 10 additions & 2 deletions app/assets/javascripts/short-form/ShortFormDataService.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,10 @@ ShortFormDataService = (ListingDataService, ListingConstantsService, ListingPref
memberId = appPrefs[prefKey + '_household_member']
member = _.find(allMembers, { id: memberId })

if !member && prefKey == 'rentBurden'
# set a default member in the case of rentBurden where none was indicated
if !member && prefKey == 'rentBurden' ||
(listingPref.preferenceName == PREFS.dalpEducator && application.dalpEducator) ||
(listingPref.preferenceName == PREFS.dalpFirstResponder && application.dalpFirstResponder)
# set a default member in the case of rentBurden or DALP where none was indicated
member = application.applicant

if member
Expand Down Expand Up @@ -547,6 +549,12 @@ ShortFormDataService = (ListingDataService, ListingConstantsService, ListingPref
prefKey = 'assistedHousing'
else if shortFormPref.individualPreference == 'Rent Burdened'
prefKey = 'rentBurden'
else if listingPref.preferenceName == ListingDataService.preferenceMap.dalpEducator
data.dalpEducator = !!shortFormPref.appMemberID
return
else if listingPref.preferenceName == ListingDataService.preferenceMap.dalpFirstResponder
data.dalpFirstResponder = !!shortFormPref.appMemberID
return
else
prefKey = _.invert(ListingDataService.preferenceMap)[listingPref.preferenceName]
unless prefKey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ ShortFormNavigationService = (
scopedCallbacks: [{func: 'validateCommunityEligibility'}]
'custom-educator-screening':
scopedCallbacks: [{func: 'customEducatorValidateEligibility'}]
'dalp-screening':
scopedCallbacks: [{func: 'afterDalpScreening'}]
# you
'prerequisites':
callbacks: [
Expand Down Expand Up @@ -283,6 +285,8 @@ ShortFormNavigationService = (
listing = ShortFormApplicationService.listing
if listing && ListingIdentityService.isRental(listing)
sections.shift()
if listing && ShortFormApplicationService.listingIsDalp()
sections.splice(4, 1)
sections

Service.submitOptionsForCurrentPage = ->
Expand Down Expand Up @@ -423,7 +427,9 @@ ShortFormNavigationService = (
Service.getPrevPageOfGeneralLottery()
# -- Review
when 'review-optional'
if ShortFormApplicationService.applicantHasNoPreferences()
if ShortFormApplicationService.listingIsDalp()
'income'
else if ShortFormApplicationService.applicantHasNoPreferences()
'general-lottery-notice'
else if Service.hasCustomPreferences()
'custom-preferences'
Expand Down Expand Up @@ -590,6 +596,8 @@ ShortFormNavigationService = (
!!ShortFormApplicationService.listing.customPreferences.length

Service.initialState = () ->
if ShortFormApplicationService.listingIsDalp()
'dahlia.short-form-application.dalp-screening'
if ListingIdentityService.isSale(ShortFormApplicationService.listing)
'dahlia.short-form-application.prerequisites'
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ section.app-container.focus-container ng-class="{'t-zh': application.application
.button-pager_row.primary
button#submit.button.primary ui-sref="dahlia.short-form-application.name({id: listing.Id})" ng-if="listingIsRental()"
| {{'t.next' | translate}}
button#submit.button.primary ui-sref="dahlia.short-form-application.prerequisites({id: listing.Id})" ng-if="listingIsSale()"
button#submit.button.primary ui-sref="dahlia.short-form-application.prerequisites({id: listing.Id})" ng-if="listingIsSale() && !listingIsDalp()"
| {{'t.next' | translate}}
button#submit.button.primary ui-sref="dahlia.short-form-application.dalp-screening({id: listing.Id})" ng-if="listingIsDalp()"
| {{'t.next' | translate}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
section.app-container.focus-container
.app-card.form-card
short-form-header
h2.app-card_question
| {{ 'a3a_dalp_screening.title' | translate }}

.app-inner.inset
.form-group
p.form-note.margin-bottom translate="label.please_select_all_that_apply"
.checkbox
input#dalp_educator name="dalp_educator" type="checkbox" ngchange="onSelectDalpEducator()" ng-model="application.dalpEducator"
label for="dalp_educator" Dalp educator
| {{ application.dalpEducator }}
.form-group.margin-left--2x.margin-right--2x.margin-bottom--2x ng-if="application.dalpEducator"
file-uploader button-label="Upload Paystub" file-label="Paystub" document="application.documents['Dalp educator proof']" file-type="Paystub" required="true"
.checkbox
input#dalp_first_responder name="dalp_first_responder" type="checkbox" ng-model="application.dalpFirstResponder"
label for="dalp_first_responder" Dalp first responder
| {{ application.dalpFirstResponder }}
.form-group.margin-left--2x.margin-right--2x.margin-bottom--2x ng-if="application.dalpFirstResponder"
file-uploader button-label="Upload Pay Stub" file-label="Paystub" document="application.documents['Dalp first responder proof']" file-type="Paystub with employer address" required="true"
.button-pager
.button-pager_row.primary.text-center
input#submit.button.primary.radius type="submit" value="{{'t.next' | translate}}" ng-disabled="eligibilityErrors.length > 0"
13 changes: 13 additions & 0 deletions app/javascript/__tests__/pages/SignIn.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { userEvent } from "@testing-library/user-event"
import { post } from "../../api/apiService"
import { SiteAlert } from "../../components/SiteAlert"
import { t } from "@bloom-housing/ui-components"
import "@testing-library/jest-dom"

jest.mock("react-helmet-async", () => {
return {
Expand Down Expand Up @@ -78,6 +79,18 @@ describe("<SignIn />", () => {
screen.getByText(/Email or password is incorrect\. Check for mistakes and try again/i)
).not.toBeNull()
})

expect(
screen.getByRole("link", {
name: /reset your password/i,
})
).toHaveAttribute("href", "/forgot-password?email=test")

expect(
screen.getByRole("link", {
name: /forgot password\?/i,
})
).toHaveAttribute("href", "/forgot-password?email=test")
})

it("shows the correct error message when bad credentials are entered", async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ exports[`<AccountSettingsPage /> when the user is signed in resize events 1`] =
</div>
<a
class="seeds-link forgot-password-link"
href="/forgot-password"
href="/forgot-password[email protected]"
>
Forgot password?
</a>
Expand Down Expand Up @@ -1506,7 +1506,7 @@ exports[`<AccountSettingsPage /> when the user is signed in resize events 1`] =
</div>
<a
class="seeds-link forgot-password-link"
href="/forgot-password"
href="/forgot-password[email protected]"
>
Forgot password?
</a>
Expand Down Expand Up @@ -2404,7 +2404,7 @@ exports[`<AccountSettingsPage /> when the user is signed in resize events 2`] =
</div>
<a
class="seeds-link forgot-password-link"
href="/forgot-password"
href="/forgot-password[email protected]"
>
Forgot password?
</a>
Expand Down Expand Up @@ -3245,7 +3245,7 @@ exports[`<AccountSettingsPage /> when the user is signed in resize events 2`] =
</div>
<a
class="seeds-link forgot-password-link"
href="/forgot-password"
href="/forgot-password[email protected]"
>
Forgot password?
</a>
Expand Down
12 changes: 12 additions & 0 deletions app/javascript/__tests__/pages/account/account-settings.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,12 @@ describe("<AccountSettingsPage />", () => {
name: /email/i,
})

expect(
screen.getByRole("link", {
name: /forgot password\?/i,
})
).toHaveAttribute("href", "/[email protected]")

const emailField = within(group).getByRole("textbox")

await act(async () => {
Expand Down Expand Up @@ -308,6 +314,12 @@ describe("<AccountSettingsPage />", () => {
}),
})
)

expect(
screen.getByRole("link", {
name: /forgot password\?/i,
})
).toHaveAttribute("href", "/[email protected]")
})

it("does not update with malformed emails", async () => {
Expand Down
46 changes: 46 additions & 0 deletions app/javascript/__tests__/pages/forgot-password.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,50 @@ describe("<ForgotPassword />", () => {
)
).not.toBeNull()
})

it("should extract the email parameter from the URL", async () => {
const customLocation = {
...window.location,
search: "[email protected]",
href: "http://dahlia.com",
assign: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),
toString: jest.fn(),
}

Object.defineProperty(window, "location", {
configurable: true,
enumerable: true,
writable: true,
value: customLocation,
})
await renderAndLoadAsync(<ForgotPassword assetPaths={{}} />)

const emailInput = screen.getByLabelText(/email/i)
expect(emailInput).toHaveValue("[email protected]")
})

it("should handle the absence of the email parameter", async () => {
const customLocation = {
...window.location,
search: "",
href: "http://dahlia.com",
assign: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),
toString: jest.fn(),
}

Object.defineProperty(window, "location", {
configurable: true,
enumerable: true,
writable: true,
value: customLocation,
})
await renderAndLoadAsync(<ForgotPassword assetPaths={{}} />)

const emailInput = screen.getByLabelText(/email/i)
expect(emailInput).toHaveValue("")
})
})
Loading

0 comments on commit f56fa14

Please sign in to comment.