Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

release: date field improvements and application due date #940

Merged
merged 18 commits into from
Nov 26, 2024
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
20 changes: 20 additions & 0 deletions api/src/dtos/listings/listing-published-update.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import {
ArrayMaxSize,
ArrayMinSize,
IsBoolean,
IsDate,
IsDefined,
IsEmail,
IsEnum,
IsPhoneNumber,
IsString,
MaxLength,
ValidateIf,
ValidateNested,
} from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
Expand Down Expand Up @@ -39,6 +41,7 @@ export class ListingPublishedUpdate extends OmitType(ListingUpdate, [
'reviewOrderType',
'units',
'listingsBuildingAddress',
'applicationDueDate',
]) {
@Expose()
@ValidateNested({ groups: [ValidationsGroupsEnum.default], each: true })
Expand Down Expand Up @@ -150,4 +153,21 @@ export class ListingPublishedUpdate extends OmitType(ListingUpdate, [
@ArrayMaxSize(256, { groups: [ValidationsGroupsEnum.default] })
@Type(() => UnitCreate)
units: UnitCreate[];

@Expose()
@IsDate({ groups: [ValidationsGroupsEnum.default] })
@ValidateIf(
(o) =>
!(
o.applicationDueDate == undefined &&
o.reviewOrderType == ReviewOrderTypeEnum.waitlist
),
{
groups: [ValidationsGroupsEnum.default],
},
)
@IsDefined({ groups: [ValidationsGroupsEnum.default] })
@Type(() => Date)
@ApiProperty()
applicationDueDate: Date;
}
Original file line number Diff line number Diff line change
Expand Up @@ -1120,7 +1120,9 @@ describe('Testing Permissioning of endpoints as Jurisdictional Admin in the corr
});

it('should succeed for update endpoint & create an activity log entry', async () => {
const listingData = await listingFactory(jurisId, prisma);
const listingData = await listingFactory(jurisId, prisma, {
applicationDueDate: new Date(),
});
const listing = await prisma.listings.create({
data: listingData,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,9 @@ describe('Testing Permissioning of endpoints as Limited Jurisdictional Admin in
});

it('should succeed for update endpoint & create an activity log entry when user is not updating dates', async () => {
const listingData = await listingFactory(jurisId, prisma);
const listingData = await listingFactory(jurisId, prisma, {
applicationDueDate: new Date(),
});
const listing = await prisma.listings.create({
data: listingData,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,10 @@ describe('Testing Permissioning of endpoints as partner with correct listing', (

listingMulitselectQuestion = msq.id;

const tomorrowsDate = new Date();
tomorrowsDate.setDate(tomorrowsDate.getDate() + 1);
const listingData = await listingFactory(jurisId, prisma, {
applicationDueDate: tomorrowsDate,
multiselectQuestions: [msq],
digitalApp: true,
});
Expand Down Expand Up @@ -1073,12 +1076,18 @@ describe('Testing Permissioning of endpoints as partner with correct listing', (
});

it('should succeed for update endpoint & create an activity log entry', async () => {
const listing = await prisma.listings.findFirst({
where: {
id: userListingId,
},
});

const val = await constructFullListingData(
prisma,
userListingId,
jurisId,
);
val.applicationDueDate = new Date('05-16-2024 01:25:18PM GMT+2');
val.applicationDueDate = listing.applicationDueDate;
val.reviewOrderType = null;

await request(app.getHttpServer())
Expand Down
2 changes: 1 addition & 1 deletion doorway-ui-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"typescript": "^4.9.4"
},
"dependencies": {
"@bloom-housing/ui-components": "12.1.6",
"@bloom-housing/ui-components": "12.4.0",
"ag-grid-community": "^26.0.0",
"markdown-to-jsx": "7.1.8",
"nanoid": "^3.3.6",
Expand Down
2 changes: 1 addition & 1 deletion shared-helpers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
},
"dependencies": {
"@bloom-housing/doorway-ui-components": "^1.0.0",
"@bloom-housing/ui-components": "12.1.6",
"@bloom-housing/ui-components": "12.4.0",
"@bloom-housing/ui-seeds": "1.17.0",
"@heroicons/react": "^2.1.1",
"axios-cookiejar-support": "4.0.6",
Expand Down
10 changes: 8 additions & 2 deletions sites/partners/cypress/e2e/default/03-listing.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ describe("Listing Management Tests", () => {
})
})

// Fill out a First Come, First Serve (FCFS) listing
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function fillOutListing(cy: Cypress.cy, listing: any): void {
cy.getByID("jurisdictions.id").select(listing["jurisdiction.id"])
Expand Down Expand Up @@ -217,7 +218,6 @@ describe("Listing Management Tests", () => {
cy.getByID("specialNotes").type(listing["specialNotes"])
cy.get("button").contains("Application Process").click()
cy.getByID("reviewOrderFCFS").check()
cy.getByID("dueDateQuestionNo").check()
cy.getByID("waitlistOpenNo").check()
cy.getByID("leasingAgentName").type(listing["leasingAgentName"])
cy.getByID("leasingAgentEmail").type(listing["leasingAgentEmail"])
Expand Down Expand Up @@ -271,6 +271,13 @@ describe("Listing Management Tests", () => {
cy.getByID("startTime.period").select("AM")
cy.getByID("endTime.period").select("PM")
cy.getByID("saveOpenHouseFormButton").contains("Save").click()

cy.getByID("applicationDueDateField.month").type(listing["date.month"])
cy.getByID("applicationDueDateField.day").type(listing["date.day"])
cy.getByID("applicationDueDateField.year").type((new Date().getFullYear() + 1).toString())
cy.getByID("applicationDueTimeField.hours").type(listing["startTime.hours"])
cy.getByID("applicationDueTimeField.minutes").type(listing["startTime.minutes"])
cy.getByID("applicationDueTimeField.period").select("PM")
cy.getByID("publishButton").contains("Publish").click()

cy.getByID("publishButtonConfirm").contains("Publish").click()
Expand Down Expand Up @@ -335,7 +342,6 @@ describe("Listing Management Tests", () => {
cy.getByID("programRules").contains(listing["programRules"])
cy.getByID("specialNotes").contains(listing["specialNotes"])
cy.getByID("reviewOrderQuestion").contains("First come first serve")
cy.getByID("dueDateQuestion").contains("No")
cy.getByID("whatToExpect").contains(
"Applicants will be contacted by the property agent in rank order until vacancies are filled. All of the information that you have provided will be verified and your eligibility confirmed. Your application will be removed from the waitlist if you have made any fraudulent statements. If we cannot verify a housing preference that you have claimed, you will not receive the preference but will not be otherwise penalized. Should your application be chosen, be prepared to fill out a more detailed application and provide required supporting documents."
)
Expand Down
6 changes: 5 additions & 1 deletion sites/partners/cypress/fixtures/minimalListing.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,9 @@
"unitType.id": "One Bedroom",
"leasingAgentName": "Basic Agent Name",
"leasingAgentEmail": "[email protected]",
"leasingAgentPhone": "520-245-8811"
"leasingAgentPhone": "520-245-8811",
"date.month": "10",
"date.day": "04",
"date.hours": "10",
"date.minutes": "04"
}
33 changes: 20 additions & 13 deletions sites/partners/cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,8 @@ Cypress.Commands.add("verifyTerms", (application) => {
})

Cypress.Commands.add("addMinimalListing", (listingName, isLottery, isApproval, jurisdiction) => {
// Create and publish minimal lottery listing
// Create and publish minimal FCFS or Lottery listing
// TODO: test Open Waitlist, though maybe with integration test instead
cy.getByID("addListingButton").contains("Add Listing").click()
cy.contains("New Listing")
cy.fixture("minimalListing").then((listing) => {
Expand Down Expand Up @@ -445,19 +446,25 @@ Cypress.Commands.add("addMinimalListing", (listingName, isLottery, isApproval, j
cy.getByID("digitalApplicationChoiceYes").check()
cy.getByID("commonDigitalApplicationChoiceYes").check()
cy.getByID("paperApplicationNo").check()

if (isApproval) {
cy.getByID("submitButton").contains("Submit").click()
cy.getByID("submitListingForApprovalButtonConfirm").contains("Submit").click()
cy.getByTestId("page-header").should("be.visible")
cy.getByTestId("page-header").should("have.text", listingName)
} else {
cy.getByID("publishButton").contains("Publish").click()
cy.getByID("publishButtonConfirm").contains("Publish").click()
cy.get("[data-testid=page-header]").should("be.visible")
cy.getByTestId("page-header").should("have.text", listingName)
}
cy.getByID("applicationDueDateField.month").type(listing["date.month"])
cy.getByID("applicationDueDateField.day").type(listing["date.day"])
cy.getByID("applicationDueDateField.year").type((new Date().getFullYear() + 1).toString())
cy.getByID("applicationDueTimeField.hours").type(listing["date.hours"])
cy.getByID("applicationDueTimeField.minutes").type(listing["date.minutes"])
cy.getByID("applicationDueTimeField.period").select("PM")
})

if (isApproval) {
cy.getByID("submitButton").contains("Submit").click()
cy.getByID("submitListingForApprovalButtonConfirm").contains("Submit").click()
cy.getByTestId("page-header").should("be.visible")
cy.getByTestId("page-header").should("have.text", listingName)
} else {
cy.getByID("publishButton").contains("Publish").click()
cy.getByID("publishButtonConfirm").contains("Publish").click()
cy.get("[data-testid=page-header]").should("be.visible")
cy.getByTestId("page-header").should("have.text", listingName)
}
})

Cypress.Commands.add("addMinimalApplication", (listingName) => {
Expand Down
2 changes: 1 addition & 1 deletion sites/partners/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"dependencies": {
"@bloom-housing/doorway-ui-components": "^1.0.0",
"@bloom-housing/shared-helpers": "^7.7.1",
"@bloom-housing/ui-components": "12.1.6",
"@bloom-housing/ui-components": "12.4.0",
"@bloom-housing/ui-seeds": "1.17.0",
"@heroicons/react": "^2.1.1",
"@mapbox/mapbox-sdk": "^0.13.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const FormApplicationData = (props: FormApplicationDataProps) => {
register={register}
error={errors?.dateSubmitted}
watch={watch}
setValue={setValue}
label={t("application.add.dateSubmitted")}
errorMessage={t("errors.dateError")}
required={!!isDateRequired}
Expand All @@ -80,6 +81,7 @@ const FormApplicationData = (props: FormApplicationDataProps) => {
name="timeSubmitted"
label={t("application.add.timeSubmitted")}
register={register}
setValue={setValue}
watch={watch}
error={!!errors?.timeSubmitted}
disabled={!isDateFilled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,6 @@ const DetailRankingsAndResults = () => {
</Grid.Row>
</>
)}
{getReviewOrderType() === ReviewOrderTypeEnum.firstComeFirstServe && (
<Grid.Row>
<FieldValue id="dueDateQuestion" label={t("listings.dueDateQuestion")}>
{listing.applicationDueDate ? t("t.yes") : t("t.no")}
</FieldValue>
</Grid.Row>
)}
{listing.reviewOrderType === ReviewOrderTypeEnum.waitlist && (
<>
<Grid.Row>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const OpenHouseForm = ({ onSubmit, currentEvent }: OpenHouseFormProps) => {
})()

// eslint-disable-next-line @typescript-eslint/unbound-method
const { register, watch, trigger, getValues, errors } = useForm<OpenHouseFormValues>({
const { register, setValue, watch, trigger, getValues, errors } = useForm<OpenHouseFormValues>({
defaultValues,
})

Expand Down Expand Up @@ -107,6 +107,7 @@ const OpenHouseForm = ({ onSubmit, currentEvent }: OpenHouseFormProps) => {
name="date"
id="date"
register={register}
setValue={setValue}
watch={watch}
error={errors?.date}
errorMessage={t("errors.requiredFieldError")}
Expand All @@ -120,6 +121,7 @@ const OpenHouseForm = ({ onSubmit, currentEvent }: OpenHouseFormProps) => {
name="startTime"
id="startTime"
register={register}
setValue={setValue}
watch={watch}
error={!!errors?.startTime}
required
Expand All @@ -132,6 +134,7 @@ const OpenHouseForm = ({ onSubmit, currentEvent }: OpenHouseFormProps) => {
name="endTime"
id="endTime"
register={register}
setValue={setValue}
watch={watch}
error={!!errors?.endTime}
required
Expand Down
70 changes: 38 additions & 32 deletions sites/partners/src/components/listings/PaperListingForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -191,44 +191,50 @@
try {
setLoading(true)
clearErrors()

const dataPipeline = new ListingDataPipeline(formData, {
preferences,
programs,
units,
openHouseEvents,
profile: profile,
latLong,
customMapPositionChosen,
})
const formattedData = await dataPipeline.run()
let result
if (editMode) {
result = await listingsService.update({
id: listing.id,
body: { id: listing.id, ...(formattedData as unknown as ListingUpdate) },
})
} else {
result = await listingsService.create({
body: formattedData as unknown as ListingCreate,
const successful = await formMethods.trigger()

if (successful) {
const dataPipeline = new ListingDataPipeline(formData, {
preferences,
programs,
units,
openHouseEvents,
profile: profile,
latLong,
customMapPositionChosen,
})
}
const formattedData = await dataPipeline.run()
let result
if (editMode) {
result = await listingsService.update({
id: listing.id,
body: { id: listing.id, ...(formattedData as unknown as ListingUpdate) },
})
} else {
result = await listingsService.create({
body: formattedData as unknown as ListingCreate,
})
}

reset(formData)
reset(formData)

if (result) {
addToast(getToast(listing, listing?.status, formattedData?.status), {
variant: "success",
})
if (result) {
addToast(getToast(listing, listing?.status, formattedData?.status), {
variant: "success",
})

if (continueEditing) {
setAlert(null)
setListingName(result.name)
} else {
await router.push(`/listings/${result.id}`)
if (continueEditing) {
setAlert(null)
setListingName(result.name)
} else {
await router.push(`/listings/${result.id}`)
}
}
setLoading(false)
} else {
setLoading(false)
setAlert("form")
}
setLoading(false)
} catch (err) {
reset(formData)
setLoading(false)
Expand Down Expand Up @@ -267,7 +273,7 @@
}
}
},
[

Check warning on line 276 in sites/partners/src/components/listings/PaperListingForm/index.tsx

View workflow job for this annotation

GitHub Actions / run-linters

React Hook useCallback has missing dependencies: 'formMethods' and 'setListingName'. Either include them or remove the dependency array. If 'setListingName' changes too often, find the parent component that defines it and wrap that definition in useCallback
units,
openHouseEvents,
editMode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,9 @@ const ApplicationAddress = ({ listing }: ApplicationAddressProps) => {
name={"postmarkByDateDateField"}
id={"postmarkByDateDateField"}
register={register}
setValue={setValue}
watch={watch}
error={errors?.postmarkByDateDateField}
defaultDate={{
month: listing?.postmarkedApplicationsReceivedByDate
? dayjsDate.format("MM")
Expand All @@ -728,7 +730,9 @@ const ApplicationAddress = ({ listing }: ApplicationAddressProps) => {
name={"postmarkByDateTimeField"}
id={"postmarkByDateTimeField"}
register={register}
setValue={setValue}
watch={watch}
error={errors?.postmarkByDateTimeField}
defaultValues={{
hours: listing?.postmarkedApplicationsReceivedByDate
? dayjsDate.format("hh")
Expand Down
Loading
Loading