diff --git a/api/src/dtos/listings/listing-published-update.dto.ts b/api/src/dtos/listings/listing-published-update.dto.ts
index 445759f9b6..b1a3c16710 100644
--- a/api/src/dtos/listings/listing-published-update.dto.ts
+++ b/api/src/dtos/listings/listing-published-update.dto.ts
@@ -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';
@@ -39,6 +41,7 @@ export class ListingPublishedUpdate extends OmitType(ListingUpdate, [
'reviewOrderType',
'units',
'listingsBuildingAddress',
+ 'applicationDueDate',
]) {
@Expose()
@ValidateNested({ groups: [ValidationsGroupsEnum.default], each: true })
@@ -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;
}
diff --git a/api/test/integration/permission-tests/permission-as-juris-admin-correct-juris.e2e-spec.ts b/api/test/integration/permission-tests/permission-as-juris-admin-correct-juris.e2e-spec.ts
index 3866035582..1562774b08 100644
--- a/api/test/integration/permission-tests/permission-as-juris-admin-correct-juris.e2e-spec.ts
+++ b/api/test/integration/permission-tests/permission-as-juris-admin-correct-juris.e2e-spec.ts
@@ -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,
});
diff --git a/api/test/integration/permission-tests/permission-as-limited-juris-admin-correct-juris.e2e-spec.ts b/api/test/integration/permission-tests/permission-as-limited-juris-admin-correct-juris.e2e-spec.ts
index fd74592897..10bf7a2b0d 100644
--- a/api/test/integration/permission-tests/permission-as-limited-juris-admin-correct-juris.e2e-spec.ts
+++ b/api/test/integration/permission-tests/permission-as-limited-juris-admin-correct-juris.e2e-spec.ts
@@ -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,
});
diff --git a/api/test/integration/permission-tests/permission-as-partner-correct-listing.e2e-spec.ts b/api/test/integration/permission-tests/permission-as-partner-correct-listing.e2e-spec.ts
index a11e321d36..6896c14d91 100644
--- a/api/test/integration/permission-tests/permission-as-partner-correct-listing.e2e-spec.ts
+++ b/api/test/integration/permission-tests/permission-as-partner-correct-listing.e2e-spec.ts
@@ -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,
});
@@ -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())
diff --git a/doorway-ui-components/package.json b/doorway-ui-components/package.json
index f5866df0df..6397f03ef3 100644
--- a/doorway-ui-components/package.json
+++ b/doorway-ui-components/package.json
@@ -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",
diff --git a/shared-helpers/package.json b/shared-helpers/package.json
index dd3f8b78a1..97b6a5050b 100644
--- a/shared-helpers/package.json
+++ b/shared-helpers/package.json
@@ -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",
diff --git a/sites/partners/cypress/e2e/default/03-listing.spec.ts b/sites/partners/cypress/e2e/default/03-listing.spec.ts
index ed3a032d37..fd651a7a81 100644
--- a/sites/partners/cypress/e2e/default/03-listing.spec.ts
+++ b/sites/partners/cypress/e2e/default/03-listing.spec.ts
@@ -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"])
@@ -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"])
@@ -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()
@@ -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."
)
diff --git a/sites/partners/cypress/fixtures/minimalListing.json b/sites/partners/cypress/fixtures/minimalListing.json
index b37a8bce84..f79922f47f 100644
--- a/sites/partners/cypress/fixtures/minimalListing.json
+++ b/sites/partners/cypress/fixtures/minimalListing.json
@@ -11,5 +11,9 @@
"unitType.id": "One Bedroom",
"leasingAgentName": "Basic Agent Name",
"leasingAgentEmail": "basicAgent@email.com",
- "leasingAgentPhone": "520-245-8811"
+ "leasingAgentPhone": "520-245-8811",
+ "date.month": "10",
+ "date.day": "04",
+ "date.hours": "10",
+ "date.minutes": "04"
}
diff --git a/sites/partners/cypress/support/commands.js b/sites/partners/cypress/support/commands.js
index db38a4e1da..d0712c6330 100644
--- a/sites/partners/cypress/support/commands.js
+++ b/sites/partners/cypress/support/commands.js
@@ -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) => {
@@ -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) => {
diff --git a/sites/partners/package.json b/sites/partners/package.json
index f6ec7dee8a..679aa86115 100644
--- a/sites/partners/package.json
+++ b/sites/partners/package.json
@@ -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",
diff --git a/sites/partners/src/components/applications/PaperApplicationForm/sections/FormApplicationData.tsx b/sites/partners/src/components/applications/PaperApplicationForm/sections/FormApplicationData.tsx
index c179f28701..bd09f41468 100644
--- a/sites/partners/src/components/applications/PaperApplicationForm/sections/FormApplicationData.tsx
+++ b/sites/partners/src/components/applications/PaperApplicationForm/sections/FormApplicationData.tsx
@@ -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}
@@ -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}
diff --git a/sites/partners/src/components/listings/PaperListingDetails/sections/DetailRankingsAndResults.tsx b/sites/partners/src/components/listings/PaperListingDetails/sections/DetailRankingsAndResults.tsx
index b9d9c88595..d2e0e6d57f 100644
--- a/sites/partners/src/components/listings/PaperListingDetails/sections/DetailRankingsAndResults.tsx
+++ b/sites/partners/src/components/listings/PaperListingDetails/sections/DetailRankingsAndResults.tsx
@@ -63,13 +63,6 @@ const DetailRankingsAndResults = () => {
>
)}
- {getReviewOrderType() === ReviewOrderTypeEnum.firstComeFirstServe && (
- {t("listings.dueDateQuestion")}
@@ -116,9 +112,16 @@ const ApplicationDates = ({
name={"applicationDueDateField"}
id={"applicationDueDateField"}
register={register}
+ setValue={setValue}
watch={watch}
+ error={
+ hasDueDateError && {
+ month: hasDueDateError,
+ day: hasDueDateError,
+ year: hasDueDateError,
+ }
+ }
note={t("listings.whenApplicationsClose")}
- disabled={disableDueDate || enableDueDate === YesNoEnum.no}
defaultDate={{
month: listing?.applicationDueDate
? dayjs(new Date(listing?.applicationDueDate)).format("MM")
@@ -138,24 +141,19 @@ const ApplicationDates = ({
name={"applicationDueTimeField"}
id={"applicationDueTimeField"}
register={register}
+ setValue={setValue}
watch={watch}
- disabled={disableDueDate || enableDueDate === YesNoEnum.no}
+ error={errors?.applicationDueDate || errors?.applicationDueTimeField}
defaultValues={{
hours: listing?.applicationDueDate
? dayjs(new Date(listing?.applicationDueDate)).format("hh")
- : enableDueDate === YesNoEnum.no
- ? null
- : "05",
+ : null,
minutes: listing?.applicationDueDate
? dayjs(new Date(listing?.applicationDueDate)).format("mm")
- : enableDueDate === YesNoEnum.no
- ? null
- : "00",
+ : null,
seconds: listing?.applicationDueDate
? dayjs(new Date(listing?.applicationDueDate)).format("ss")
- : enableDueDate === YesNoEnum.no
- ? null
- : "00",
+ : null,
period: listing?.applicationDueDate
? new Date(listing?.applicationDueDate).getHours() >= 12
? "pm"
diff --git a/sites/partners/src/components/listings/PaperListingForm/sections/RankingsAndResults.tsx b/sites/partners/src/components/listings/PaperListingForm/sections/RankingsAndResults.tsx
index 4afe16a39b..9dd60a5769 100644
--- a/sites/partners/src/components/listings/PaperListingForm/sections/RankingsAndResults.tsx
+++ b/sites/partners/src/components/listings/PaperListingForm/sections/RankingsAndResults.tsx
@@ -24,7 +24,7 @@ const RankingsAndResults = ({ listing, disableDueDates, isAdmin }: RankingsAndRe
const formMethods = useFormContext()
// eslint-disable-next-line @typescript-eslint/unbound-method
- const { register, watch, control, errors } = formMethods
+ const { register, setValue, watch, control, errors } = formMethods
const lotteryEvent = getLotteryEvent(listing as unknown as Listing)
@@ -62,6 +62,7 @@ const RankingsAndResults = ({ listing, disableDueDates, isAdmin }: RankingsAndRe
value: YesNoEnum.no,
},
]
+
return (
<>