From d71641de95b74bd55700866f9b54fd52c47deaac Mon Sep 17 00:00:00 2001 From: "github.context.workflow" Date: Thu, 16 Jun 2022 18:12:57 +0000 Subject: [PATCH 001/598] chore(release): version - @bloom-housing/backend-core@5.0.1-alpha.0 - @bloom-housing/shared-helpers@5.0.1-alpha.0 - @bloom-housing/partners@5.0.1-alpha.0 - @bloom-housing/public@5.0.1-alpha.0 - @bloom-housing/ui-components@5.0.1-alpha.0 --- backend/core/CHANGELOG.md | 22 ++++++++++++++++++++++ backend/core/package.json | 2 +- shared-helpers/CHANGELOG.md | 22 ++++++++++++++++++++++ shared-helpers/package.json | 6 +++--- sites/partners/CHANGELOG.md | 22 ++++++++++++++++++++++ sites/partners/package.json | 8 ++++---- sites/public/CHANGELOG.md | 22 ++++++++++++++++++++++ sites/public/package.json | 8 ++++---- ui-components/CHANGELOG.md | 22 ++++++++++++++++++++++ ui-components/package.json | 2 +- 10 files changed, 123 insertions(+), 13 deletions(-) diff --git a/backend/core/CHANGELOG.md b/backend/core/CHANGELOG.md index b9beda6c39..cac2e17c87 100644 --- a/backend/core/CHANGELOG.md +++ b/backend/core/CHANGELOG.md @@ -3,6 +3,28 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.0](https://github.com/bloom-housing/bloom/compare/@bloom-housing/backend-core@4.4.1-alpha.13...@bloom-housing/backend-core@5.0.1-alpha.0) (2022-06-16) + + +* 2022-06 -16 sync master (#2825) ([17dabfe](https://github.com/bloom-housing/bloom/commit/17dabfeaf77afb55d629f97fe8e90001df94dc04)), closes [#2825](https://github.com/bloom-housing/bloom/issues/2825) [#2753](https://github.com/bloom-housing/bloom/issues/2753) [#2441](https://github.com/bloom-housing/bloom/issues/2441) [#2460](https://github.com/bloom-housing/bloom/issues/2460) [#2459](https://github.com/bloom-housing/bloom/issues/2459) [#2464](https://github.com/bloom-housing/bloom/issues/2464) [#2465](https://github.com/bloom-housing/bloom/issues/2465) [#2466](https://github.com/bloom-housing/bloom/issues/2466) [#2436](https://github.com/bloom-housing/bloom/issues/2436) [#2451](https://github.com/bloom-housing/bloom/issues/2451) [#2415](https://github.com/bloom-housing/bloom/issues/2415) [#2354](https://github.com/bloom-housing/bloom/issues/2354) [#2455](https://github.com/bloom-housing/bloom/issues/2455) [#2484](https://github.com/bloom-housing/bloom/issues/2484) [#2482](https://github.com/bloom-housing/bloom/issues/2482) [#2483](https://github.com/bloom-housing/bloom/issues/2483) [#2476](https://github.com/bloom-housing/bloom/issues/2476) [#2485](https://github.com/bloom-housing/bloom/issues/2485) [#2470](https://github.com/bloom-housing/bloom/issues/2470) [#2488](https://github.com/bloom-housing/bloom/issues/2488) [#2487](https://github.com/bloom-housing/bloom/issues/2487) [#2496](https://github.com/bloom-housing/bloom/issues/2496) [#2498](https://github.com/bloom-housing/bloom/issues/2498) [#2499](https://github.com/bloom-housing/bloom/issues/2499) [#2291](https://github.com/bloom-housing/bloom/issues/2291) [#2461](https://github.com/bloom-housing/bloom/issues/2461) [#2485](https://github.com/bloom-housing/bloom/issues/2485) [#2494](https://github.com/bloom-housing/bloom/issues/2494) [#2503](https://github.com/bloom-housing/bloom/issues/2503) [#2495](https://github.com/bloom-housing/bloom/issues/2495) [#2477](https://github.com/bloom-housing/bloom/issues/2477) [#2505](https://github.com/bloom-housing/bloom/issues/2505) [#2372](https://github.com/bloom-housing/bloom/issues/2372) [#2489](https://github.com/bloom-housing/bloom/issues/2489) [#2497](https://github.com/bloom-housing/bloom/issues/2497) [#2506](https://github.com/bloom-housing/bloom/issues/2506) [#2486](https://github.com/bloom-housing/bloom/issues/2486) + + +### BREAKING CHANGES + +* consolidated all event section components in one new component, uptake will require removing the deprecated components and uptaking EventSection + +* chore(release): version + + - @bloom-housing/backend-core@3.0.2-alpha.38 + - @bloom-housing/shared-helpers@4.0.1-alpha.63 + - @bloom-housing/partners@4.0.1-alpha.67 + - @bloom-housing/public@4.0.1-alpha.66 + - @bloom-housing/ui-components@4.0.1-alpha.62 + + + + + # [5.0.0](https://github.com/seanmalbert/bloom/compare/@bloom-housing/backend-core@4.4.0...@bloom-housing/backend-core@5.0.0) (2022-06-16) diff --git a/backend/core/package.json b/backend/core/package.json index 3fcd1d3e8e..14643826c4 100644 --- a/backend/core/package.json +++ b/backend/core/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/backend-core", - "version": "5.0.0", + "version": "5.0.1-alpha.0", "description": "Listings service reference implementation for the Bloom affordable housing system", "author": "Sean Albert ", "private": false, diff --git a/shared-helpers/CHANGELOG.md b/shared-helpers/CHANGELOG.md index 8842dfabde..abe4e08ccd 100644 --- a/shared-helpers/CHANGELOG.md +++ b/shared-helpers/CHANGELOG.md @@ -3,6 +3,28 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.0](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@4.4.1-alpha.30...@bloom-housing/shared-helpers@5.0.1-alpha.0) (2022-06-16) + + +* 2022-06 -16 sync master (#2825) ([17dabfe](https://github.com/bloom-housing/bloom/commit/17dabfeaf77afb55d629f97fe8e90001df94dc04)), closes [#2825](https://github.com/bloom-housing/bloom/issues/2825) [#2753](https://github.com/bloom-housing/bloom/issues/2753) [#2441](https://github.com/bloom-housing/bloom/issues/2441) [#2460](https://github.com/bloom-housing/bloom/issues/2460) [#2459](https://github.com/bloom-housing/bloom/issues/2459) [#2464](https://github.com/bloom-housing/bloom/issues/2464) [#2465](https://github.com/bloom-housing/bloom/issues/2465) [#2466](https://github.com/bloom-housing/bloom/issues/2466) [#2436](https://github.com/bloom-housing/bloom/issues/2436) [#2451](https://github.com/bloom-housing/bloom/issues/2451) [#2415](https://github.com/bloom-housing/bloom/issues/2415) [#2354](https://github.com/bloom-housing/bloom/issues/2354) [#2455](https://github.com/bloom-housing/bloom/issues/2455) [#2484](https://github.com/bloom-housing/bloom/issues/2484) [#2482](https://github.com/bloom-housing/bloom/issues/2482) [#2483](https://github.com/bloom-housing/bloom/issues/2483) [#2476](https://github.com/bloom-housing/bloom/issues/2476) [#2485](https://github.com/bloom-housing/bloom/issues/2485) [#2470](https://github.com/bloom-housing/bloom/issues/2470) [#2488](https://github.com/bloom-housing/bloom/issues/2488) [#2487](https://github.com/bloom-housing/bloom/issues/2487) [#2496](https://github.com/bloom-housing/bloom/issues/2496) [#2498](https://github.com/bloom-housing/bloom/issues/2498) [#2499](https://github.com/bloom-housing/bloom/issues/2499) [#2291](https://github.com/bloom-housing/bloom/issues/2291) [#2461](https://github.com/bloom-housing/bloom/issues/2461) [#2485](https://github.com/bloom-housing/bloom/issues/2485) [#2494](https://github.com/bloom-housing/bloom/issues/2494) [#2503](https://github.com/bloom-housing/bloom/issues/2503) [#2495](https://github.com/bloom-housing/bloom/issues/2495) [#2477](https://github.com/bloom-housing/bloom/issues/2477) [#2505](https://github.com/bloom-housing/bloom/issues/2505) [#2372](https://github.com/bloom-housing/bloom/issues/2372) [#2489](https://github.com/bloom-housing/bloom/issues/2489) [#2497](https://github.com/bloom-housing/bloom/issues/2497) [#2506](https://github.com/bloom-housing/bloom/issues/2506) [#2486](https://github.com/bloom-housing/bloom/issues/2486) + + +### BREAKING CHANGES + +* consolidated all event section components in one new component, uptake will require removing the deprecated components and uptaking EventSection + +* chore(release): version + + - @bloom-housing/backend-core@3.0.2-alpha.38 + - @bloom-housing/shared-helpers@4.0.1-alpha.63 + - @bloom-housing/partners@4.0.1-alpha.67 + - @bloom-housing/public@4.0.1-alpha.66 + - @bloom-housing/ui-components@4.0.1-alpha.62 + + + + + # [5.0.0](https://github.com/seanmalbert/bloom/compare/@bloom-housing/shared-helpers@4.4.0...@bloom-housing/shared-helpers@5.0.0) (2022-06-16) diff --git a/shared-helpers/package.json b/shared-helpers/package.json index 23c55a81c3..592cab8319 100644 --- a/shared-helpers/package.json +++ b/shared-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/shared-helpers", - "version": "5.0.0", + "version": "5.0.1-alpha.0", "description": "Shared helpers for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared-helpers", "main": "index.js", @@ -16,8 +16,8 @@ "prettier": "prettier --write \"**/*.ts\"" }, "dependencies": { - "@bloom-housing/backend-core": "^5.0.0", - "@bloom-housing/ui-components": "^5.0.0" + "@bloom-housing/backend-core": "^5.0.1-alpha.0", + "@bloom-housing/ui-components": "^5.0.1-alpha.0" }, "devDependencies": { "@bloom-housing/ui-components": "^3.0.1-alpha.15", diff --git a/sites/partners/CHANGELOG.md b/sites/partners/CHANGELOG.md index 793f6d21c9..878ee26402 100644 --- a/sites/partners/CHANGELOG.md +++ b/sites/partners/CHANGELOG.md @@ -3,6 +3,28 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.0](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@4.4.1-alpha.32...@bloom-housing/partners@5.0.1-alpha.0) (2022-06-16) + + +* 2022-06 -16 sync master (#2825) ([17dabfe](https://github.com/bloom-housing/bloom/commit/17dabfeaf77afb55d629f97fe8e90001df94dc04)), closes [#2825](https://github.com/bloom-housing/bloom/issues/2825) [#2753](https://github.com/bloom-housing/bloom/issues/2753) [#2441](https://github.com/bloom-housing/bloom/issues/2441) [#2460](https://github.com/bloom-housing/bloom/issues/2460) [#2459](https://github.com/bloom-housing/bloom/issues/2459) [#2464](https://github.com/bloom-housing/bloom/issues/2464) [#2465](https://github.com/bloom-housing/bloom/issues/2465) [#2466](https://github.com/bloom-housing/bloom/issues/2466) [#2436](https://github.com/bloom-housing/bloom/issues/2436) [#2451](https://github.com/bloom-housing/bloom/issues/2451) [#2415](https://github.com/bloom-housing/bloom/issues/2415) [#2354](https://github.com/bloom-housing/bloom/issues/2354) [#2455](https://github.com/bloom-housing/bloom/issues/2455) [#2484](https://github.com/bloom-housing/bloom/issues/2484) [#2482](https://github.com/bloom-housing/bloom/issues/2482) [#2483](https://github.com/bloom-housing/bloom/issues/2483) [#2476](https://github.com/bloom-housing/bloom/issues/2476) [#2485](https://github.com/bloom-housing/bloom/issues/2485) [#2470](https://github.com/bloom-housing/bloom/issues/2470) [#2488](https://github.com/bloom-housing/bloom/issues/2488) [#2487](https://github.com/bloom-housing/bloom/issues/2487) [#2496](https://github.com/bloom-housing/bloom/issues/2496) [#2498](https://github.com/bloom-housing/bloom/issues/2498) [#2499](https://github.com/bloom-housing/bloom/issues/2499) [#2291](https://github.com/bloom-housing/bloom/issues/2291) [#2461](https://github.com/bloom-housing/bloom/issues/2461) [#2485](https://github.com/bloom-housing/bloom/issues/2485) [#2494](https://github.com/bloom-housing/bloom/issues/2494) [#2503](https://github.com/bloom-housing/bloom/issues/2503) [#2495](https://github.com/bloom-housing/bloom/issues/2495) [#2477](https://github.com/bloom-housing/bloom/issues/2477) [#2505](https://github.com/bloom-housing/bloom/issues/2505) [#2372](https://github.com/bloom-housing/bloom/issues/2372) [#2489](https://github.com/bloom-housing/bloom/issues/2489) [#2497](https://github.com/bloom-housing/bloom/issues/2497) [#2506](https://github.com/bloom-housing/bloom/issues/2506) [#2486](https://github.com/bloom-housing/bloom/issues/2486) + + +### BREAKING CHANGES + +* consolidated all event section components in one new component, uptake will require removing the deprecated components and uptaking EventSection + +* chore(release): version + + - @bloom-housing/backend-core@3.0.2-alpha.38 + - @bloom-housing/shared-helpers@4.0.1-alpha.63 + - @bloom-housing/partners@4.0.1-alpha.67 + - @bloom-housing/public@4.0.1-alpha.66 + - @bloom-housing/ui-components@4.0.1-alpha.62 + + + + + # [5.0.0](https://github.com/seanmalbert/bloom/compare/@bloom-housing/partners@4.4.0...@bloom-housing/partners@5.0.0) (2022-06-16) diff --git a/sites/partners/package.json b/sites/partners/package.json index d5078183fa..7136a3c467 100644 --- a/sites/partners/package.json +++ b/sites/partners/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/partners", - "version": "5.0.0", + "version": "5.0.1-alpha.0", "author": "Sean Albert ", "description": "Partners app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -26,9 +26,9 @@ "dev:server-wait-cypress": "wait-on \"http-get://localhost:${PORT:-3100}/listings\" --httpTimeout 60000 --tcpTimeout 1500 -v --interval 15000 && yarn build && yarn start" }, "dependencies": { - "@bloom-housing/backend-core": "^5.0.0", - "@bloom-housing/shared-helpers": "^5.0.0", - "@bloom-housing/ui-components": "^5.0.0", + "@bloom-housing/backend-core": "^5.0.1-alpha.0", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.0", + "@bloom-housing/ui-components": "^5.0.1-alpha.0", "@mapbox/mapbox-sdk": "^0.13.0", "ag-grid-community": "^26.0.0", "ag-grid-react": "^26.0.0", diff --git a/sites/public/CHANGELOG.md b/sites/public/CHANGELOG.md index da619d4da4..09f611c65d 100644 --- a/sites/public/CHANGELOG.md +++ b/sites/public/CHANGELOG.md @@ -3,6 +3,28 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.0](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@4.4.1-alpha.31...@bloom-housing/public@5.0.1-alpha.0) (2022-06-16) + + +* 2022-06 -16 sync master (#2825) ([17dabfe](https://github.com/bloom-housing/bloom/commit/17dabfeaf77afb55d629f97fe8e90001df94dc04)), closes [#2825](https://github.com/bloom-housing/bloom/issues/2825) [#2753](https://github.com/bloom-housing/bloom/issues/2753) [#2441](https://github.com/bloom-housing/bloom/issues/2441) [#2460](https://github.com/bloom-housing/bloom/issues/2460) [#2459](https://github.com/bloom-housing/bloom/issues/2459) [#2464](https://github.com/bloom-housing/bloom/issues/2464) [#2465](https://github.com/bloom-housing/bloom/issues/2465) [#2466](https://github.com/bloom-housing/bloom/issues/2466) [#2436](https://github.com/bloom-housing/bloom/issues/2436) [#2451](https://github.com/bloom-housing/bloom/issues/2451) [#2415](https://github.com/bloom-housing/bloom/issues/2415) [#2354](https://github.com/bloom-housing/bloom/issues/2354) [#2455](https://github.com/bloom-housing/bloom/issues/2455) [#2484](https://github.com/bloom-housing/bloom/issues/2484) [#2482](https://github.com/bloom-housing/bloom/issues/2482) [#2483](https://github.com/bloom-housing/bloom/issues/2483) [#2476](https://github.com/bloom-housing/bloom/issues/2476) [#2485](https://github.com/bloom-housing/bloom/issues/2485) [#2470](https://github.com/bloom-housing/bloom/issues/2470) [#2488](https://github.com/bloom-housing/bloom/issues/2488) [#2487](https://github.com/bloom-housing/bloom/issues/2487) [#2496](https://github.com/bloom-housing/bloom/issues/2496) [#2498](https://github.com/bloom-housing/bloom/issues/2498) [#2499](https://github.com/bloom-housing/bloom/issues/2499) [#2291](https://github.com/bloom-housing/bloom/issues/2291) [#2461](https://github.com/bloom-housing/bloom/issues/2461) [#2485](https://github.com/bloom-housing/bloom/issues/2485) [#2494](https://github.com/bloom-housing/bloom/issues/2494) [#2503](https://github.com/bloom-housing/bloom/issues/2503) [#2495](https://github.com/bloom-housing/bloom/issues/2495) [#2477](https://github.com/bloom-housing/bloom/issues/2477) [#2505](https://github.com/bloom-housing/bloom/issues/2505) [#2372](https://github.com/bloom-housing/bloom/issues/2372) [#2489](https://github.com/bloom-housing/bloom/issues/2489) [#2497](https://github.com/bloom-housing/bloom/issues/2497) [#2506](https://github.com/bloom-housing/bloom/issues/2506) [#2486](https://github.com/bloom-housing/bloom/issues/2486) + + +### BREAKING CHANGES + +* consolidated all event section components in one new component, uptake will require removing the deprecated components and uptaking EventSection + +* chore(release): version + + - @bloom-housing/backend-core@3.0.2-alpha.38 + - @bloom-housing/shared-helpers@4.0.1-alpha.63 + - @bloom-housing/partners@4.0.1-alpha.67 + - @bloom-housing/public@4.0.1-alpha.66 + - @bloom-housing/ui-components@4.0.1-alpha.62 + + + + + # [5.0.0](https://github.com/seanmalbert/bloom/compare/@bloom-housing/public@4.4.0...@bloom-housing/public@5.0.0) (2022-06-16) diff --git a/sites/public/package.json b/sites/public/package.json index 7ba954cba2..3917d64647 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/public", - "version": "5.0.0", + "version": "5.0.1-alpha.0", "author": "Sean Albert ", "description": "Public web app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -24,9 +24,9 @@ "dev:all": "concurrently \"yarn dev:listings\" \"yarn dev:server-wait\"" }, "dependencies": { - "@bloom-housing/backend-core": "^5.0.0", - "@bloom-housing/shared-helpers": "^5.0.0", - "@bloom-housing/ui-components": "^5.0.0", + "@bloom-housing/backend-core": "^5.0.1-alpha.0", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.0", + "@bloom-housing/ui-components": "^5.0.1-alpha.0", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1", diff --git a/ui-components/CHANGELOG.md b/ui-components/CHANGELOG.md index c7878e14df..362661a3e8 100644 --- a/ui-components/CHANGELOG.md +++ b/ui-components/CHANGELOG.md @@ -3,6 +3,28 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.0](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@4.4.1-alpha.27...@bloom-housing/ui-components@5.0.1-alpha.0) (2022-06-16) + + +* 2022-06 -16 sync master (#2825) ([17dabfe](https://github.com/bloom-housing/bloom/commit/17dabfeaf77afb55d629f97fe8e90001df94dc04)), closes [#2825](https://github.com/bloom-housing/bloom/issues/2825) [#2753](https://github.com/bloom-housing/bloom/issues/2753) [#2441](https://github.com/bloom-housing/bloom/issues/2441) [#2460](https://github.com/bloom-housing/bloom/issues/2460) [#2459](https://github.com/bloom-housing/bloom/issues/2459) [#2464](https://github.com/bloom-housing/bloom/issues/2464) [#2465](https://github.com/bloom-housing/bloom/issues/2465) [#2466](https://github.com/bloom-housing/bloom/issues/2466) [#2436](https://github.com/bloom-housing/bloom/issues/2436) [#2451](https://github.com/bloom-housing/bloom/issues/2451) [#2415](https://github.com/bloom-housing/bloom/issues/2415) [#2354](https://github.com/bloom-housing/bloom/issues/2354) [#2455](https://github.com/bloom-housing/bloom/issues/2455) [#2484](https://github.com/bloom-housing/bloom/issues/2484) [#2482](https://github.com/bloom-housing/bloom/issues/2482) [#2483](https://github.com/bloom-housing/bloom/issues/2483) [#2476](https://github.com/bloom-housing/bloom/issues/2476) [#2485](https://github.com/bloom-housing/bloom/issues/2485) [#2470](https://github.com/bloom-housing/bloom/issues/2470) [#2488](https://github.com/bloom-housing/bloom/issues/2488) [#2487](https://github.com/bloom-housing/bloom/issues/2487) [#2496](https://github.com/bloom-housing/bloom/issues/2496) [#2498](https://github.com/bloom-housing/bloom/issues/2498) [#2499](https://github.com/bloom-housing/bloom/issues/2499) [#2291](https://github.com/bloom-housing/bloom/issues/2291) [#2461](https://github.com/bloom-housing/bloom/issues/2461) [#2485](https://github.com/bloom-housing/bloom/issues/2485) [#2494](https://github.com/bloom-housing/bloom/issues/2494) [#2503](https://github.com/bloom-housing/bloom/issues/2503) [#2495](https://github.com/bloom-housing/bloom/issues/2495) [#2477](https://github.com/bloom-housing/bloom/issues/2477) [#2505](https://github.com/bloom-housing/bloom/issues/2505) [#2372](https://github.com/bloom-housing/bloom/issues/2372) [#2489](https://github.com/bloom-housing/bloom/issues/2489) [#2497](https://github.com/bloom-housing/bloom/issues/2497) [#2506](https://github.com/bloom-housing/bloom/issues/2506) [#2486](https://github.com/bloom-housing/bloom/issues/2486) + + +### BREAKING CHANGES + +* consolidated all event section components in one new component, uptake will require removing the deprecated components and uptaking EventSection + +* chore(release): version + + - @bloom-housing/backend-core@3.0.2-alpha.38 + - @bloom-housing/shared-helpers@4.0.1-alpha.63 + - @bloom-housing/partners@4.0.1-alpha.67 + - @bloom-housing/public@4.0.1-alpha.66 + - @bloom-housing/ui-components@4.0.1-alpha.62 + + + + + # [5.0.0](https://github.com/seanmalbert/bloom/compare/@bloom-housing/ui-components@4.4.0...@bloom-housing/ui-components@5.0.0) (2022-06-16) diff --git a/ui-components/package.json b/ui-components/package.json index 80e710c068..0806b7210f 100644 --- a/ui-components/package.json +++ b/ui-components/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/ui-components", - "version": "5.0.0", + "version": "5.0.1-alpha.0", "author": "Sean Albert ", "description": "Shared user interface components for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared/ui-components", From d66bed1cea335b3b48643c2722a60f68ee395b50 Mon Sep 17 00:00:00 2001 From: Sean Albert Date: Thu, 16 Jun 2022 21:18:39 -0700 Subject: [PATCH 002/598] build: sets patch version for @nestjs/typeorm --- backend/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/core/package.json b/backend/core/package.json index 14643826c4..30fe0ba103 100644 --- a/backend/core/package.json +++ b/backend/core/package.json @@ -49,7 +49,7 @@ "@nestjs/schedule": "^1.0.2", "@nestjs/swagger": "5.2.0", "@nestjs/throttler": "^2.0.0", - "@nestjs/typeorm": "^8.0.3", + "@nestjs/typeorm": "~8.0.3", "@types/cache-manager": "^3.4.0", "@types/cron": "^1.7.3", "async-retry": "^1.3.1", From e019f59c1932c922372b83de77c091f602b9a896 Mon Sep 17 00:00:00 2001 From: "github.context.workflow" Date: Fri, 17 Jun 2022 04:19:22 +0000 Subject: [PATCH 003/598] chore(release): version - @bloom-housing/backend-core@5.0.1-alpha.1 - @bloom-housing/shared-helpers@5.0.1-alpha.1 - @bloom-housing/partners@5.0.1-alpha.1 - @bloom-housing/public@5.0.1-alpha.1 --- backend/core/CHANGELOG.md | 8 ++++++++ backend/core/package.json | 2 +- shared-helpers/CHANGELOG.md | 8 ++++++++ shared-helpers/package.json | 4 ++-- sites/partners/CHANGELOG.md | 8 ++++++++ sites/partners/package.json | 6 +++--- sites/public/CHANGELOG.md | 8 ++++++++ sites/public/package.json | 6 +++--- 8 files changed, 41 insertions(+), 9 deletions(-) diff --git a/backend/core/CHANGELOG.md b/backend/core/CHANGELOG.md index cac2e17c87..0f84ac9427 100644 --- a/backend/core/CHANGELOG.md +++ b/backend/core/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.1](https://github.com/bloom-housing/bloom/compare/@bloom-housing/backend-core@5.0.1-alpha.0...@bloom-housing/backend-core@5.0.1-alpha.1) (2022-06-17) + +**Note:** Version bump only for package @bloom-housing/backend-core + + + + + ## [5.0.1-alpha.0](https://github.com/bloom-housing/bloom/compare/@bloom-housing/backend-core@4.4.1-alpha.13...@bloom-housing/backend-core@5.0.1-alpha.0) (2022-06-16) diff --git a/backend/core/package.json b/backend/core/package.json index 30fe0ba103..6d408be396 100644 --- a/backend/core/package.json +++ b/backend/core/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/backend-core", - "version": "5.0.1-alpha.0", + "version": "5.0.1-alpha.1", "description": "Listings service reference implementation for the Bloom affordable housing system", "author": "Sean Albert ", "private": false, diff --git a/shared-helpers/CHANGELOG.md b/shared-helpers/CHANGELOG.md index abe4e08ccd..b35c24dd44 100644 --- a/shared-helpers/CHANGELOG.md +++ b/shared-helpers/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.1](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.0...@bloom-housing/shared-helpers@5.0.1-alpha.1) (2022-06-17) + +**Note:** Version bump only for package @bloom-housing/shared-helpers + + + + + ## [5.0.1-alpha.0](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@4.4.1-alpha.30...@bloom-housing/shared-helpers@5.0.1-alpha.0) (2022-06-16) diff --git a/shared-helpers/package.json b/shared-helpers/package.json index 592cab8319..87c8fb4b26 100644 --- a/shared-helpers/package.json +++ b/shared-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/shared-helpers", - "version": "5.0.1-alpha.0", + "version": "5.0.1-alpha.1", "description": "Shared helpers for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared-helpers", "main": "index.js", @@ -16,7 +16,7 @@ "prettier": "prettier --write \"**/*.ts\"" }, "dependencies": { - "@bloom-housing/backend-core": "^5.0.1-alpha.0", + "@bloom-housing/backend-core": "^5.0.1-alpha.1", "@bloom-housing/ui-components": "^5.0.1-alpha.0" }, "devDependencies": { diff --git a/sites/partners/CHANGELOG.md b/sites/partners/CHANGELOG.md index 878ee26402..c79fbbf303 100644 --- a/sites/partners/CHANGELOG.md +++ b/sites/partners/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.1](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.0...@bloom-housing/partners@5.0.1-alpha.1) (2022-06-17) + +**Note:** Version bump only for package @bloom-housing/partners + + + + + ## [5.0.1-alpha.0](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@4.4.1-alpha.32...@bloom-housing/partners@5.0.1-alpha.0) (2022-06-16) diff --git a/sites/partners/package.json b/sites/partners/package.json index 7136a3c467..59d4f86986 100644 --- a/sites/partners/package.json +++ b/sites/partners/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/partners", - "version": "5.0.1-alpha.0", + "version": "5.0.1-alpha.1", "author": "Sean Albert ", "description": "Partners app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -26,8 +26,8 @@ "dev:server-wait-cypress": "wait-on \"http-get://localhost:${PORT:-3100}/listings\" --httpTimeout 60000 --tcpTimeout 1500 -v --interval 15000 && yarn build && yarn start" }, "dependencies": { - "@bloom-housing/backend-core": "^5.0.1-alpha.0", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.0", + "@bloom-housing/backend-core": "^5.0.1-alpha.1", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.1", "@bloom-housing/ui-components": "^5.0.1-alpha.0", "@mapbox/mapbox-sdk": "^0.13.0", "ag-grid-community": "^26.0.0", diff --git a/sites/public/CHANGELOG.md b/sites/public/CHANGELOG.md index 09f611c65d..233b16f0ab 100644 --- a/sites/public/CHANGELOG.md +++ b/sites/public/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.1](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.0...@bloom-housing/public@5.0.1-alpha.1) (2022-06-17) + +**Note:** Version bump only for package @bloom-housing/public + + + + + ## [5.0.1-alpha.0](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@4.4.1-alpha.31...@bloom-housing/public@5.0.1-alpha.0) (2022-06-16) diff --git a/sites/public/package.json b/sites/public/package.json index 3917d64647..1453c9b656 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/public", - "version": "5.0.1-alpha.0", + "version": "5.0.1-alpha.1", "author": "Sean Albert ", "description": "Public web app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -24,8 +24,8 @@ "dev:all": "concurrently \"yarn dev:listings\" \"yarn dev:server-wait\"" }, "dependencies": { - "@bloom-housing/backend-core": "^5.0.1-alpha.0", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.0", + "@bloom-housing/backend-core": "^5.0.1-alpha.1", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.1", "@bloom-housing/ui-components": "^5.0.1-alpha.0", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", From 4ac37c94013a6b98efa0a132728a506cb304c27a Mon Sep 17 00:00:00 2001 From: Sean Albert Date: Thu, 16 Jun 2022 21:20:27 -0700 Subject: [PATCH 004/598] build: yarn lock file --- yarn.lock | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0ff44d537a..21afee0451 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5293,10 +5293,10 @@ dependencies: md5 "^2.2.1" -"@nestjs/typeorm@^8.0.3": - version "8.0.3" - resolved "https://registry.yarnpkg.com/@nestjs/typeorm/-/typeorm-8.0.3.tgz#e62feb1f3ec7fa0d1353f774f3f6915594c34a4e" - integrity sha512-tf9rTXP6LeFInkwd+tktQhtLRsKp4RRYImprqT8gcHcJDx+xMP1IygnXELOKwF5vo2/mnhrGtBlRQ/iiS6170g== +"@nestjs/typeorm@~8.0.3": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@nestjs/typeorm/-/typeorm-8.0.4.tgz#ce6dbacb4c2a75e7d341da90a0d355bffd3149b4" + integrity sha512-l1Fv+XicVFs7j4EKTq32lJ6QomYLlXZwvug9zcts6IBDVd8rYYBPy28pyBMDzLtnm0zD4tcW298rpylL7eTZtQ== dependencies: uuid "8.3.2" @@ -7992,12 +7992,7 @@ ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-regex@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-regex@^5.0.1: +ansi-regex@^5.0.0, ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== @@ -24243,11 +24238,6 @@ yargs-parser@^20.2.2: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== -yargs-parser@^20.2.3: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - yargs-parser@^21.0.0: version "21.0.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" From 77693dcee60be8deba641595ed6ae5e141d14a51 Mon Sep 17 00:00:00 2001 From: Jackson Flint-Gonzales Date: Mon, 20 Jun 2022 20:49:01 -0700 Subject: [PATCH 005/598] feat(listingcard): add compatability for jsx elements in subheader (#2821) BREAKING CHANGE: prop name change for header from "text" to "content" --- sites/public/lib/helpers.tsx | 8 ++-- .../page_components/ListingCard.test.tsx | 12 +++--- .../listing/ListingCard.stories.tsx | 40 +++++++++---------- .../page_components/listing/ListingCard.tsx | 10 ++--- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/sites/public/lib/helpers.tsx b/sites/public/lib/helpers.tsx index 0b78560306..f4ff5aceae 100644 --- a/sites/public/lib/helpers.tsx +++ b/sites/public/lib/helpers.tsx @@ -108,9 +108,9 @@ export const getListings = (listings) => { const generateTableSubHeader = (listing) => { if (listing.listingAvailability === ListingAvailability.availableUnits) { - return { text: t("listings.availableUnits") } + return { content: t("listings.availableUnits") } } else if (listing.listingAvailability === ListingAvailability.openWaitlist) { - return { text: t("listings.waitlist.open") } + return { content: t("listings.waitlist.open") } } return null } @@ -142,8 +142,8 @@ export const getListings = (listings) => { { text: t("t.seeDetails"), href: `/listing/${listing.id}/${listing.urlSlug}` }, ]} contentProps={{ - contentHeader: { text: listing.name }, - contentSubheader: { text: getListingCardSubtitle(listing.buildingAddress) }, + contentHeader: { content: listing.name }, + contentSubheader: { content: getListingCardSubtitle(listing.buildingAddress) }, tableHeader: generateTableSubHeader(listing), }} /> diff --git a/ui-components/__tests__/page_components/ListingCard.test.tsx b/ui-components/__tests__/page_components/ListingCard.test.tsx index 7eacf5fb92..544ed73aa0 100644 --- a/ui-components/__tests__/page_components/ListingCard.test.tsx +++ b/ui-components/__tests__/page_components/ListingCard.test.tsx @@ -33,10 +33,10 @@ describe("", () => { cardTags={[{ text: "card tag 1" }, { text: "card tag 2" }]} footerButtons={[{ text: "see details", href: `see-details-link` }]} contentProps={{ - contentHeader: { text: "title" }, - contentSubheader: { text: "subtitle" }, - tableHeader: { text: "optional table header" }, - tableSubheader: { text: "optional table subheader" }, + contentHeader: { content: "title" }, + contentSubheader: { content: "subtitle" }, + tableHeader: { content: "optional table header" }, + tableSubheader: { content: "optional table subheader" }, }} >
child content
@@ -66,8 +66,8 @@ describe("", () => { href: "listing-link", }} contentProps={{ - contentHeader: { text: "title" }, - contentSubheader: { text: "subtitle" }, + contentHeader: { content: "title" }, + contentSubheader: { content: "subtitle" }, }} /> ) diff --git a/ui-components/src/page_components/listing/ListingCard.stories.tsx b/ui-components/src/page_components/listing/ListingCard.stories.tsx index 0e45ec13e0..db2343ffba 100644 --- a/ui-components/src/page_components/listing/ListingCard.stories.tsx +++ b/ui-components/src/page_components/listing/ListingCard.stories.tsx @@ -86,10 +86,10 @@ export const WithHeaders = () => { tableProps={{ ...standardTableProps }} footerButtons={[{ text: "See Details", href: "see-details-link" }]} contentProps={{ - contentHeader: { text: "Optional content header" }, - contentSubheader: { text: "Optional content subheader" }, - tableHeader: { text: "Optional table header" }, - tableSubheader: { text: "Optional table subheader" }, + contentHeader: { content: "Optional content header" }, + contentSubheader: { content: "Optional content subheader" }, + tableHeader: { content: "Optional table header" }, + tableSubheader: { content: "Optional table subheader" }, }} /> ) @@ -102,8 +102,8 @@ export const WithTags = () => { tableProps={{ ...standardTableProps }} footerButtons={[{ text: "See Details", href: "see-details-link" }]} contentProps={{ - contentHeader: { text: "Optional content header" }, - contentSubheader: { text: "Optional content subheader" }, + contentHeader: { content: "Optional content header" }, + contentSubheader: { content: "Optional content subheader" }, }} cardTags={[ { text: "Tag 1 text" }, @@ -121,10 +121,10 @@ export const WithTagsAndHeaders = () => { tableProps={{ ...standardTableProps }} footerButtons={[{ text: "See Details", href: "see-details-link" }]} contentProps={{ - contentHeader: { text: "Optional content header" }, - contentSubheader: { text: "Optional content subheader" }, - tableHeader: { text: "Optional table header" }, - tableSubheader: { text: "Optional table subheader" }, + contentHeader: { content: "Optional content header" }, + contentSubheader: { content: "Optional content subheader" }, + tableHeader: { content: "Optional table header" }, + tableSubheader: { content: "Optional table subheader" }, }} cardTags={[ { text: "Tag 1 text" }, @@ -141,10 +141,10 @@ export const WithHeadersContent = () => { tableProps={{ ...standardTableProps }} footerButtons={[{ text: "See Details", href: "see-details-link" }]} contentProps={{ - contentHeader: { text: "Property Listing" }, - contentSubheader: { text: "Street Address, Local City ST 12345" }, - tableHeader: { text: "Open Waitlist & Available Units" }, - tableSubheader: { text: "Includes priority units for mobility impairments" }, + contentHeader: { content: "Property Listing" }, + contentSubheader: { content: "Street Address, Local City ST 12345" }, + tableHeader: { content: "Open Waitlist & Available Units" }, + tableSubheader: { content: "Includes priority units for mobility impairments" }, }} /> ) @@ -227,8 +227,8 @@ export const MultipleFooterButtons = () => { { text: "Apply", href: "apply-link" }, ]} contentProps={{ - tableHeader: { text: "Optional table header" }, - tableSubheader: { text: "Optional table subheader" }, + tableHeader: { content: "Optional table header" }, + tableSubheader: { content: "Optional table subheader" }, }} /> ) @@ -245,8 +245,8 @@ export const MultipleSpreadFooterButtons = () => { ]} footerContainerClass={"flex justify-between"} contentProps={{ - tableHeader: { text: "Optional table header" }, - tableSubheader: { text: "Optional table subheader" }, + tableHeader: { content: "Optional table header" }, + tableSubheader: { content: "Optional table subheader" }, }} /> ) @@ -271,8 +271,8 @@ export const CustomFooter = () => { footerContent={getCustomFooter()} footerContainerClass={"flex justify-between"} contentProps={{ - tableHeader: { text: "Optional table header" }, - tableSubheader: { text: "Optional table subheader" }, + tableHeader: { content: "Optional table header" }, + tableSubheader: { content: "Optional table subheader" }, }} /> ) diff --git a/ui-components/src/page_components/listing/ListingCard.tsx b/ui-components/src/page_components/listing/ListingCard.tsx index 4a6e5d68b1..70c3f34636 100644 --- a/ui-components/src/page_components/listing/ListingCard.tsx +++ b/ui-components/src/page_components/listing/ListingCard.tsx @@ -13,7 +13,7 @@ interface ListingCardTableProps extends StandardTableProps, StackedTableProps {} export interface CardHeader { customClass?: string - text: string + content: string | React.ReactNode } export interface FooterButton { @@ -75,10 +75,10 @@ const ListingCard = (props: ListingCardProps) => { style?: HeaderType, customClass?: string ) => { - if (header && header.text) { + if (header && header.content) { return ( - {header.text} + {header.content} ) } else { @@ -118,8 +118,8 @@ const ListingCard = (props: ListingCardProps) => { return ( <>
- {(contentProps?.tableHeader?.text || contentProps?.tableSubheader?.text) && - (contentProps.contentHeader?.text || contentProps?.contentSubheader?.text) && ( + {(contentProps?.tableHeader?.content || contentProps?.tableSubheader?.content) && + (contentProps.contentHeader?.content || contentProps?.contentSubheader?.content) && (
)}
From c380d06134a458ff33c11d4a6b0d67746a51e94f Mon Sep 17 00:00:00 2001 From: "github.context.workflow" Date: Tue, 21 Jun 2022 03:49:32 +0000 Subject: [PATCH 006/598] chore(release): version - @bloom-housing/shared-helpers@5.0.1-alpha.2 - @bloom-housing/partners@5.0.1-alpha.2 - @bloom-housing/public@5.0.1-alpha.2 - @bloom-housing/ui-components@5.0.1-alpha.1 --- shared-helpers/CHANGELOG.md | 8 ++++++++ shared-helpers/package.json | 4 ++-- sites/partners/CHANGELOG.md | 8 ++++++++ sites/partners/package.json | 6 +++--- sites/public/CHANGELOG.md | 16 ++++++++++++++++ sites/public/package.json | 6 +++--- ui-components/CHANGELOG.md | 16 ++++++++++++++++ ui-components/package.json | 2 +- 8 files changed, 57 insertions(+), 9 deletions(-) diff --git a/shared-helpers/CHANGELOG.md b/shared-helpers/CHANGELOG.md index b35c24dd44..c9dd676098 100644 --- a/shared-helpers/CHANGELOG.md +++ b/shared-helpers/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.2](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.1...@bloom-housing/shared-helpers@5.0.1-alpha.2) (2022-06-21) + +**Note:** Version bump only for package @bloom-housing/shared-helpers + + + + + ## [5.0.1-alpha.1](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.0...@bloom-housing/shared-helpers@5.0.1-alpha.1) (2022-06-17) **Note:** Version bump only for package @bloom-housing/shared-helpers diff --git a/shared-helpers/package.json b/shared-helpers/package.json index 87c8fb4b26..d1deaabc4d 100644 --- a/shared-helpers/package.json +++ b/shared-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/shared-helpers", - "version": "5.0.1-alpha.1", + "version": "5.0.1-alpha.2", "description": "Shared helpers for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared-helpers", "main": "index.js", @@ -17,7 +17,7 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.1", - "@bloom-housing/ui-components": "^5.0.1-alpha.0" + "@bloom-housing/ui-components": "^5.0.1-alpha.1" }, "devDependencies": { "@bloom-housing/ui-components": "^3.0.1-alpha.15", diff --git a/sites/partners/CHANGELOG.md b/sites/partners/CHANGELOG.md index c79fbbf303..24b468ba7e 100644 --- a/sites/partners/CHANGELOG.md +++ b/sites/partners/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.2](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.1...@bloom-housing/partners@5.0.1-alpha.2) (2022-06-21) + +**Note:** Version bump only for package @bloom-housing/partners + + + + + ## [5.0.1-alpha.1](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.0...@bloom-housing/partners@5.0.1-alpha.1) (2022-06-17) **Note:** Version bump only for package @bloom-housing/partners diff --git a/sites/partners/package.json b/sites/partners/package.json index 59d4f86986..51f68a148a 100644 --- a/sites/partners/package.json +++ b/sites/partners/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/partners", - "version": "5.0.1-alpha.1", + "version": "5.0.1-alpha.2", "author": "Sean Albert ", "description": "Partners app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -27,8 +27,8 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.1", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.1", - "@bloom-housing/ui-components": "^5.0.1-alpha.0", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.2", + "@bloom-housing/ui-components": "^5.0.1-alpha.1", "@mapbox/mapbox-sdk": "^0.13.0", "ag-grid-community": "^26.0.0", "ag-grid-react": "^26.0.0", diff --git a/sites/public/CHANGELOG.md b/sites/public/CHANGELOG.md index 233b16f0ab..3ebe28904e 100644 --- a/sites/public/CHANGELOG.md +++ b/sites/public/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.2](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.1...@bloom-housing/public@5.0.1-alpha.2) (2022-06-21) + + +### Features + +* **listingcard:** add compatability for jsx elements in subheader ([#2821](https://github.com/bloom-housing/bloom/issues/2821)) ([77693dc](https://github.com/bloom-housing/bloom/commit/77693dcee60be8deba641595ed6ae5e141d14a51)) + + +### BREAKING CHANGES + +* **listingcard:** prop name change for header from "text" to "content" + + + + + ## [5.0.1-alpha.1](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.0...@bloom-housing/public@5.0.1-alpha.1) (2022-06-17) **Note:** Version bump only for package @bloom-housing/public diff --git a/sites/public/package.json b/sites/public/package.json index 1453c9b656..88549a6bca 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/public", - "version": "5.0.1-alpha.1", + "version": "5.0.1-alpha.2", "author": "Sean Albert ", "description": "Public web app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -25,8 +25,8 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.1", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.1", - "@bloom-housing/ui-components": "^5.0.1-alpha.0", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.2", + "@bloom-housing/ui-components": "^5.0.1-alpha.1", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1", diff --git a/ui-components/CHANGELOG.md b/ui-components/CHANGELOG.md index 362661a3e8..a0e4dbbab4 100644 --- a/ui-components/CHANGELOG.md +++ b/ui-components/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.1](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.0...@bloom-housing/ui-components@5.0.1-alpha.1) (2022-06-21) + + +### Features + +* **listingcard:** add compatability for jsx elements in subheader ([#2821](https://github.com/bloom-housing/bloom/issues/2821)) ([77693dc](https://github.com/bloom-housing/bloom/commit/77693dcee60be8deba641595ed6ae5e141d14a51)) + + +### BREAKING CHANGES + +* **listingcard:** prop name change for header from "text" to "content" + + + + + ## [5.0.1-alpha.0](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@4.4.1-alpha.27...@bloom-housing/ui-components@5.0.1-alpha.0) (2022-06-16) diff --git a/ui-components/package.json b/ui-components/package.json index 0806b7210f..4bc769a02a 100644 --- a/ui-components/package.json +++ b/ui-components/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/ui-components", - "version": "5.0.1-alpha.0", + "version": "5.0.1-alpha.1", "author": "Sean Albert ", "description": "Shared user interface components for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared/ui-components", From 8f951f9a4239bb5aad179cc5567f208d34533a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pleba=C5=84ski?= Date: Wed, 22 Jun 2022 05:10:11 +0200 Subject: [PATCH 007/598] feat(backend): add jurisdictional admin relation (#2764) * feat(backend): add jurisdictional admin relation * Fix code style issues with Prettier * feat(backend): add jurisdictional admin handling in applications * feat(backend): factor out common query to ListingsRepository * feat(backend): add authorization actions to listing service methods * feat(backend): add jurisdictional admin permissions for user/userProfile resource * feat(backend): add authz tests for jurisdictional admin * Fix code style issues with Prettier * feat(backend): remove jurisdictionalAdmins and add new role * refactor: remove some waitlist fields, update how it displays (#2767) * chore(release): version - @bloom-housing/backend-core@4.4.1-alpha.9 - @bloom-housing/shared-helpers@4.4.1-alpha.11 - @bloom-housing/partners@4.4.1-alpha.13 - @bloom-housing/public@4.4.1-alpha.12 - @bloom-housing/ui-components@4.4.1-alpha.11 * test: fixes backend e2e tests (#2787) * chore(release): version - @bloom-housing/backend-core@4.4.1-alpha.10 - @bloom-housing/shared-helpers@4.4.1-alpha.12 - @bloom-housing/partners@4.4.1-alpha.14 - @bloom-housing/public@4.4.1-alpha.13 - @bloom-housing/ui-components@4.4.1-alpha.12 * feat(modal): add option for fixed-height scrollable modal (#2769) * chore(release): version - @bloom-housing/shared-helpers@4.4.1-alpha.13 - @bloom-housing/partners@4.4.1-alpha.15 - @bloom-housing/public@4.4.1-alpha.14 - @bloom-housing/ui-components@4.4.1-alpha.13 * feat: create table card component (#2781) * chore(release): version - @bloom-housing/shared-helpers@4.4.1-alpha.14 - @bloom-housing/partners@4.4.1-alpha.16 - @bloom-housing/public@4.4.1-alpha.15 - @bloom-housing/ui-components@4.4.1-alpha.14 * feat(backend): fix jurisdictional admin tests * Fix code style issues with Prettier * feat(backend): allow jurisdictional admins to invite other admins * fix(backend): remove unnecessary console log * fix(backend): add user roles to UserInviteDto * Fix code style issues with Prettier * fix(backend): linter * fix(backend): add type assertion * fix(backend): linter * Fix code style issues with Prettier * fix(backend): linter * fix(backend): linter * feat(backend): add jurisdictions to UserUpdateDto * Fix code style issues with Prettier * feat(backend): optimize authz-service * fix(backend): regenerate swagger client * test: updates authz tests * test: listing tests * test: listing tests * test: listing tests * test: listing tests * test: listing tests * test: listing tests Co-authored-by: Lint Action Co-authored-by: Emily Jablonski <65367387+emilyjablonski@users.noreply.github.com> Co-authored-by: github.context.workflow Co-authored-by: Sean Albert Co-authored-by: Jackson Flint-Gonzales Co-authored-by: dominikx96 --- .circleci/config.yml | 8 +- .../src/applications/applications.module.ts | 3 +- .../services/applications.service.ts | 107 +++-- .../core/src/assets/assets.controller.spec.ts | 1 - backend/core/src/auth/auth.module.ts | 4 +- backend/core/src/auth/authz_policy.csv | 6 +- .../controllers/user-profile.controller.ts | 12 +- .../src/auth/controllers/user.controller.ts | 36 +- backend/core/src/auth/dto/user-invite.dto.ts | 10 +- backend/core/src/auth/dto/user-update.dto.ts | 15 +- .../src/auth/entities/user-roles.entity.ts | 4 + .../core/src/auth/enum/authz-actions.enum.ts | 3 + .../core/src/auth/services/authz.service.ts | 76 +++- .../src/auth/services/user.service.spec.ts | 28 +- .../core/src/auth/services/user.service.ts | 294 +++++++++----- backend/core/src/custom.d.ts | 2 +- .../entities/jurisdiction.entity.ts | 1 + .../core/src/listings/listings.controller.ts | 2 +- backend/core/src/listings/listings.module.ts | 2 + backend/core/src/listings/listings.service.ts | 175 +++++--- .../repositories/listing.repository.ts | 19 + .../listings/tests/listings.service.spec.ts | 7 +- ...612292843-add-jurisdictional-admin-role.ts | 15 + backend/core/src/seeder/seed.ts | 76 ++-- .../core/src/seeder/seeds/jurisdictions.ts | 1 + .../applications/applications.e2e-spec.ts | 13 +- backend/core/test/authz/authz.e2e-spec.ts | 384 +++++++++++++++++- .../core/test/listings/listings.e2e-spec.ts | 21 +- backend/core/test/user/user.e2e-spec.ts | 93 ++--- .../{listings => }/utils/make-test-listing.ts | 4 +- backend/core/types/src/backend-swagger.ts | 20 +- 31 files changed, 1045 insertions(+), 397 deletions(-) create mode 100644 backend/core/src/listings/repositories/listing.repository.ts create mode 100644 backend/core/src/migration/1654612292843-add-jurisdictional-admin-role.ts rename backend/core/test/{listings => }/utils/make-test-listing.ts (96%) diff --git a/.circleci/config.yml b/.circleci/config.yml index f529e44b34..fe81c176d1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,8 +6,8 @@ executors: standard-node: docker: - image: "cimg/node:14.17.6" - - image: "circleci/redis:6.2.1-alpine" - - image: "circleci/postgres:12.3-postgis" + - image: "cimg/redis:6.2.6" + - image: "cimg/postgres:12.10" environment: POSTGRES_USER: bloom-ci # Never do this in production or with any sensitive / non-test data: @@ -16,8 +16,8 @@ executors: cypress-node: docker: - image: "cypress/base:14.17.0" - - image: "circleci/redis:6.2.1-alpine" - - image: "circleci/postgres:12.3-postgis" + - image: "cimg/redis:6.2.6" + - image: "cimg/postgres:12.10" environment: POSTGRES_USER: bloom-ci # Never do this in production or with any sensitive / non-test data: diff --git a/backend/core/src/applications/applications.module.ts b/backend/core/src/applications/applications.module.ts index 36db6b78fd..2fe9357089 100644 --- a/backend/core/src/applications/applications.module.ts +++ b/backend/core/src/applications/applications.module.ts @@ -17,10 +17,11 @@ import { CsvBuilder } from "./services/csv-builder.service" import { ApplicationCsvExporterService } from "./services/application-csv-exporter.service" import { EmailModule } from "../email/email.module" import { ActivityLogModule } from "../activity-log/activity-log.module" +import { ListingRepository } from "../listings/repositories/listing.repository" @Module({ imports: [ - TypeOrmModule.forFeature([Application, Applicant, Address, Listing]), + TypeOrmModule.forFeature([Application, Applicant, Address, Listing, ListingRepository]), AuthModule, ActivityLogModule, SharedModule, diff --git a/backend/core/src/applications/services/applications.service.ts b/backend/core/src/applications/services/applications.service.ts index a33c1e6323..87b2326e25 100644 --- a/backend/core/src/applications/services/applications.service.ts +++ b/backend/core/src/applications/services/applications.service.ts @@ -18,7 +18,6 @@ import { ApplicationFlaggedSetsService } from "../../application-flagged-sets/ap import { AuthzService } from "../../auth/services/authz.service" import { ListingsService } from "../../listings/listings.service" import { Application } from "../entities/application.entity" -import { Listing } from "../../listings/entities/listing.entity" import { authzActions } from "../../auth/enum/authz-actions.enum" import { assignDefined } from "../../shared/utils/assign-defined" import { EmailService } from "../../email/email.service" @@ -27,6 +26,7 @@ import { PaginatedApplicationListQueryParams } from "../dto/paginated-applicatio import { ApplicationCreateDto } from "../dto/application-create.dto" import { ApplicationUpdateDto } from "../dto/application-update.dto" import { ApplicationsCsvListQueryParams } from "../dto/applications-csv-list-query-params" +import { ListingRepository } from "../../listings/repositories/listing.repository" @Injectable({ scope: Scope.REQUEST }) export class ApplicationsService { @@ -37,22 +37,30 @@ export class ApplicationsService { private readonly listingsService: ListingsService, private readonly emailService: EmailService, @InjectRepository(Application) private readonly repository: Repository, - @InjectRepository(Listing) private readonly listingsRepository: Repository+ @InjectRepository(ListingRepository) private readonly listingsRepository: ListingRepository ) {} public async list(params: PaginatedApplicationListQueryParams) { const qb = this._getQb(params) const result = await qb.getMany() + await Promise.all( result.map(async (application) => { - await this.authorizeUserAction(this.req.user, application, authzActions.read) + await this.authorizeUserAction( + this.req.user, + application, + application.listingId, + authzActions.read + ) }) ) + return result } public async rawListWithFlagged(params: ApplicationsCsvListQueryParams) { await this.authorizeCSVExport(this.req.user, params.listingId) + const qb = this._getQb(params) qb.leftJoin( "application_flagged_set_applications_applications", @@ -98,9 +106,15 @@ export class ApplicationsService { await Promise.all( result.map(async (application) => { - await this.authorizeUserAction(this.req.user, application, authzActions.read) + await this.authorizeUserAction( + this.req.user, + application, + application.listingId, + authzActions.read + ) }) ) + return { ...applicationIDResult, items: result, @@ -109,11 +123,13 @@ export class ApplicationsService { async submit(applicationCreateDto: ApplicationCreateDto) { applicationCreateDto.submissionDate = new Date() + const listing = await this.listingsRepository .createQueryBuilder("listings") .where(`listings.id = :listingId`, { listingId: applicationCreateDto.listing.id }) - .select("listings.applicationDueDate") + .select(["listings.applicationDueDate", "listings.id"]) .getOne() + if ( listing && listing.applicationDueDate && @@ -121,7 +137,14 @@ export class ApplicationsService { ) { throw new BadRequestException("Listing is not open for application submission.") } - await this.authorizeUserAction(this.req.user, applicationCreateDto, authzActions.submit) + + await this.authorizeUserAction( + this.req.user, + applicationCreateDto, + listing.id, + authzActions.submit + ) + return await this._create( { ...applicationCreateDto, @@ -132,18 +155,35 @@ export class ApplicationsService { } async create(applicationCreateDto: ApplicationCreateDto) { - await this.authorizeUserAction(this.req.user, applicationCreateDto, authzActions.create) + await this.authorizeUserAction( + this.req.user, + applicationCreateDto, + applicationCreateDto.listing.id, + authzActions.create + ) + return this._create(applicationCreateDto, false) } async findOne(applicationId: string) { - const application = await this.repository.findOneOrFail({ + const application = await this.repository.findOne({ where: { id: applicationId, }, relations: ["user"], }) - await this.authorizeUserAction(this.req.user, application, authzActions.read) + + if (!application) { + throw new NotFoundException() + } + + await this.authorizeUserAction( + this.req.user, + application, + application.listingId, + authzActions.read + ) + return application } @@ -151,10 +191,18 @@ export class ApplicationsService { const application = await this.repository.findOne({ where: { id: applicationUpdateDto.id }, }) + if (!application) { throw new NotFoundException() } - await this.authorizeUserAction(this.req.user, application, authzActions.update) + + await this.authorizeUserAction( + this.req.user, + application, + application.listingId, + authzActions.update + ) + assignDefined(application, { ...applicationUpdateDto, id: application.id, @@ -164,7 +212,9 @@ export class ApplicationsService { "SERIALIZABLE", async (transactionalEntityManager) => { const applicationsRepository = transactionalEntityManager.getRepository(Application) + const newApplication = await applicationsRepository.save(application) + await this.applicationFlaggedSetsService.onApplicationUpdate( application, transactionalEntityManager @@ -176,8 +226,19 @@ export class ApplicationsService { } async delete(applicationId: string) { - const application = await this.findOne(applicationId) - await this.authorizeUserAction(this.req.user, application, authzActions.delete) + const application = await this.repository.findOne({ id: applicationId }) + + if (!application) { + throw new NotFoundException() + } + + await this.authorizeUserAction( + this.req.user, + application, + application.listingId, + authzActions.delete + ) + return await this.repository.softRemove({ id: applicationId }) } @@ -216,6 +277,7 @@ export class ApplicationsService { paramsMap[paramKey](qb, params) } }) + return qb } @@ -303,21 +365,17 @@ export class ApplicationsService { private async authorizeUserAction( user, app: T, + listingId: string, action ) { - let resource: T = app + const jurisdictionId = await this.listingsRepository.getJurisdictionIdByListingId(listingId) - if (app instanceof Application) { - resource = { - ...app, - listing_id: app.listingId, - } - } else if (app instanceof ApplicationCreateDto) { - resource = { - ...app, - listing_id: app.listing.id, - } + const resource: T & { listingId: string; jurisdictionId: string } = { + ...app, + listingId, + jurisdictionId, } + return this.authzService.canOrThrow(user, "application", action, resource) } @@ -325,8 +383,11 @@ export class ApplicationsService { /** * Checking authorization for each application is very expensive. By making lisitngId required, we can check if the user has update permissions for the listing, since right now if a user has that they also can run the export for that listing */ + const jurisdictionId = await this.listingsRepository.getJurisdictionIdByListingId(listingId) + return await this.authzService.canOrThrow(user, "listing", authzActions.update, { id: listingId, + jurisdictionId, }) } diff --git a/backend/core/src/assets/assets.controller.spec.ts b/backend/core/src/assets/assets.controller.spec.ts index c7ac9c9ee1..36c58c2a7c 100644 --- a/backend/core/src/assets/assets.controller.spec.ts +++ b/backend/core/src/assets/assets.controller.spec.ts @@ -9,7 +9,6 @@ describe("AssetsController", () => { let controller: AssetsController beforeEach(async () => { - console.log("dbOptions = ", dbOptions) const module: TestingModule = await Test.createTestingModule({ controllers: [AssetsController], imports: [TypeOrmModule.forRoot(dbOptions), AuthModule], diff --git a/backend/core/src/auth/auth.module.ts b/backend/core/src/auth/auth.module.ts index 72f0a6e2e1..4612600e4f 100644 --- a/backend/core/src/auth/auth.module.ts +++ b/backend/core/src/auth/auth.module.ts @@ -22,6 +22,8 @@ import { EmailModule } from "../email/email.module" import { SmsMfaService } from "./services/sms-mfa.service" import { TwilioModule } from "nestjs-twilio" import { UserRepository } from "./repositories/user-repository" +import Listing from "../listings/entities/listing.entity" +import { ListingRepository } from "../listings/repositories/listing.repository" @Module({ imports: [ @@ -44,7 +46,7 @@ import { UserRepository } from "./repositories/user-repository" }), inject: [ConfigService], }), - TypeOrmModule.forFeature([RevokedToken, User, UserRepository, Application]), + TypeOrmModule.forFeature([RevokedToken, User, UserRepository, Application, ListingRepository]), SharedModule, JurisdictionsModule, EmailModule, diff --git a/backend/core/src/auth/authz_policy.csv b/backend/core/src/auth/authz_policy.csv index 1289c4ecf1..90b313df47 100644 --- a/backend/core/src/auth/authz_policy.csv +++ b/backend/core/src/auth/authz_policy.csv @@ -1,12 +1,12 @@ p, admin, application, true, .* p, user, application, true, submit -p, user, application, !r.obj || (r.sub == r.obj.userId), read +p, user, application, r.sub == r.obj.userId, read p, anonymous, application, true, submit p, admin, user, true, .* p, admin, userProfile, true, .* -p, user, user, !!r.obj && (r.sub == r.obj.id), read -p, user, userProfile, !!r.obj && (r.sub == r.obj.id), (read|update) +p, user, user, r.sub == r.obj.id, read +p, user, userProfile, r.sub == r.obj.id, (read|update) p, anonymous, user, true, create p, admin, asset, true, .* diff --git a/backend/core/src/auth/controllers/user-profile.controller.ts b/backend/core/src/auth/controllers/user-profile.controller.ts index 7d4a52248d..ed8c5aba38 100644 --- a/backend/core/src/auth/controllers/user-profile.controller.ts +++ b/backend/core/src/auth/controllers/user-profile.controller.ts @@ -1,14 +1,11 @@ -import { Body, Controller, Put, Request, UseGuards, UsePipes, ValidationPipe } from "@nestjs/common" +import { Body, Controller, Put, UseGuards, UsePipes, ValidationPipe } from "@nestjs/common" import { ApiBearerAuth, ApiOperation, ApiTags } from "@nestjs/swagger" -import { Request as ExpressRequest } from "express" import { ResourceType } from "../decorators/resource-type.decorator" import { defaultValidationPipeOptions } from "../../shared/default-validation-pipe-options" import { UserService } from "../services/user.service" import { AuthzGuard } from "../guards/authz.guard" import { UserDto } from "../dto/user.dto" import { mapTo } from "../../shared/mapTo" -import { AuthContext } from "../types/auth-context" -import { User } from "../entities/user.entity" import { DefaultAuthGuard } from "../guards/default.guard" import { UserProfileUpdateDto } from "../dto/user-profile.dto" @@ -22,10 +19,7 @@ export class UserProfileController { @Put(":id") @UseGuards(DefaultAuthGuard, AuthzGuard) @ApiOperation({ summary: "Update profile user", operationId: "update" }) - async update( - @Request() req: ExpressRequest, - @Body() dto: UserProfileUpdateDto - ): Promise { - return mapTo(UserDto, await this.userService.update(dto, new AuthContext(req.user as User))) + async update(@Body() dto: UserProfileUpdateDto): Promise { + return mapTo(UserDto, await this.userService.update(dto)) } } diff --git a/backend/core/src/auth/controllers/user.controller.ts b/backend/core/src/auth/controllers/user.controller.ts index 6fce350fc0..b736d09203 100644 --- a/backend/core/src/auth/controllers/user.controller.ts +++ b/backend/core/src/auth/controllers/user.controller.ts @@ -14,7 +14,6 @@ import { ValidationPipe, } from "@nestjs/common" import { ApiBearerAuth, ApiExtraModels, ApiOperation, ApiTags } from "@nestjs/swagger" -import { Request as ExpressRequest } from "express" import { ResourceType } from "../decorators/resource-type.decorator" import { defaultValidationPipeOptions } from "../../shared/default-validation-pipe-options" import { UserService } from "../services/user.service" @@ -26,9 +25,6 @@ import { StatusDto } from "../../shared/dto/status.dto" import { ConfirmDto } from "../dto/confirm.dto" import { ForgotPasswordDto } from "../dto/forgot-password.dto" import { UpdatePasswordDto } from "../dto/update-password.dto" -import { AuthContext } from "../types/auth-context" -import { User } from "../entities/user.entity" -import { ResourceAction } from "../decorators/resource-action.decorator" import { UserBasicDto } from "../dto/user-basic.dto" import { EmailDto } from "../dto/email.dto" import { UserCreateDto } from "../dto/user-create.dto" @@ -38,7 +34,6 @@ import { PaginatedUserListDto } from "../dto/paginated-user-list.dto" import { UserInviteDto } from "../dto/user-invite.dto" import { ForgotPasswordResponseDto } from "../dto/forgot-password-response.dto" import { LoginResponseDto } from "../dto/login-response.dto" -import { authzActions } from "../enum/authz-actions.enum" import { UserCreateQueryParams } from "../dto/user-create-query-params" import { UserFilterParams } from "../dto/user-filter-params" import { DefaultAuthGuard } from "../guards/default.guard" @@ -63,17 +58,12 @@ export class UserController { @UseGuards(OptionalAuthGuard, AuthzGuard) @ApiOperation({ summary: "Create user", operationId: "create" }) async create( - @Request() req: ExpressRequest, @Body() dto: UserCreateDto, @Query() queryParams: UserCreateQueryParams ): Promise { return mapTo( UserBasicDto, - await this.userService.createPublicUser( - dto, - new AuthContext(req.user as User), - queryParams.noWelcomeEmail !== true - ) + await this.userService.createPublicUser(dto, queryParams.noWelcomeEmail !== true) ) } @@ -139,34 +129,24 @@ export class UserController { @UseGuards(DefaultAuthGuard, AuthzGuard) @ApiOperation({ summary: "Update user", operationId: "update" }) @UseInterceptors(ActivityLogInterceptor) - async update(@Request() req: ExpressRequest, @Body() dto: UserUpdateDto): Promise { - return mapTo(UserDto, await this.userService.update(dto, new AuthContext(req.user as User))) + async update(@Body() dto: UserUpdateDto): Promise { + return mapTo(UserDto, await this.userService.update(dto)) } @Get("/list") @UseGuards(OptionalAuthGuard, AuthzGuard) @ApiExtraModels(UserFilterParams) @ApiOperation({ summary: "List users", operationId: "list" }) - async list( - @Query() queryParams: UserListQueryParams, - @Request() req: ExpressRequest - ): Promise { - return mapTo( - PaginatedUserListDto, - await this.userService.list(queryParams, new AuthContext(req.user as User)) - ) + async list(@Query() queryParams: UserListQueryParams): Promise { + return mapTo(PaginatedUserListDto, await this.userService.list(queryParams)) } @Post("/invite") - @UseGuards(OptionalAuthGuard, AuthzGuard) + @UseGuards(OptionalAuthGuard) @ApiOperation({ summary: "Invite user", operationId: "invite" }) - @ResourceAction(authzActions.invite) @UseInterceptors(ActivityLogInterceptor) - async invite(@Request() req: ExpressRequest, @Body() dto: UserInviteDto): Promise { - return mapTo( - UserBasicDto, - await this.userService.invitePartnersPortalUser(dto, new AuthContext(req.user as User)) - ) + async invite(@Body() dto: UserInviteDto): Promise { + return mapTo(UserBasicDto, await this.userService.invite(dto)) } @Get(`:id`) diff --git a/backend/core/src/auth/dto/user-invite.dto.ts b/backend/core/src/auth/dto/user-invite.dto.ts index 360ff09ced..0abffe6ac3 100644 --- a/backend/core/src/auth/dto/user-invite.dto.ts +++ b/backend/core/src/auth/dto/user-invite.dto.ts @@ -20,11 +20,6 @@ export class UserInviteDto extends OmitType(UserDto, [ "failedLoginAttemptsCount", "agreedToTermsOfService", ] as const) { - @Expose() - @IsDefined({ groups: [ValidationsGroupsEnum.default] }) - @Type(() => UserRolesCreateDto) - roles: UserRolesCreateDto | null - @Expose() @IsArray({ groups: [ValidationsGroupsEnum.default] }) @ArrayMinSize(1, { groups: [ValidationsGroupsEnum.default] }) @@ -38,4 +33,9 @@ export class UserInviteDto extends OmitType(UserDto, [ @ValidateNested({ groups: [ValidationsGroupsEnum.default], each: true }) @Type(() => IdDto) leasingAgentInListings?: IdDto[] | null + + @Expose() + @IsDefined({ groups: [ValidationsGroupsEnum.default] }) + @Type(() => UserRolesCreateDto) + roles?: UserRolesCreateDto } diff --git a/backend/core/src/auth/dto/user-update.dto.ts b/backend/core/src/auth/dto/user-update.dto.ts index 8ab8b3dedd..a8a24cfb11 100644 --- a/backend/core/src/auth/dto/user-update.dto.ts +++ b/backend/core/src/auth/dto/user-update.dto.ts @@ -1,6 +1,8 @@ import { OmitType } from "@nestjs/swagger" import { Expose, Type } from "class-transformer" import { + ArrayMinSize, + IsArray, IsDate, IsDefined, IsEmail, @@ -70,12 +72,6 @@ export class UserUpdateDto extends OmitType(UserDto, [ @IsNotEmpty({ groups: [ValidationsGroupsEnum.default] }) currentPassword?: string - @Expose() - @IsDefined({ groups: [ValidationsGroupsEnum.default] }) - @ValidateNested({ groups: [ValidationsGroupsEnum.default], each: true }) - @Type(() => IdDto) - jurisdictions: IdDto[] - @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsDefined({ groups: [ValidationsGroupsEnum.default] }) @@ -94,4 +90,11 @@ export class UserUpdateDto extends OmitType(UserDto, [ @IsString({ groups: [ValidationsGroupsEnum.default] }) @MaxLength(256, { groups: [ValidationsGroupsEnum.default] }) appUrl?: string | null + + @Expose() + @IsArray({ groups: [ValidationsGroupsEnum.default] }) + @ArrayMinSize(1, { groups: [ValidationsGroupsEnum.default] }) + @ValidateNested({ groups: [ValidationsGroupsEnum.default], each: true }) + @Type(() => IdDto) + jurisdictions: IdDto[] } diff --git a/backend/core/src/auth/entities/user-roles.entity.ts b/backend/core/src/auth/entities/user-roles.entity.ts index f07c9b318c..7bf325b7f6 100644 --- a/backend/core/src/auth/entities/user-roles.entity.ts +++ b/backend/core/src/auth/entities/user-roles.entity.ts @@ -16,6 +16,10 @@ export class UserRoles { @Expose() isAdmin?: boolean + @Column("boolean", { default: false }) + @Expose() + isJurisdictionalAdmin?: boolean + @Column("boolean", { default: false }) @Expose() isPartner?: boolean diff --git a/backend/core/src/auth/enum/authz-actions.enum.ts b/backend/core/src/auth/enum/authz-actions.enum.ts index 877c60fd7d..fe494ee809 100644 --- a/backend/core/src/auth/enum/authz-actions.enum.ts +++ b/backend/core/src/auth/enum/authz-actions.enum.ts @@ -6,4 +6,7 @@ export enum authzActions { submit = "submit", confirm = "confirm", invite = "invite", + invitePartner = "invitePartner", + inviteJurisdictionalAdmin = "inviteJurisdictionalAdmin", + inviteSuperAdmin = "inviteSuperAdmin", } diff --git a/backend/core/src/auth/services/authz.service.ts b/backend/core/src/auth/services/authz.service.ts index 97b90875e8..4387e44fff 100644 --- a/backend/core/src/auth/services/authz.service.ts +++ b/backend/core/src/auth/services/authz.service.ts @@ -1,10 +1,11 @@ import { HttpException, HttpStatus, Injectable } from "@nestjs/common" -import { newEnforcer } from "casbin" +import { Enforcer, newEnforcer } from "casbin" import path from "path" import { User } from "../entities/user.entity" import { Listing } from "../../listings/entities/listing.entity" import { UserRoleEnum } from "../enum/user-role-enum" import { authzActions } from "../enum/authz-actions.enum" +import { Jurisdiction } from "../../jurisdictions/entities/jurisdiction.entity" @Injectable() export class AuthzService { @@ -25,44 +26,77 @@ export class AuthzService { // eslint-disable-next-line @typescript-eslint/no-explicit-any obj?: any ): Promise { - const e = await newEnforcer( + let e = await newEnforcer( path.join(__dirname, "..", "authz_model.conf"), path.join(__dirname, "..", "authz_policy.csv") ) - // Get User roles and add them to our enforcer if (user) { - if (user.roles?.isAdmin) { - await e.addRoleForUser(user.id, UserRoleEnum.admin) - } - if (user.roles?.isPartner) { - await e.addRoleForUser(user.id, UserRoleEnum.partner) - } - await e.addRoleForUser(user.id, UserRoleEnum.user) + e = await this.addUserPermissions(e, user) + } + + return await e.enforce(user ? user.id : "anonymous", type, action, obj) + } + + private async addUserPermissions(enforcer: Enforcer, user: User): Promise { + await enforcer.addRoleForUser(user.id, UserRoleEnum.user) + + if (user.roles?.isAdmin) { + await enforcer.addRoleForUser(user.id, UserRoleEnum.admin) + return enforcer + } - // NOTE This normally should be in authz_policy.csv, but casbin does not support expressions on arrays. - // Permissions for a leasing agent on applications are there defined here programatically. - // A User becomes a leasing agent for a given listing if he has a relation (M:N) with it. - // User side this is expressed by 'leasingAgentInListings' property. + if (user.roles?.isJurisdictionalAdmin) { await Promise.all( - user?.leasingAgentInListings.map((listing: Listing) => { - void e.addPermissionForUser( + user.jurisdictions.map((adminInJurisdiction: Jurisdiction) => { + void enforcer.addPermissionForUser( user.id, "application", - `!r.obj || r.obj.listing_id == '${listing.id}'`, + `r.obj.jurisdictionId == '${adminInJurisdiction.id}'`, `(${authzActions.read}|${authzActions.create}|${authzActions.update}|${authzActions.delete})` ) - void e.addPermissionForUser( + void enforcer.addPermissionForUser( user.id, "listing", - `!r.obj || r.obj.id == '${listing.id}'`, - `(${authzActions.read}|${authzActions.update})` + `r.obj.jurisdictionId == '${adminInJurisdiction.id}'`, + `(${authzActions.read}|${authzActions.create}|${authzActions.update}|${authzActions.delete})` + ) + void enforcer.addPermissionForUser( + user.id, + "user", + `r.obj.jurisdictionId == '${adminInJurisdiction.id}'`, + `(${authzActions.read}|${authzActions.invitePartner}|${authzActions.inviteJurisdictionalAdmin})` ) }) ) + return enforcer } - return await e.enforce(user ? user.id : "anonymous", type, action, obj) + // NOTE This normally should be in authz_policy.csv, but casbin does not support expressions on arrays. + // Permissions for a leasing agent on applications are there defined here programatically. + // A User becomes a leasing agent for a given listing if he has a relation (M:N) with it. + // User side this is expressed by 'leasingAgentInListings' property. + if (user.roles?.isPartner) { + await enforcer.addRoleForUser(user.id, UserRoleEnum.partner) + + await Promise.all( + user?.leasingAgentInListings.map((leasingAgentInListing: Listing) => { + void enforcer.addPermissionForUser( + user.id, + "application", + `r.obj.listingId == '${leasingAgentInListing.id}'`, + `(${authzActions.read}|${authzActions.create}|${authzActions.update}|${authzActions.delete})` + ) + void enforcer.addPermissionForUser( + user.id, + "listing", + `r.obj.id == '${leasingAgentInListing.id}'`, + `(${authzActions.read}|${authzActions.update})` + ) + }) + ) + } + return enforcer } /** diff --git a/backend/core/src/auth/services/user.service.spec.ts b/backend/core/src/auth/services/user.service.spec.ts index fc5f1711f3..ac3e8515b4 100644 --- a/backend/core/src/auth/services/user.service.spec.ts +++ b/backend/core/src/auth/services/user.service.spec.ts @@ -15,6 +15,7 @@ import { EmailService } from "../../email/email.service" import { SmsMfaService } from "./sms-mfa.service" import { UserInviteDto } from "../dto/user-invite.dto" import { UserRepository } from "../repositories/user-repository" +import { ListingRepository } from "../../listings/repositories/listing.repository" // Cypress brings in Chai types for the global expect, but we want to use jest // expect here so we need to re-declare it. @@ -41,6 +42,14 @@ describe("UserService", () => { save: jest.fn(), } + const mockListingRepository = { + findOne: jest.fn(), + save: jest.fn(), + createQueryBuilder: jest.fn(), + findByEmail: jest.fn(), + findByResetToken: jest.fn(), + } + beforeEach(async () => { process.env.APP_SECRET = "SECRET" const module: TestingModule = await Test.createTestingModule({ @@ -59,6 +68,10 @@ describe("UserService", () => { provide: getRepositoryToken(Application), useValue: mockApplicationRepo, }, + { + provide: getRepositoryToken(ListingRepository), + useValue: mockListingRepository, + }, { provide: EmailService, useValue: { forgotPassword: jest.fn(), invite: jest.fn() }, @@ -107,7 +120,7 @@ describe("UserService", () => { lastName: "Last", dob: new Date(), } - await expect(service.createPublicUser(user, null, null)).rejects.toThrow( + await expect(service.createPublicUser(user, null)).rejects.toThrow( new HttpException(USER_ERRORS.EMAIL_IN_USE.message, USER_ERRORS.EMAIL_IN_USE.status) ) }) @@ -124,7 +137,7 @@ describe("UserService", () => { } mockUserRepo.findByEmail = jest.fn().mockResolvedValue(null) mockUserRepo.save = jest.fn().mockRejectedValue(new Error(USER_ERRORS.ERROR_SAVING.message)) - await expect(service.createPublicUser(user, null, null)).rejects.toThrow( + await expect(service.createPublicUser(user, null)).rejects.toThrow( new HttpException(USER_ERRORS.ERROR_SAVING.message, USER_ERRORS.ERROR_SAVING.status) ) @@ -143,7 +156,7 @@ describe("UserService", () => { dob: new Date(), } mockUserRepo.findByEmail = jest.fn().mockResolvedValue({ ...user, confirmedAt: new Date() }) - await expect(service._createUser(user, null)).rejects.toThrow( + await expect(service._createUser(user)).rejects.toThrow( new HttpException(USER_ERRORS.EMAIL_IN_USE.message, USER_ERRORS.EMAIL_IN_USE.status) ) @@ -180,7 +193,7 @@ describe("UserService", () => { dob: new Date(), } mockUserRepo.findByEmail = jest.fn().mockResolvedValue(existingUser) - await expect(service._createUser(user, null)).rejects.toThrow( + await expect(service._createUser(user)).rejects.toThrow( new HttpException(USER_ERRORS.EMAIL_IN_USE.message, USER_ERRORS.EMAIL_IN_USE.status) ) @@ -211,13 +224,13 @@ describe("UserService", () => { firstName: "First", lastName: "Last", dob: new Date(), - roles: { isPartner: true }, jurisdictions: [], + roles: {}, } mockUserRepo.findByEmail = jest.fn().mockResolvedValue(existingUser) mockUserRepo.save = jest.fn().mockResolvedValue(user) - const savedUser = await service.invitePartnersPortalUser(user, null) + const savedUser = await service.invite(user) expect(savedUser).toBe(user) // Reset mockUserRepo.save @@ -248,12 +261,11 @@ describe("UserService", () => { firstName: "First", lastName: "Last", dob: new Date(), - roles: { isPartner: true }, jurisdictions: [], } mockUserRepo.findByEmail = jest.fn().mockResolvedValue(existingUser) - await expect(service._createUser(user, null)).rejects.toThrow( + await expect(service._createUser(user)).rejects.toThrow( new HttpException(USER_ERRORS.EMAIL_IN_USE.message, USER_ERRORS.EMAIL_IN_USE.status) ) diff --git a/backend/core/src/auth/services/user.service.ts b/backend/core/src/auth/services/user.service.ts index ee3f7b4e96..eee9bf7cc5 100644 --- a/backend/core/src/auth/services/user.service.ts +++ b/backend/core/src/auth/services/user.service.ts @@ -1,6 +1,7 @@ import { BadRequestException, HttpException, + Inject, Injectable, NotFoundException, Scope, @@ -20,7 +21,6 @@ import { AuthService } from "./auth.service" import { AuthzService } from "./authz.service" import { ForgotPasswordDto } from "../dto/forgot-password.dto" -import { AuthContext } from "../types/auth-context" import { PasswordService } from "./password.service" import { JurisdictionResolverService } from "../../jurisdictions/services/jurisdiction-resolver.service" import { EmailDto } from "../dto/email.dto" @@ -32,8 +32,6 @@ import { ConfigService } from "@nestjs/config" import { authzActions } from "../enum/authz-actions.enum" import { userFilterTypeToFieldMap } from "../dto/user-filter-type-to-field-map" import { Application } from "../../applications/entities/application.entity" -import { Listing } from "../../listings/entities/listing.entity" -import { UserRoles } from "../entities/user-roles.entity" import { Jurisdiction } from "../../jurisdictions/entities/jurisdiction.entity" import { assignDefined } from "../../shared/utils/assign-defined" import { EmailService } from "../../email/email.service" @@ -48,6 +46,10 @@ import { UserFilterParams } from "../dto/user-filter-params" import advancedFormat from "dayjs/plugin/advancedFormat" import { UserRepository } from "../repositories/user-repository" +import { REQUEST } from "@nestjs/core" +import { Request as ExpressRequest } from "express" +import { UserProfileUpdateDto } from "../dto/user-profile.dto" +import { ListingRepository } from "../../listings/repositories/listing.repository" dayjs.extend(advancedFormat) @@ -55,6 +57,7 @@ dayjs.extend(advancedFormat) export class UserService { constructor( @InjectRepository(UserRepository) private readonly userRepository: UserRepository, + @InjectRepository(ListingRepository) private readonly listingRepository: ListingRepository, @InjectRepository(Application) private readonly applicationsRepository: Repository, private readonly emailService: EmailService, private readonly configService: ConfigService, @@ -62,30 +65,24 @@ export class UserService { private readonly authzService: AuthzService, private readonly passwordService: PasswordService, private readonly jurisdictionResolverService: JurisdictionResolverService, - private readonly smsMfaService: SmsMfaService + private readonly smsMfaService: SmsMfaService, + @Inject(REQUEST) private req: ExpressRequest ) {} public async findById(id: string) { const user = await this.userRepository.findById(id) + if (!user) { throw new NotFoundException() } - return user - } - public static isPasswordOutdated(user: User) { - return ( - new Date(user.passwordUpdatedAt.getTime() + user.passwordValidForDays * 24 * 60 * 60 * 1000) < - new Date() && - user.roles && - (user.roles.isAdmin || user.roles.isPartner) - ) + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + await this.authorizeUserAction(this.req.user as User, user, authzActions.read) + + return user } - public async list( - params: UserListQueryParams, - authContext: AuthContext - ): Promise> { + public async list(params: UserListQueryParams): Promise> { const options = { limit: params.limit === "all" ? undefined : params.limit, page: params.page || 10, @@ -141,7 +138,7 @@ export class UserService { */ await Promise.all( result.map(async (user) => { - await this.authzService.canOrThrow(authContext.user, "user", authzActions.read, user) + await this.authorizeUserAction(this.req.user, user, authzActions.read) }) ) @@ -151,8 +148,27 @@ export class UserService { } } - async update(dto: UserUpdateDto, authContext: AuthContext) { + async update(dto: UserUpdateDto | UserProfileUpdateDto) { const user = await this.findById(dto.id) + + if (!user) { + throw new NotFoundException() + } + + if (dto instanceof UserProfileUpdateDto) { + await this.authorizeUserProfileAction(this.req.user, user, authzActions.update) + } else { + await Promise.all( + dto.jurisdictions.map(async (jurisdiction) => { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + await this.authzService.canOrThrow(this.req.user as User, "user", authzActions.update, { + id: user.id, + jurisdictionId: jurisdiction.id, + }) + }) + ) + } + let passwordHash let passwordUpdatedAt if (dto.password) { @@ -169,20 +185,6 @@ export class UserService { delete dto.password } - /** - * jurisdictions should be filtered based off of what the authContext user has - */ - if (authContext.user.jurisdictions) { - if (dto.jurisdictions) { - dto.jurisdictions = dto.jurisdictions.filter( - (jurisdiction) => - authContext.user.jurisdictions.findIndex((val) => val.id === jurisdiction.id) > -1 - ) - } - } else { - delete dto.jurisdictions - } - if (dto.newEmail && dto.appUrl) { user.confirmationToken = UserService.createConfirmationToken(user.id, dto.newEmail) const confirmationUrl = UserService.getPublicConfirmationUrl(dto.appUrl, user) @@ -261,20 +263,6 @@ export class UserService { } } - private async setHitConfirmationURl(user: User, token: string) { - if (!user) { - throw new HttpException(USER_ERRORS.NOT_FOUND.message, USER_ERRORS.NOT_FOUND.status) - } - - if (user.confirmationToken !== token) { - throw new HttpException(USER_ERRORS.TOKEN_MISSING.message, USER_ERRORS.TOKEN_MISSING.status) - } - user.hitConfirmationURL = new Date() - await this.userRepository.save({ - ...user, - }) - } - public async isUserConfirmationTokenValid(dto: ConfirmDto) { try { const token = decode(dto.token, process.env.APP_SECRET) @@ -293,15 +281,6 @@ export class UserService { } } - private static createConfirmationToken(userId: string, email: string) { - const payload = { - id: userId, - email, - exp: Number.parseInt(dayjs().add(24, "hours").format("X")), - } - return encode(payload, process.env.APP_SECRET) - } - public async resendPublicConfirmation(dto: EmailDto) { const user = await this.userRepository.findByEmail(dto.email) if (!user) { @@ -325,14 +304,6 @@ export class UserService { } } - private static getPublicConfirmationUrl(appUrl: string, user: User) { - return `${appUrl}?token=${user.confirmationToken}` - } - - private static getPartnersConfirmationUrl(appUrl: string, user: User) { - return `${appUrl}/users/confirm?token=${user.confirmationToken}` - } - public async connectUserWithExistingApplications(user: User) { const applications = await this.applicationsRepository .createQueryBuilder("applications") @@ -348,12 +319,11 @@ export class UserService { await this.applicationsRepository.save(applications) } - public async _createUser(dto: DeepPartial, authContext: AuthContext) { + public async _createUser(dto: DeepPartial) { if (dto.confirmedAt) { - await this.authzService.canOrThrow(authContext.user, "user", authzActions.confirm, { - ...dto, - }) + await this.authorizeUserAction(this.req.user, dto, authzActions.confirm) } + const existingUser = await this.userRepository.findByEmail(dto.email) if (existingUser) { @@ -378,22 +348,15 @@ export class UserService { return await this.userRepository.save(newUser) } - public async createPublicUser( - dto: UserCreateDto, - authContext: AuthContext, - sendWelcomeEmail = false - ) { - const newUser = await this._createUser( - { - ...dto, - passwordHash: await this.passwordService.passwordToHash(dto.password), - jurisdictions: dto.jurisdictions - ? (dto.jurisdictions as Jurisdiction[]) - : [await this.jurisdictionResolverService.getJurisdiction()], - mfaEnabled: false, - }, - authContext - ) + public async createPublicUser(dto: UserCreateDto, sendWelcomeEmail = false) { + const newUser = await this._createUser({ + ...dto, + passwordHash: await this.passwordService.passwordToHash(dto.password), + jurisdictions: dto.jurisdictions + ? (dto.jurisdictions as Jurisdiction[]) + : [await this.jurisdictionResolverService.getJurisdiction()], + mfaEnabled: false, + }) if (sendWelcomeEmail) { const confirmationUrl = UserService.getPublicConfirmationUrl(dto.appUrl, newUser) await this.emailService.welcome(newUser, dto.appUrl, confirmationUrl) @@ -434,21 +397,19 @@ export class UserService { return this.authService.generateAccessToken(user) } - async invitePartnersPortalUser(dto: UserInviteDto, authContext: AuthContext) { + async invite(dto: UserInviteDto) { const password = crypto.randomBytes(8).toString("hex") - const user = await this._createUser( - { - ...dto, - passwordHash: await this.passwordService.passwordToHash(password), - leasingAgentInListings: dto.leasingAgentInListings as Listing[], - roles: dto.roles as UserRoles, - jurisdictions: dto.jurisdictions - ? (dto.jurisdictions as Jurisdiction[]) - : [await this.jurisdictionResolverService.getJurisdiction()], - mfaEnabled: true, - }, - authContext - ) + + await this.validateInviteActionPermissionsOrThrow(dto) + + const user = await this._createUser({ + ...dto, + passwordHash: await this.passwordService.passwordToHash(password), + jurisdictions: dto.jurisdictions + ? dto.jurisdictions + : [await this.jurisdictionResolverService.getJurisdiction()], + mfaEnabled: true, + }) await this.emailService.invite( user, @@ -460,23 +421,14 @@ export class UserService { async delete(userId: string) { const user = await this.userRepository.findOne({ id: userId }) + if (!user) { throw new NotFoundException() } - await this.userRepository.remove(user) - } - generateMfaCode() { - let out = "" - const characters = "0123456789" - for (let i = 0; i < this.configService.get("MFA_CODE_LENGTH"); i++) { - out += characters.charAt(Math.floor(Math.random() * characters.length)) - } - return out - } + await this.authorizeUserAction(this.req.user, user, authzActions.delete) - private static hasUsedMfaInThePast(user: User): boolean { - return !!user.mfaCodeUpdatedAt + await this.userRepository.remove(user) } async getMfaInfo(getMfaInfoDto: GetMfaInfoDto): Promise { @@ -554,4 +506,126 @@ export class UserService { phoneNumberVerified: user.phoneNumberVerified, } } + + public static isPasswordOutdated(user: User) { + return ( + new Date(user.passwordUpdatedAt.getTime() + user.passwordValidForDays * 24 * 60 * 60 * 1000) < + new Date() && + user.roles && + (user.roles.isAdmin || user.roles.isPartner || user.roles.isJurisdictionalAdmin) + ) + } + + private generateMfaCode() { + let out = "" + const characters = "0123456789" + for (let i = 0; i < this.configService.get("MFA_CODE_LENGTH"); i++) { + out += characters.charAt(Math.floor(Math.random() * characters.length)) + } + return out + } + + private static hasUsedMfaInThePast(user: User): boolean { + return !!user.mfaCodeUpdatedAt + } + + private static createConfirmationToken(userId: string, email: string) { + const payload = { + id: userId, + email, + exp: Number.parseInt(dayjs().add(24, "hours").format("X")), + } + return encode(payload, process.env.APP_SECRET) + } + + private static getPublicConfirmationUrl(appUrl: string, user: User) { + return `${appUrl}?token=${user.confirmationToken}` + } + + private static getPartnersConfirmationUrl(appUrl: string, user: User) { + return `${appUrl}/users/confirm?token=${user.confirmationToken}` + } + + private async setHitConfirmationURl(user: User, token: string) { + if (!user) { + throw new HttpException(USER_ERRORS.NOT_FOUND.message, USER_ERRORS.NOT_FOUND.status) + } + + if (user.confirmationToken !== token) { + throw new HttpException(USER_ERRORS.TOKEN_MISSING.message, USER_ERRORS.TOKEN_MISSING.status) + } + user.hitConfirmationURL = new Date() + + await this.userRepository.save({ + ...user, + }) + } + + private async authorizeUserAction(requestingUser, targetUser, action) { + return await this.authzService.canOrThrow(requestingUser, "user", action, { + id: targetUser.id, + jurisdictionId: targetUser.id, + }) + } + + private async authorizeUserProfileAction(requestingUser, targetUser, action) { + return await this.authzService.canOrThrow(requestingUser, "userProfile", action, { + id: targetUser.id, + jurisdictionId: targetUser.id, + }) + } + + private async validateInviteActionPermissionsOrThrow(dto: UserInviteDto) { + if (dto.roles.isAdmin) { + await this.authzService.canOrThrow( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + this.req.user as User, + "user", + authzActions.inviteSuperAdmin, + null + ) + } + + // For each jurisdiction we need to check if this requesting user is allowed to invite new users to it + if (dto.roles.isJurisdictionalAdmin) { + if (dto.jurisdictions?.length) { + await Promise.all( + dto.jurisdictions.map(async (jurisdiction) => { + await this.authzService.canOrThrow( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + this.req.user as User, + "user", + authzActions.inviteJurisdictionalAdmin, + { + jurisdictionId: jurisdiction.id, + } + ) + }) + ) + } + } + + if (dto.leasingAgentInListings?.length) { + // For each jurisdiction we need to check if this requesting user is allowed to invite new users to it + const jurisdictionsIds = await Promise.all( + dto.leasingAgentInListings.map(async (listing) => { + return await this.listingRepository.getJurisdictionIdByListingId(listing.id) + }) + ) + + await Promise.all( + jurisdictionsIds.map(async (jurisdictionId) => { + await this.authzService.canOrThrow( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + this.req.user as User, + "user", + authzActions.invitePartner, + { + jurisdictionId, + } + ) + }) + ) + } + } } diff --git a/backend/core/src/custom.d.ts b/backend/core/src/custom.d.ts index d27f3e1e3c..4c6542f460 100644 --- a/backend/core/src/custom.d.ts +++ b/backend/core/src/custom.d.ts @@ -3,7 +3,7 @@ declare global { declare module "express-serve-static-core" { export interface Request { - user?: import("./user/entities/user.entity").User + user?: import("./auth/entities/user.entity").User } } } diff --git a/backend/core/src/jurisdictions/entities/jurisdiction.entity.ts b/backend/core/src/jurisdictions/entities/jurisdiction.entity.ts index 9784a54cdd..86c88084de 100644 --- a/backend/core/src/jurisdictions/entities/jurisdiction.entity.ts +++ b/backend/core/src/jurisdictions/entities/jurisdiction.entity.ts @@ -15,6 +15,7 @@ import { ValidationsGroupsEnum } from "../../shared/types/validations-groups-enu import { Language } from "../../shared/types/language-enum" import { Expose, Type } from "class-transformer" import { Preference } from "../../preferences/entities/preference.entity" +import { User } from "../../auth/entities/user.entity" @Entity({ name: "jurisdictions" }) export class Jurisdiction extends AbstractEntity { diff --git a/backend/core/src/listings/listings.controller.ts b/backend/core/src/listings/listings.controller.ts index 3e37a52b0b..d32f9c78ed 100644 --- a/backend/core/src/listings/listings.controller.ts +++ b/backend/core/src/listings/listings.controller.ts @@ -40,7 +40,7 @@ import { ListingsApiExtraModels } from "./types/listings-api-extra-models" @ApiBearerAuth() @ResourceType("listing") @ApiExtraModels(ListingFilterParams, ListingsApiExtraModels) -@UseGuards(OptionalAuthGuard, AuthzGuard) +@UseGuards(OptionalAuthGuard) @ActivityLogMetadata([{ targetPropertyName: "status", propertyPath: "status" }]) @UseInterceptors(ActivityLogInterceptor) export class ListingsController { diff --git a/backend/core/src/listings/listings.module.ts b/backend/core/src/listings/listings.module.ts index a9cef95330..17fcb0f023 100644 --- a/backend/core/src/listings/listings.module.ts +++ b/backend/core/src/listings/listings.module.ts @@ -12,6 +12,7 @@ import { TranslationsModule } from "../translations/translations.module" import { AmiChart } from "../ami-charts/entities/ami-chart.entity" import { ListingFeatures } from "./entities/listing-features.entity" import { ActivityLogModule } from "../activity-log/activity-log.module" +import { ListingRepository } from "./repositories/listing.repository" @Module({ imports: [ @@ -22,6 +23,7 @@ import { ActivityLogModule } from "../activity-log/activity-log.module" User, Property, AmiChart, + ListingRepository, ListingFeatures, ]), AuthModule, diff --git a/backend/core/src/listings/listings.service.ts b/backend/core/src/listings/listings.service.ts index c7ec32c89c..0f48257dfb 100644 --- a/backend/core/src/listings/listings.service.ts +++ b/backend/core/src/listings/listings.service.ts @@ -1,4 +1,11 @@ -import { HttpException, HttpStatus, Injectable, NotFoundException } from "@nestjs/common" +import { + HttpException, + HttpStatus, + Inject, + Injectable, + NotFoundException, + Scope, +} from "@nestjs/common" import { InjectRepository } from "@nestjs/typeorm" import { Pagination } from "nestjs-typeorm-paginate" import { In, Repository } from "typeorm" @@ -20,6 +27,12 @@ import { filterTypeToFieldMap } from "./dto/filter-type-to-field-map" import { ListingStatus } from "./types/listing-status-enum" import { TranslationsService } from "../translations/services/translations.service" import { OrderParam } from "../applications/types/order-param" +import { authzActions } from "../auth/enum/authz-actions.enum" +import { ListingRepository } from "./repositories/listing.repository" +import { AuthzService } from "../auth/services/authz.service" +import { Request as ExpressRequest } from "express" +import { REQUEST } from "@nestjs/core" +import { User } from "../auth/entities/user.entity" type OrderByConditionData = { orderBy: string @@ -27,69 +40,20 @@ type OrderByConditionData = { nulls?: "NULLS LAST" | "NULLS FIRST" } -@Injectable() +@Injectable({ scope: Scope.REQUEST }) export class ListingsService { constructor( - @InjectRepository(Listing) private readonly listingRepository: Repository, + @InjectRepository(ListingRepository) private readonly listingRepository: ListingRepository, @InjectRepository(AmiChart) private readonly amiChartsRepository: Repository, - private readonly translationService: TranslationsService + private readonly translationService: TranslationsService, + private readonly authzService: AuthzService, + @Inject(REQUEST) private req: ExpressRequest ) {} private getFullyJoinedQueryBuilder() { return getView(this.listingRepository.createQueryBuilder("listings"), "full").getViewQb() } - private static getOrderByCondition( - orderBy: OrderByFieldsEnum, - orderDir: OrderParam - ): OrderByConditionData { - switch (orderBy) { - case OrderByFieldsEnum.mostRecentlyUpdated: - return { orderBy: "listings.updated_at", orderDir } - case OrderByFieldsEnum.status: - return { orderBy: "listings.status", orderDir } - case OrderByFieldsEnum.name: - return { orderBy: "listings.name", orderDir } - case OrderByFieldsEnum.waitlistOpen: - return { orderBy: "listings.isWaitlistOpen", orderDir } - case OrderByFieldsEnum.unitsAvailable: - return { orderBy: "property.unitsAvailable", orderDir } - case OrderByFieldsEnum.mostRecentlyClosed: - return { - orderBy: "listings.closedAt", - orderDir, - nulls: "NULLS LAST", - } - case OrderByFieldsEnum.marketingType: - return { orderBy: "listings.marketingType", orderDir } - case OrderByFieldsEnum.applicationDates: - case undefined: - // Default to ordering by applicationDates (i.e. applicationDueDate - // and applicationOpenDate) if no orderBy param is specified. - return { orderBy: "listings.applicationDueDate", orderDir } - default: - throw new HttpException( - `OrderBy parameter not recognized or not yet implemented.`, - HttpStatus.NOT_IMPLEMENTED - ) - } - } - - private static buildOrderByConditions(params: ListingsQueryParams): Array { - if (!params.orderDir || !params.orderBy) { - return [ - ListingsService.getOrderByCondition(OrderByFieldsEnum.applicationDates, OrderParam.ASC), - ] - } - const orderByConditionDataArray = [] - for (let i = 0; i < params.orderDir.length; i++) { - orderByConditionDataArray.push( - ListingsService.getOrderByCondition(params.orderBy[i], params.orderDir[i]) - ) - } - return orderByConditionDataArray - } - public async list(params: ListingsQueryParams): Promise> { // Inner query to get the sorted listing ids of the listings to display // TODO(avaleske): Only join the tables we need for the filters that are applied @@ -195,12 +159,18 @@ export class ListingsService { } async create(listingDto: ListingCreateDto) { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + await this.authzService.canOrThrow(this.req.user as User, "listing", authzActions.create, { + jurisdictionId: listingDto.jurisdiction.id, + }) + const listing = this.listingRepository.create({ ...listingDto, publishedAt: listingDto.status === ListingStatus.active ? new Date() : null, closedAt: listingDto.status === ListingStatus.closed ? new Date() : null, property: plainToClass(PropertyCreateDto, listingDto), }) + return await listing.save() } @@ -212,6 +182,9 @@ export class ListingsService { if (!listing) { throw new NotFoundException() } + + await this.authorizeUserActionForListingId(this.req.user, listing.id, authzActions.update) + const availableUnits = listingDto.listingAvailability === ListingAvailability.availableUnits ? listingDto.units.length @@ -221,8 +194,8 @@ export class ListingsService { delete unit.id } }) - listingDto.unitsAvailable = availableUnits + Object.assign(listing, { ...plainToClass(Listing, listingDto, { excludeExtraneousValues: true }), publishedAt: @@ -253,6 +226,9 @@ export class ListingsService { const listing = await this.listingRepository.findOneOrFail({ where: { id: listingId }, }) + + await this.authorizeUserActionForListingId(this.req.user, listing.id, authzActions.delete) + return await this.listingRepository.remove(listing) } @@ -264,6 +240,7 @@ export class ListingsService { "listingPreferences.ordinal": "ASC", }) .getOne() + if (!result) { throw new NotFoundException() } @@ -276,16 +253,6 @@ export class ListingsService { return result } - private async addUnitsSummarized(listing: Listing) { - if (Array.isArray(listing.property.units) && listing.property.units.length > 0) { - const amiCharts = await this.amiChartsRepository.find({ - where: { id: In(listing.property.units.map((unit) => unit.amiChartId)) }, - }) - listing.unitsSummarized = summarizeUnits(listing.property.units, amiCharts, listing) - } - return listing - } - @Interval(1000 * 60 * 60) public async changeOverdueListingsStatusCron() { const listings = await this.listingRepository @@ -295,6 +262,7 @@ export class ListingsService { .andWhere(`listings.applicationDueDate IS NOT NULL`) .andWhere(`listings.applicationDueDate < NOW()`) .getMany() + for (const listing of listings) { listing.status = ListingStatus.closed listing.closedAt = new Date() @@ -302,4 +270,79 @@ export class ListingsService { await this.listingRepository.save(listings) } + + private async addUnitsSummarized(listing: Listing) { + if (Array.isArray(listing.property.units) && listing.property.units.length > 0) { + const amiCharts = await this.amiChartsRepository.find({ + where: { id: In(listing.property.units.map((unit) => unit.amiChartId)) }, + }) + listing.unitsSummarized = summarizeUnits(listing.property.units, amiCharts, listing) + } + return listing + } + + private static getOrderByCondition( + orderBy: OrderByFieldsEnum, + orderDir: OrderParam + ): OrderByConditionData { + switch (orderBy) { + case OrderByFieldsEnum.mostRecentlyUpdated: + return { orderBy: "listings.updated_at", orderDir } + case OrderByFieldsEnum.status: + return { orderBy: "listings.status", orderDir } + case OrderByFieldsEnum.name: + return { orderBy: "listings.name", orderDir } + case OrderByFieldsEnum.waitlistOpen: + return { orderBy: "listings.isWaitlistOpen", orderDir } + case OrderByFieldsEnum.unitsAvailable: + return { orderBy: "property.unitsAvailable", orderDir } + case OrderByFieldsEnum.mostRecentlyClosed: + return { + orderBy: "listings.closedAt", + orderDir, + nulls: "NULLS LAST", + } + case OrderByFieldsEnum.marketingType: + return { orderBy: "listings.marketingType", orderDir } + case OrderByFieldsEnum.applicationDates: + case undefined: + // Default to ordering by applicationDates (i.e. applicationDueDate + // and applicationOpenDate) if no orderBy param is specified. + return { orderBy: "listings.applicationDueDate", orderDir } + default: + throw new HttpException( + `OrderBy parameter not recognized or not yet implemented.`, + HttpStatus.NOT_IMPLEMENTED + ) + } + } + + private static buildOrderByConditions(params: ListingsQueryParams): Array { + if (!params.orderDir || !params.orderBy) { + return [ + ListingsService.getOrderByCondition(OrderByFieldsEnum.applicationDates, OrderParam.ASC), + ] + } + + const orderByConditionDataArray = [] + for (let i = 0; i < params.orderDir.length; i++) { + orderByConditionDataArray.push( + ListingsService.getOrderByCondition(params.orderBy[i], params.orderDir[i]) + ) + } + + return orderByConditionDataArray + } + + private async authorizeUserActionForListingId(user, listingId: string, action) { + /** + * Checking authorization for each application is very expensive. By making lisitngId required, we can check if the user has update permissions for the listing, since right now if a user has that they also can run the export for that listing + */ + const jurisdictionId = await this.listingRepository.getJurisdictionIdByListingId(listingId) + + return await this.authzService.canOrThrow(user, "listing", action, { + id: listingId, + jurisdictionId, + }) + } } diff --git a/backend/core/src/listings/repositories/listing.repository.ts b/backend/core/src/listings/repositories/listing.repository.ts new file mode 100644 index 0000000000..8ef47664ac --- /dev/null +++ b/backend/core/src/listings/repositories/listing.repository.ts @@ -0,0 +1,19 @@ +import Listing from "../entities/listing.entity" +import { EntityRepository, Repository } from "typeorm" + +@EntityRepository(Listing) +export class ListingRepository extends Repository { + public async getJurisdictionIdByListingId(listingId: string | null): Promise { + if (!listingId) { + return null + } + + const listing = await this.createQueryBuilder("listings") + .where("listings.id = :listingId", { listingId }) + .leftJoin("listings.jurisdiction", "jurisdiction") + .select(["listings.id", "jurisdiction.id"]) + .getOne() + + return listing.jurisdiction.id + } +} diff --git a/backend/core/src/listings/tests/listings.service.spec.ts b/backend/core/src/listings/tests/listings.service.spec.ts index 178c33b1af..3fb961fd33 100644 --- a/backend/core/src/listings/tests/listings.service.spec.ts +++ b/backend/core/src/listings/tests/listings.service.spec.ts @@ -10,6 +10,8 @@ import { Compare } from "../../shared/dto/filter.dto" import { ListingFilterParams } from "../dto/listing-filter-params" import { OrderByFieldsEnum } from "../types/listing-orderby-enum" import { OrderParam } from "../../applications/types/order-param" +import { AuthzService } from "../../auth/services/authz.service" +import { ListingRepository } from "../repositories/listing.repository" // Cypress brings in Chai types for the global expect, but we want to use jest // expect here so we need to re-declare it. @@ -106,8 +108,9 @@ describe("ListingsService", () => { const module: TestingModule = await Test.createTestingModule({ providers: [ ListingsService, + AuthzService, { - provide: getRepositoryToken(Listing), + provide: getRepositoryToken(ListingRepository), useValue: mockListingsRepo, }, { @@ -121,7 +124,7 @@ describe("ListingsService", () => { ], }).compile() - service = module.get(ListingsService) + service = await module.resolve(ListingsService) }) afterEach(() => { diff --git a/backend/core/src/migration/1654612292843-add-jurisdictional-admin-role.ts b/backend/core/src/migration/1654612292843-add-jurisdictional-admin-role.ts new file mode 100644 index 0000000000..269c324615 --- /dev/null +++ b/backend/core/src/migration/1654612292843-add-jurisdictional-admin-role.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from "typeorm" + +export class addJurisdictionalAdminRole1654612292843 implements MigrationInterface { + name = "addJurisdictionalAdminRole1654612292843" + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "user_roles" ADD "is_jurisdictional_admin" boolean NOT NULL DEFAULT false` + ) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "user_roles" DROP COLUMN "is_jurisdictional_admin"`) + } +} diff --git a/backend/core/src/seeder/seed.ts b/backend/core/src/seeder/seed.ts index 335e7c35fc..2740224ddc 100644 --- a/backend/core/src/seeder/seed.ts +++ b/backend/core/src/seeder/seed.ts @@ -34,7 +34,6 @@ import { } from "./seeds/listings/shared" import { UserCreateDto } from "../auth/dto/user-create.dto" import { AmiDefaultSanJose } from "./seeds/ami-charts/default-ami-chart-san-jose" -import { AuthContext } from "../auth/types/auth-context" import { createJurisdictions } from "./seeds/jurisdictions" import { AmiDefaultMissingAMI } from "./seeds/ami-charts/missing-household-ami-levels" import { SeederModule } from "./seeder.module" @@ -53,6 +52,7 @@ import { ApplicationMethodsService } from "../application-methods/application-me import { ApplicationMethodType } from "../application-methods/types/application-method-type-enum" import { UnitTypesService } from "../unit-types/unit-types.service" import dayjs from "dayjs" +import { CountyCode } from "../shared/types/county-code" const argv = yargs.scriptName("seed").options({ test: { type: "boolean", default: false }, @@ -106,8 +106,7 @@ export async function createLeasingAgents( plainToClass(UserCreateDto, { ...leasingAgent, jurisdictions: [jurisdictions[0]], - }), - new AuthContext(null) + }) ) ) ) @@ -246,8 +245,7 @@ async function seed() { password: "abcdef", passwordConfirmation: "abcdef", jurisdictions: [jurisdictions[0]], - }), - new AuthContext(null) + }) ) await userService.confirm({ token: user1.confirmationToken }) @@ -263,8 +261,7 @@ async function seed() { password: "ghijkl", passwordConfirmation: "ghijkl", jurisdictions: [jurisdictions[0]], - }), - new AuthContext(null) + }) ) await userService.confirm({ token: user2.confirmationToken }) @@ -280,8 +277,7 @@ async function seed() { password: "abcdef", passwordConfirmation: "abcdef", jurisdictions: [jurisdictions[0]], - }), - new AuthContext(null) + }) ) // create user with expired password @@ -296,9 +292,7 @@ async function seed() { password: "abcdef", passwordConfirmation: "abcdef", jurisdictions: [jurisdictions[0]], - roles: { isAdmin: false, isPartner: true }, - }), - new AuthContext(null) + }) ) await userService.confirm({ token: userExpiredPassword.confirmationToken }) @@ -320,9 +314,11 @@ async function seed() { password: "abcdef", passwordConfirmation: "abcdef", jurisdictions, - }), - new AuthContext(null) + }) ) + const roles: UserRoles = { user: admin, isPartner: true, isAdmin: true } + await rolesRepo.save(roles) + await userService.confirm({ token: admin.confirmationToken }) const mfaUser = await userService.createPublicUser( plainToClass(UserCreateDto, { @@ -335,9 +331,43 @@ async function seed() { password: "abcdef12", passwordConfirmation: "abcdef12", jurisdictions, - }), - new AuthContext(null) + }) + ) + await userRepo.save(mfaUser) + await userRepo.save({ + ...mfaUser, + mfaEnabled: true, + mfaCode: "123456", + mfaCodeUpdatedAt: dayjs(new Date()).add(1, "day"), + }) + const mfaRoles: UserRoles = { user: mfaUser, isPartner: true, isAdmin: true } + await rolesRepo.save(mfaRoles) + await userService.confirm({ token: mfaUser.confirmationToken }) + + const alamedaJurisdiction = jurisdictions.filter((j) => j.name == CountyCode.alameda)[0] + const alamedaAdmin = await userService.createPublicUser( + plainToClass(UserCreateDto, { + email: "alameda-admin@example.com", + emailConfirmation: "alameda-admin@example.com", + firstName: "Alameda", + middleName: "Admin", + lastName: "MFA", + dob: new Date(), + password: "abcdef", + passwordConfirmation: "abcdef", + jurisdictions: [alamedaJurisdiction], + }) ) + await userRepo.save(alamedaAdmin) + await userService.confirm({ token: alamedaAdmin.confirmationToken }) + + const alamedaAdminRoles: UserRoles = { + user: alamedaAdmin, + isPartner: false, + isAdmin: false, + isJurisdictionalAdmin: true, + } + await rolesRepo.save(alamedaAdminRoles) const unitTypesService = await app.resolve(UnitTypesService) @@ -352,20 +382,6 @@ async function seed() { } } - await userRepo.save(admin) - await userRepo.save({ - ...mfaUser, - mfaEnabled: true, - mfaCode: "123456", - mfaCodeUpdatedAt: dayjs(new Date()).add(1, "day"), - }) - const roles: UserRoles = { user: admin, isPartner: true, isAdmin: true } - const mfaRoles: UserRoles = { user: mfaUser, isPartner: true, isAdmin: true } - await rolesRepo.save(roles) - await rolesRepo.save(mfaRoles) - - await userService.confirm({ token: admin.confirmationToken }) - await userService.confirm({ token: mfaUser.confirmationToken }) await app.close() } diff --git a/backend/core/src/seeder/seeds/jurisdictions.ts b/backend/core/src/seeder/seeds/jurisdictions.ts index 62e4d742aa..5fb046fbff 100644 --- a/backend/core/src/seeder/seeds/jurisdictions.ts +++ b/backend/core/src/seeder/seeds/jurisdictions.ts @@ -58,6 +58,7 @@ export async function createJurisdictions(app: INestApplicationContext) { const jurisdictionService = await app.resolve(JurisdictionsService) // some jurisdictions are added via previous migrations const jurisdictions = await jurisdictionService.list() + console.log({ jurisdictions }) const toInsert = defaultJurisdictions.filter( (rec) => jurisdictions.findIndex((item) => item.name === rec.name) === -1 ) diff --git a/backend/core/test/applications/applications.e2e-spec.ts b/backend/core/test/applications/applications.e2e-spec.ts index 739bebe967..60710f49da 100644 --- a/backend/core/test/applications/applications.e2e-spec.ts +++ b/backend/core/test/applications/applications.e2e-spec.ts @@ -23,6 +23,7 @@ import { UserCreateDto } from "../../src/auth/dto/user-create.dto" import { Listing } from "../../src/listings/entities/listing.entity" import { EmailService } from "../../src/email/email.service" import { UserRepository } from "../../src/auth/repositories/user-repository" +import { ListingRepository } from "../../src/listings/repositories/listing.repository" // Cypress brings in Chai types for the global expect, but we want to use jest // expect here so we need to re-declare it. @@ -54,7 +55,13 @@ describe("Applications", () => { AuthModule, ListingsModule, ApplicationsModule, - TypeOrmModule.forFeature([Application, HouseholdMember, Listing, UserRepository]), + TypeOrmModule.forFeature([ + Application, + HouseholdMember, + Listing, + UserRepository, + ListingRepository, + ]), ThrottlerModule.forRoot({ ttl: 60, limit: 2, @@ -450,14 +457,16 @@ describe("Applications", () => { .send(body) .set(...setAuthorization(user1AccessToken)) .expect(201) + await supertest(app.getHttpServer()) .delete(`/applications/${createRes.body.id}`) .set(...setAuthorization(adminAccessToken)) .expect(200) + await supertest(app.getHttpServer()) .get(`/applications/${createRes.body.id}`) .set(...setAuthorization(user1AccessToken)) - .expect(500) + .expect(404) }) it(`should disallow users to delete their own applications`, async () => { diff --git a/backend/core/test/authz/authz.e2e-spec.ts b/backend/core/test/authz/authz.e2e-spec.ts index 2dfa5c7cdf..f2c6a448bc 100644 --- a/backend/core/test/authz/authz.e2e-spec.ts +++ b/backend/core/test/authz/authz.e2e-spec.ts @@ -10,18 +10,26 @@ import { getUserAccessToken } from "../utils/get-user-access-token" import { setAuthorization } from "../utils/set-authorization-helper" import { UserDto } from "../../src/auth/dto/user.dto" import { v4 as uuidv4 } from "uuid" -import { Repository } from "typeorm" +import { DeepPartial, Repository } from "typeorm" import { Application } from "../../src/applications/entities/application.entity" import { getRepositoryToken } from "@nestjs/typeorm" -import { getTestAppBody } from "../lib/get-test-app-body" import { Listing } from "../../src/listings/entities/listing.entity" import { ApplicationDto } from "../../src/applications/dto/application.dto" +import { Jurisdiction } from "../../src/jurisdictions/entities/jurisdiction.entity" +import * as uuid from "uuid" +import { User } from "../../src/auth/entities/user.entity" +import { PasswordService } from "../../src/auth/services/password.service" +import { makeTestListing } from "../utils/make-test-listing" +import { UserInviteDto } from "../../src/auth/dto/user-invite.dto" +import { EmailService } from "../../src/email/email.service" +import { getTestAppBody } from "../lib/get-test-app-body" jest.setTimeout(30000) describe("Authz", () => { let app: INestApplication let userAccessToken: string + let adminAccessToken: string let userProfile: UserDto const adminOnlyEndpoints = ["/preferences", "/units", "/translations"] const applicationsEndpoint = "/applications" @@ -29,33 +37,57 @@ describe("Authz", () => { const userEndpoint = "/user" let applicationsRepository: Repository let listingsRepository: Repository+ let jurisdictionsRepository: Repository + let usersRepository: Repository + let passwordService: PasswordService let app1: ApplicationDto + let listing1Id: string beforeAll(async () => { + const testEmailService = { + // eslint-disable-next-line @typescript-eslint/no-empty-function + invite: async () => {}, + // eslint-disable-next-line @typescript-eslint/no-empty-function + confirmation: async () => {}, + } + const moduleRef = await Test.createTestingModule({ imports: [AppModule.register(dbOptions)], - }).compile() + }) + .overrideProvider(EmailService) + .useValue(testEmailService) + .compile() app = moduleRef.createNestApplication() app = applicationSetup(app) await app.init() userAccessToken = await getUserAccessToken(app, "test@example.com", "abcdef") + adminAccessToken = await getUserAccessToken(app, "admin@example.com", "abcdef") + userProfile = ( await supertest(app.getHttpServer()) .get("/user") .set(...setAuthorization(userAccessToken)) ).body + applicationsRepository = app.get>(getRepositoryToken(Application)) listingsRepository = app.get>(getRepositoryToken(Listing)) + jurisdictionsRepository = app.get>(getRepositoryToken(Jurisdiction)) + usersRepository = app.get>(getRepositoryToken(User)) + passwordService = app.get(PasswordService) + const listings = await listingsRepository.find({ take: 1 }) - const listing1Application = getTestAppBody(listings[0].id) + listing1Id = listings[0].id + + const listing1Application = getTestAppBody(listing1Id) app1 = ( await supertest(app.getHttpServer()) .post(`/applications/submit`) .send(listing1Application) .set("jurisdictionName", "Alameda") .set(...setAuthorization(userAccessToken)) + .expect(201) ).body }) @@ -182,6 +214,7 @@ describe("Authz", () => { .put(applicationsEndpoint + `/${app1.id}`) .send(app1) .expect(403) + // logged in normal user await supertest(app.getHttpServer()) .put(applicationsEndpoint + `/${app1.id}`) @@ -214,31 +247,41 @@ describe("Authz", () => { it(`should not allow normal/anonymous user to DELETE listings`, async () => { // anonymous await supertest(app.getHttpServer()) - .delete(listingsEndpoint + `/${uuidv4()}`) + .delete(listingsEndpoint + `/${listing1Id}`) .expect(403) // logged in normal user await supertest(app.getHttpServer()) - .delete(listingsEndpoint + `/${uuidv4()}`) + .delete(listingsEndpoint + `/${listing1Id}`) .set(...setAuthorization(userAccessToken)) .expect(403) }) it(`should not allow normal/anonymous user to PUT listings`, async () => { // anonymous await supertest(app.getHttpServer()) - .put(listingsEndpoint + `/${uuidv4()}`) + .put(listingsEndpoint + `/${listing1Id}`) + .send({ id: listing1Id }) .expect(403) + // logged in normal user await supertest(app.getHttpServer()) - .put(listingsEndpoint + `/${uuidv4()}`) + .put(listingsEndpoint + `/${listing1Id}`) + .send({ id: listing1Id }) .set(...setAuthorization(userAccessToken)) .expect(403) }) it(`should not allow normal/anonymous user to POST listings`, async () => { + const anyJurisdiction = (await jurisdictionsRepository.find({ take: 1 }))[0] + // anonymous - await supertest(app.getHttpServer()).post(listingsEndpoint).expect(403) + await supertest(app.getHttpServer()) + .post(listingsEndpoint) + .send({ jurisdiction: { id: anyJurisdiction.id } }) + .expect(403) + // logged in normal user await supertest(app.getHttpServer()) .post(listingsEndpoint) + .send({ jurisdiction: { id: anyJurisdiction.id } }) .set(...setAuthorization(userAccessToken)) .expect(403) }) @@ -265,6 +308,329 @@ describe("Authz", () => { }) }) + describe("jurisdictional admin permissions", () => { + it("can add/view/edit all listings within their jurisdiction", async () => { + const jurisdiction = await jurisdictionsRepository.save({ + name: `j-${uuid.v4()}`, + rentalAssistanceDefault: "", + enableAccessibilityFeatures: false, + }) + + const password = "abcdef" + const createJurisdictionalAdminDto: DeepPartial = { + email: `j-admin-${uuid.v4()}@example.com`, + firstName: "first", + middleName: "mid", + lastName: "last", + dob: new Date(), + passwordHash: await passwordService.passwordToHash(password), + jurisdictions: [jurisdiction], + confirmedAt: new Date(), + mfaEnabled: false, + roles: { isJurisdictionalAdmin: true }, + } + + await usersRepository.save(createJurisdictionalAdminDto) + + const jurisdictionalAdminAccessToken = await getUserAccessToken( + app, + createJurisdictionalAdminDto.email, + password + ) + + const newListingCreateDto = makeTestListing(jurisdiction.id) + let listingResponse = await supertest(app.getHttpServer()) + .post(`/listings`) + .send(newListingCreateDto) + .set(...setAuthorization(jurisdictionalAdminAccessToken)) + .expect(201) + expect(listingResponse.body.jurisdiction.id).toBe(jurisdiction.id) + + listingResponse = await supertest(app.getHttpServer()) + .get(`/listings/${listingResponse.body.id}`) + .set(...setAuthorization(jurisdictionalAdminAccessToken)) + .expect(200) + expect(listingResponse.body.name).toBe(newListingCreateDto.name) + + const changeName = "new changed name" + listingResponse = await supertest(app.getHttpServer()) + .put(`/listings/${listingResponse.body.id}`) + .send({ + ...listingResponse.body, + name: changeName, + }) + .set(...setAuthorization(jurisdictionalAdminAccessToken)) + .expect(200) + + expect(listingResponse.body.name).toBe(changeName) + }) + + it("can add/view/edit all users within their jurisdiction", async () => { + // create jurisdiction + const jurisdiction = await jurisdictionsRepository.save({ + name: `j-${uuid.v4()}`, + rentalAssistanceDefault: "", + enableAccessibilityFeatures: false, + }) + + const password = "abcdef" + const createJurisdictionalAdminDto: DeepPartial = { + email: `j-admin-${uuid.v4()}@example.com`, + firstName: "first", + middleName: "mid", + lastName: "last", + dob: new Date(), + passwordHash: await passwordService.passwordToHash(password), + jurisdictions: [jurisdiction], + confirmedAt: new Date(), + mfaEnabled: false, + roles: { isJurisdictionalAdmin: true }, + } + + await usersRepository.save(createJurisdictionalAdminDto) + + const jurisdictionalAdminAccessToken = await getUserAccessToken( + app, + createJurisdictionalAdminDto.email, + password + ) + // use jurisdictional admin account to create new user and edit it + + const newListingCreateDto = makeTestListing(jurisdiction.id) + const listingResponse = await supertest(app.getHttpServer()) + .post(`/listings`) + .send(newListingCreateDto) + .set(...setAuthorization(jurisdictionalAdminAccessToken)) + .expect(201) + expect(listingResponse.body.jurisdiction.id).toBe(jurisdiction.id) + + const userInviteDto: UserInviteDto = { + email: `partner-${uuid.v4()}@example.com`, + firstName: "Name", + lastName: "Name", + jurisdictions: [jurisdiction], + leasingAgentInListings: [{ id: listingResponse.body.id }], + roles: { isPartner: true }, + } + + await supertest(app.getHttpServer()) + .post(`/user/invite`) + .send(userInviteDto) + .set("jurisdictionName", jurisdiction.name) + .set(...setAuthorization(jurisdictionalAdminAccessToken)) + .expect(201) + }) + + it("cannot add new listings to other jurisdiction", async () => { + // create jurisdiction + const jurisdiction1 = await jurisdictionsRepository.save({ + name: `j-${uuid.v4()}`, + rentalAssistanceDefault: "", + enableAccessibilityFeatures: false, + }) + + const jurisdiction2 = await jurisdictionsRepository.save({ + name: `j-${uuid.v4()}`, + rentalAssistanceDefault: "", + enableAccessibilityFeatures: false, + }) + + const password = "abcdef" + const createJurisdictionalAdminDto: DeepPartial = { + email: `j-admin-${uuid.v4()}@example.com`, + firstName: "first", + middleName: "mid", + lastName: "last", + dob: new Date(), + passwordHash: await passwordService.passwordToHash(password), + jurisdictions: [jurisdiction1], + confirmedAt: new Date(), + mfaEnabled: false, + roles: { isJurisdictionalAdmin: true }, + } + + await usersRepository.save(createJurisdictionalAdminDto) + + const jurisdictionalAdminAccessToken = await getUserAccessToken( + app, + createJurisdictionalAdminDto.email, + password + ) + + const newListingCreateDto = makeTestListing(jurisdiction2.id) + await supertest(app.getHttpServer()) + .post(`/listings`) + .send(newListingCreateDto) + .set(...setAuthorization(jurisdictionalAdminAccessToken)) + .expect(403) + }) + + it("cannot add new users to other jurisdiction", async () => { + const jurisdiction1 = await jurisdictionsRepository.save({ + name: `j-${uuid.v4()}`, + rentalAssistanceDefault: "", + enableAccessibilityFeatures: false, + }) + + const jurisdiction2 = await jurisdictionsRepository.save({ + name: `j-${uuid.v4()}`, + rentalAssistanceDefault: "", + enableAccessibilityFeatures: false, + }) + + const password = "abcdef" + const createJurisdictionalAdminDto: DeepPartial = { + email: `j-admin-${uuid.v4()}@example.com`, + firstName: "first", + middleName: "mid", + lastName: "last", + dob: new Date(), + passwordHash: await passwordService.passwordToHash(password), + jurisdictions: [jurisdiction1], + confirmedAt: new Date(), + mfaEnabled: false, + roles: { isJurisdictionalAdmin: true }, + } + + await usersRepository.save(createJurisdictionalAdminDto) + + const jurisdictionalAdminAccessToken = await getUserAccessToken( + app, + createJurisdictionalAdminDto.email, + password + ) + + const newListingCreateDto = makeTestListing(jurisdiction2.id) + const listingResponse = await supertest(app.getHttpServer()) + .post(`/listings`) + .send(newListingCreateDto) + .set(...setAuthorization(adminAccessToken)) + .expect(201) + expect(listingResponse.body.jurisdiction.id).toBe(jurisdiction2.id) + + const userInviteDto: UserInviteDto = { + email: `partner-${uuid.v4()}@example.com`, + firstName: "Name", + lastName: "Name", + jurisdictions: [jurisdiction2], + leasingAgentInListings: [{ id: listingResponse.body.id }], + roles: { isPartner: true }, + } + + await supertest(app.getHttpServer()) + .post(`/user/invite`) + .send(userInviteDto) + .set("jurisdictionName", jurisdiction2.name) + .set(...setAuthorization(jurisdictionalAdminAccessToken)) + .expect(403) + }) + + it("cannot add new super admins", async () => { + const jurisdiction1 = await jurisdictionsRepository.save({ + name: `j-${uuid.v4()}`, + rentalAssistanceDefault: "", + enableAccessibilityFeatures: false, + }) + + const password = "abcdef" + const createJurisdictionalAdminDto: DeepPartial = { + email: `j-admin-${uuid.v4()}@example.com`, + firstName: "first", + middleName: "mid", + lastName: "last", + dob: new Date(), + passwordHash: await passwordService.passwordToHash(password), + jurisdictions: [jurisdiction1], + confirmedAt: new Date(), + mfaEnabled: false, + roles: { isJurisdictionalAdmin: true }, + } + + await usersRepository.save(createJurisdictionalAdminDto) + + const jurisdictionalAdminAccessToken = await getUserAccessToken( + app, + createJurisdictionalAdminDto.email, + password + ) + + const userInviteDto: UserInviteDto = { + email: `partner-${uuid.v4()}@example.com`, + firstName: "Name", + lastName: "Name", + jurisdictions: [jurisdiction1], + roles: { isAdmin: true }, + } + + await supertest(app.getHttpServer()) + .post(`/user/invite`) + .send(userInviteDto) + .set("jurisdictionName", jurisdiction1.name) + .set(...setAuthorization(jurisdictionalAdminAccessToken)) + .expect(403) + }) + + it("can add new jurisdictional admins to own jurisdiction but not other", async () => { + const jurisdiction1 = await jurisdictionsRepository.save({ + name: `j-${uuid.v4()}`, + rentalAssistanceDefault: "", + enableAccessibilityFeatures: false, + }) + + const jurisdiction2 = await jurisdictionsRepository.save({ + name: `j-${uuid.v4()}`, + rentalAssistanceDefault: "", + enableAccessibilityFeatures: false, + }) + + const password = "abcdef" + const createJurisdictionalAdminDto: DeepPartial = { + email: `j-admin-${uuid.v4()}@example.com`, + firstName: "first", + middleName: "mid", + lastName: "last", + dob: new Date(), + passwordHash: await passwordService.passwordToHash(password), + jurisdictions: [jurisdiction1], + confirmedAt: new Date(), + mfaEnabled: false, + roles: { isJurisdictionalAdmin: true }, + } + + await usersRepository.save(createJurisdictionalAdminDto) + + const jurisdictionalAdminAccessToken = await getUserAccessToken( + app, + createJurisdictionalAdminDto.email, + password + ) + + const userInviteDto: UserInviteDto = { + email: `partner-${uuid.v4()}@example.com`, + firstName: "Name", + lastName: "Name", + jurisdictions: [jurisdiction1], + roles: { isJurisdictionalAdmin: true }, + } + + await supertest(app.getHttpServer()) + .post(`/user/invite`) + .send(userInviteDto) + .set("jurisdictionName", jurisdiction1.name) + .set(...setAuthorization(jurisdictionalAdminAccessToken)) + .expect(201) + + userInviteDto.jurisdictions = [jurisdiction2] + + await supertest(app.getHttpServer()) + .post(`/user/invite`) + .send(userInviteDto) + .set("jurisdictionName", jurisdiction1.name) + .set(...setAuthorization(jurisdictionalAdminAccessToken)) + .expect(403) + }) + }) + afterEach(() => { jest.clearAllMocks() }) diff --git a/backend/core/test/listings/listings.e2e-spec.ts b/backend/core/test/listings/listings.e2e-spec.ts index 4976b4ee80..fc00bf524a 100644 --- a/backend/core/test/listings/listings.e2e-spec.ts +++ b/backend/core/test/listings/listings.e2e-spec.ts @@ -22,7 +22,7 @@ import { Program } from "../../src/program/entities/program.entity" import { Repository } from "typeorm" import { INestApplication } from "@nestjs/common" import { Jurisdiction } from "../../src/jurisdictions/entities/jurisdiction.entity" -import { makeTestListing } from "./utils/make-test-listing" +import { makeTestListing } from "../utils/make-test-listing" // eslint-disable-next-line @typescript-eslint/no-var-requires import dbOptions from "../../ormconfig.test" @@ -51,6 +51,7 @@ describe("Listings", () => { TypeOrmModule.forFeature([Program]), ], }).compile() + app = moduleRef.createNestApplication() app = applicationSetup(app) await app.init() @@ -82,13 +83,23 @@ describe("Listings", () => { it("should return the last page of paginated listings", async () => { // Make the limit 1 less than the full number of listings, so that the second page contains // only one listing. - const queryParams = { - limit: 15, + // query to get max number of listings + let queryParams = { + limit: 1, + page: 1, + view: "base", + } + let query = qs.stringify(queryParams) + let res = await supertest(app.getHttpServer()).get(`/listings?${query}`).expect(200) + const totalItems = res.body.meta.totalItems + + queryParams = { + limit: totalItems - 1, page: 2, view: "base", } - const query = qs.stringify(queryParams) - const res = await supertest(app.getHttpServer()).get(`/listings?${query}`).expect(200) + query = qs.stringify(queryParams) + res = await supertest(app.getHttpServer()).get(`/listings?${query}`).expect(200) expect(res.body.items.length).toEqual(1) }) diff --git a/backend/core/test/user/user.e2e-spec.ts b/backend/core/test/user/user.e2e-spec.ts index 1b84f3e657..25d2e31b69 100644 --- a/backend/core/test/user/user.e2e-spec.ts +++ b/backend/core/test/user/user.e2e-spec.ts @@ -280,10 +280,8 @@ describe("Applications", () => { firstName: "First", lastName: "Last", email: "test2@example.com", - jurisdictions: user2Profile.jurisdictions.map((jurisdiction) => ({ - id: jurisdiction.id, - })), agreedToTermsOfService: false, + jurisdictions: [], } await supertest(app.getHttpServer()) .put(`/user/${user2UpdateDto.id}`) @@ -384,8 +382,8 @@ describe("Applications", () => { lastName: "Partner", dob: new Date(), leasingAgentInListings: [{ id: listing.id }], - roles: { isPartner: true }, jurisdictions: [{ id: jurisdiction.id }], + roles: { isPartner: true }, } const mockInvite = jest.spyOn(testEmailService, "invite") @@ -555,22 +553,19 @@ describe("Applications", () => { }) it("should allow filtering by isPartner user role", async () => { - const user = await userRepository._createUser( - { - dob: new Date(), - email: "michalp@airnauts.com", - firstName: "Michal", - jurisdictions: [], - language: Language.en, - lastName: "", - middleName: "", - roles: { isPartner: true, isAdmin: false }, - updatedAt: undefined, - passwordHash: "abcd", - mfaEnabled: false, - }, - null - ) + const user = await userRepository._createUser({ + dob: new Date(), + email: "michalp@airnauts.com", + firstName: "Michal", + jurisdictions: [], + language: Language.en, + lastName: "", + middleName: "", + roles: { isPartner: true, isAdmin: false }, + updatedAt: undefined, + passwordHash: "abcd", + mfaEnabled: false, + }) const filters = [ { @@ -594,22 +589,19 @@ describe("Applications", () => { }) it("should get and delete a user by ID", async () => { - const user = await userRepository._createUser( - { - dob: new Date(), - email: "test+1@test.com", - firstName: "test", - jurisdictions: [], - language: Language.en, - lastName: "", - middleName: "", - roles: { isPartner: true, isAdmin: false }, - updatedAt: undefined, - passwordHash: "abcd", - mfaEnabled: false, - }, - null - ) + const user = await userRepository._createUser({ + dob: new Date(), + email: "test+1@test.com", + firstName: "test", + jurisdictions: [], + language: Language.en, + lastName: "", + middleName: "", + roles: { isPartner: true, isAdmin: false }, + updatedAt: undefined, + passwordHash: "abcd", + mfaEnabled: false, + }) const res = await supertest(app.getHttpServer()) .get(`/user/${user.id}`) @@ -631,22 +623,19 @@ describe("Applications", () => { it("should create and delete a user with existing application by ID", async () => { const listing = (await listingRepository.find({ take: 1 }))[0] - const user = await userRepository._createUser( - { - dob: new Date(), - email: "test+1@test.com", - firstName: "test", - jurisdictions: [], - language: Language.en, - lastName: "", - middleName: "", - roles: { isPartner: true, isAdmin: false }, - updatedAt: undefined, - passwordHash: "abcd", - mfaEnabled: false, - }, - null - ) + const user = await userRepository._createUser({ + dob: new Date(), + email: "test+1@test.com", + firstName: "test", + jurisdictions: [], + language: Language.en, + lastName: "", + middleName: "", + roles: { isPartner: true, isAdmin: false }, + updatedAt: undefined, + passwordHash: "abcd", + mfaEnabled: false, + }) const applicationUpdate = getTestAppBody(listing.id) const newApp = await applicationsRepository.save({ ...applicationUpdate, diff --git a/backend/core/test/listings/utils/make-test-listing.ts b/backend/core/test/utils/make-test-listing.ts similarity index 96% rename from backend/core/test/listings/utils/make-test-listing.ts rename to backend/core/test/utils/make-test-listing.ts index 655d92c5c7..43ba42e61a 100644 --- a/backend/core/test/listings/utils/make-test-listing.ts +++ b/backend/core/test/utils/make-test-listing.ts @@ -1,5 +1,5 @@ -import { ListingCreateDto } from "../../../src/listings/dto/listing-create.dto" -import { ListingReviewOrder, ListingStatus } from "../../../types" +import { ListingCreateDto } from "../../src/listings/dto/listing-create.dto" +import { ListingReviewOrder, ListingStatus } from "../../types" import { BaseEntity } from "typeorm" export function makeTestListing(jurisdictionId: string): Omit { diff --git a/backend/core/types/src/backend-swagger.ts b/backend/core/types/src/backend-swagger.ts index 75d518d11f..cbe22fdb6c 100644 --- a/backend/core/types/src/backend-swagger.ts +++ b/backend/core/types/src/backend-swagger.ts @@ -3989,6 +3989,9 @@ export interface UserRoles { /** */ isAdmin?: boolean + /** */ + isJurisdictionalAdmin?: boolean + /** */ isPartner?: boolean } @@ -4287,9 +4290,6 @@ export interface UserUpdate { /** */ currentPassword?: string - /** */ - jurisdictions: Id[] - /** */ leasingAgentInListings?: Id[] @@ -4299,6 +4299,9 @@ export interface UserUpdate { /** */ appUrl?: string + /** */ + jurisdictions: Id[] + /** */ confirmedAt?: Date @@ -4350,6 +4353,9 @@ export interface UserRolesCreate { /** */ isAdmin?: boolean + /** */ + isJurisdictionalAdmin?: boolean + /** */ isPartner?: boolean } @@ -4358,15 +4364,15 @@ export interface UserInvite { /** */ language?: Language - /** */ - roles: CombinedRolesTypes - /** */ jurisdictions: Id[] /** */ leasingAgentInListings?: Id[] + /** */ + roles?: UserRolesCreate + /** */ confirmedAt?: Date @@ -6658,7 +6664,7 @@ export enum EnumJurisdictionLanguages { "zh" = "zh", "tl" = "tl", } -export type CombinedRolesTypes = UserRolesCreate +export type CombinedRolesTypes = UserRoles export enum EnumUserFilterParamsComparison { "=" = "=", "<>" = "<>", From bf6fda5095b1c37c2d9b79b042398bc91e2c43e0 Mon Sep 17 00:00:00 2001 From: "github.context.workflow" Date: Wed, 22 Jun 2022 03:10:35 +0000 Subject: [PATCH 008/598] chore(release): version - @bloom-housing/backend-core@5.0.1-alpha.2 - @bloom-housing/shared-helpers@5.0.1-alpha.3 - @bloom-housing/partners@5.0.1-alpha.3 - @bloom-housing/public@5.0.1-alpha.3 --- backend/core/CHANGELOG.md | 11 +++++++++++ backend/core/package.json | 2 +- shared-helpers/CHANGELOG.md | 8 ++++++++ shared-helpers/package.json | 4 ++-- sites/partners/CHANGELOG.md | 8 ++++++++ sites/partners/package.json | 6 +++--- sites/public/CHANGELOG.md | 8 ++++++++ sites/public/package.json | 6 +++--- 8 files changed, 44 insertions(+), 9 deletions(-) diff --git a/backend/core/CHANGELOG.md b/backend/core/CHANGELOG.md index 0f84ac9427..1c38c1f690 100644 --- a/backend/core/CHANGELOG.md +++ b/backend/core/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.2](https://github.com/bloom-housing/bloom/compare/@bloom-housing/backend-core@5.0.1-alpha.1...@bloom-housing/backend-core@5.0.1-alpha.2) (2022-06-22) + + +### Features + +* **backend:** add jurisdictional admin relation ([#2764](https://github.com/bloom-housing/bloom/issues/2764)) ([8f951f9](https://github.com/bloom-housing/bloom/commit/8f951f9a4239bb5aad179cc5567f208d34533a45)), closes [#2767](https://github.com/bloom-housing/bloom/issues/2767) [#2787](https://github.com/bloom-housing/bloom/issues/2787) [#2769](https://github.com/bloom-housing/bloom/issues/2769) [#2781](https://github.com/bloom-housing/bloom/issues/2781) + + + + + ## [5.0.1-alpha.1](https://github.com/bloom-housing/bloom/compare/@bloom-housing/backend-core@5.0.1-alpha.0...@bloom-housing/backend-core@5.0.1-alpha.1) (2022-06-17) **Note:** Version bump only for package @bloom-housing/backend-core diff --git a/backend/core/package.json b/backend/core/package.json index 6d408be396..0714a5d9d8 100644 --- a/backend/core/package.json +++ b/backend/core/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/backend-core", - "version": "5.0.1-alpha.1", + "version": "5.0.1-alpha.2", "description": "Listings service reference implementation for the Bloom affordable housing system", "author": "Sean Albert ", "private": false, diff --git a/shared-helpers/CHANGELOG.md b/shared-helpers/CHANGELOG.md index c9dd676098..bdbcdff2ce 100644 --- a/shared-helpers/CHANGELOG.md +++ b/shared-helpers/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.3](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.2...@bloom-housing/shared-helpers@5.0.1-alpha.3) (2022-06-22) + +**Note:** Version bump only for package @bloom-housing/shared-helpers + + + + + ## [5.0.1-alpha.2](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.1...@bloom-housing/shared-helpers@5.0.1-alpha.2) (2022-06-21) **Note:** Version bump only for package @bloom-housing/shared-helpers diff --git a/shared-helpers/package.json b/shared-helpers/package.json index d1deaabc4d..c7314f672a 100644 --- a/shared-helpers/package.json +++ b/shared-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/shared-helpers", - "version": "5.0.1-alpha.2", + "version": "5.0.1-alpha.3", "description": "Shared helpers for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared-helpers", "main": "index.js", @@ -16,7 +16,7 @@ "prettier": "prettier --write \"**/*.ts\"" }, "dependencies": { - "@bloom-housing/backend-core": "^5.0.1-alpha.1", + "@bloom-housing/backend-core": "^5.0.1-alpha.2", "@bloom-housing/ui-components": "^5.0.1-alpha.1" }, "devDependencies": { diff --git a/sites/partners/CHANGELOG.md b/sites/partners/CHANGELOG.md index 24b468ba7e..f302ba9e2c 100644 --- a/sites/partners/CHANGELOG.md +++ b/sites/partners/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.3](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.2...@bloom-housing/partners@5.0.1-alpha.3) (2022-06-22) + +**Note:** Version bump only for package @bloom-housing/partners + + + + + ## [5.0.1-alpha.2](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.1...@bloom-housing/partners@5.0.1-alpha.2) (2022-06-21) **Note:** Version bump only for package @bloom-housing/partners diff --git a/sites/partners/package.json b/sites/partners/package.json index 51f68a148a..32e9b661cd 100644 --- a/sites/partners/package.json +++ b/sites/partners/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/partners", - "version": "5.0.1-alpha.2", + "version": "5.0.1-alpha.3", "author": "Sean Albert ", "description": "Partners app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -26,8 +26,8 @@ "dev:server-wait-cypress": "wait-on \"http-get://localhost:${PORT:-3100}/listings\" --httpTimeout 60000 --tcpTimeout 1500 -v --interval 15000 && yarn build && yarn start" }, "dependencies": { - "@bloom-housing/backend-core": "^5.0.1-alpha.1", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.2", + "@bloom-housing/backend-core": "^5.0.1-alpha.2", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.3", "@bloom-housing/ui-components": "^5.0.1-alpha.1", "@mapbox/mapbox-sdk": "^0.13.0", "ag-grid-community": "^26.0.0", diff --git a/sites/public/CHANGELOG.md b/sites/public/CHANGELOG.md index 3ebe28904e..9ba7962391 100644 --- a/sites/public/CHANGELOG.md +++ b/sites/public/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.3](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.2...@bloom-housing/public@5.0.1-alpha.3) (2022-06-22) + +**Note:** Version bump only for package @bloom-housing/public + + + + + ## [5.0.1-alpha.2](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.1...@bloom-housing/public@5.0.1-alpha.2) (2022-06-21) diff --git a/sites/public/package.json b/sites/public/package.json index 88549a6bca..dfdb8440ae 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/public", - "version": "5.0.1-alpha.2", + "version": "5.0.1-alpha.3", "author": "Sean Albert ", "description": "Public web app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -24,8 +24,8 @@ "dev:all": "concurrently \"yarn dev:listings\" \"yarn dev:server-wait\"" }, "dependencies": { - "@bloom-housing/backend-core": "^5.0.1-alpha.1", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.2", + "@bloom-housing/backend-core": "^5.0.1-alpha.2", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.3", "@bloom-housing/ui-components": "^5.0.1-alpha.1", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", From b063f5dd0813778f1d77efa510ee3a2d788be717 Mon Sep 17 00:00:00 2001 From: Mark Buckner Date: Wed, 22 Jun 2022 11:18:14 -0600 Subject: [PATCH 009/598] feat(preferenceslist): move ordinal at mobile bp; update text color for contrast re: dahlia ticket to allow more room at responsive breakpoints in pref list and increase contrast to meet AAA standards Issue #2827 --- ui-components/src/lists/PreferencesList.scss | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/ui-components/src/lists/PreferencesList.scss b/ui-components/src/lists/PreferencesList.scss index 6acdde1871..03bc2911d6 100644 --- a/ui-components/src/lists/PreferencesList.scss +++ b/ui-components/src/lists/PreferencesList.scss @@ -6,6 +6,11 @@ @screen md { @apply ml-2; } + + @media (max-width: $screen-sm){ + margin-left: 0px; + margin-top:var(--bloom-s3); + } } } @@ -40,6 +45,11 @@ margin-left: -2.65rem; } + @media (max-width: $screen-sm){ + margin-left: 0px; + float:none ; + } + sup { @apply font-normal; top: -0.35em; @@ -47,17 +57,21 @@ } .preferences-list__subtitle { - @apply text-gray-700; + @apply text-gray-750; @apply text-tiny; @apply ml-4; @screen md { @apply ml-2; } + @media (max-width: $screen-sm){ + margin-left: 0px; + } + } .preferences-list__description { - @apply text-gray-700; + @apply text-gray-750; @apply text-sm; @apply mt-3; } From c05904c01f2303a188c5f79bca06a10ebc0c4169 Mon Sep 17 00:00:00 2001 From: Lint Action Date: Wed, 22 Jun 2022 17:52:35 +0000 Subject: [PATCH 010/598] Fix code style issues with Prettier --- ui-components/src/lists/PreferencesList.scss | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/ui-components/src/lists/PreferencesList.scss b/ui-components/src/lists/PreferencesList.scss index 03bc2911d6..cb2861f0e9 100644 --- a/ui-components/src/lists/PreferencesList.scss +++ b/ui-components/src/lists/PreferencesList.scss @@ -7,10 +7,10 @@ @apply ml-2; } - @media (max-width: $screen-sm){ + @media (max-width: $screen-sm) { margin-left: 0px; - margin-top:var(--bloom-s3); - } + margin-top: var(--bloom-s3); + } } } @@ -45,9 +45,9 @@ margin-left: -2.65rem; } - @media (max-width: $screen-sm){ + @media (max-width: $screen-sm) { margin-left: 0px; - float:none ; + float: none; } sup { @@ -64,10 +64,9 @@ @screen md { @apply ml-2; } - @media (max-width: $screen-sm){ - margin-left: 0px; + @media (max-width: $screen-sm) { + margin-left: 0px; } - } .preferences-list__description { From 4a32106d0f171822961e4dcee5ee9013ee23b329 Mon Sep 17 00:00:00 2001 From: Mark Buckner Date: Wed, 22 Jun 2022 14:54:17 -0600 Subject: [PATCH 011/598] feat(preferenceslist): add storybook example without subtitle --- ui-components/src/lists/PreferencesList.stories.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/ui-components/src/lists/PreferencesList.stories.tsx b/ui-components/src/lists/PreferencesList.stories.tsx index 87e08a32e9..367301e4ba 100644 --- a/ui-components/src/lists/PreferencesList.stories.tsx +++ b/ui-components/src/lists/PreferencesList.stories.tsx @@ -11,7 +11,6 @@ const listingPreferences = [ ordinal: 1, links: [], title: "Title 1", - subtitle: "Subtitle 1", description: "Description 1", }, { From 7034cc76a04e8c395ae3a51b2f2c4e5cef944086 Mon Sep 17 00:00:00 2001 From: Mark Buckner Date: Wed, 22 Jun 2022 16:05:26 -0600 Subject: [PATCH 012/598] feat(preferenceslist): move ordinal left; make other elements align horizontally to avoid an unwanted indention when subtitle is omitted, modify styles to support empty subtitle Issue#2827 --- ui-components/src/lists/PreferencesList.scss | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/ui-components/src/lists/PreferencesList.scss b/ui-components/src/lists/PreferencesList.scss index cb2861f0e9..43bfac8bbb 100644 --- a/ui-components/src/lists/PreferencesList.scss +++ b/ui-components/src/lists/PreferencesList.scss @@ -1,11 +1,6 @@ .preferences-list__item { .info-card__title { @apply mb-0; - @apply ml-4; - - @screen md { - @apply ml-2; - } @media (max-width: $screen-sm) { margin-left: 0px; @@ -42,7 +37,7 @@ width: 2.5rem; @screen md { - margin-left: -2.65rem; + margin-left: -3rem; } @media (max-width: $screen-sm) { @@ -59,11 +54,7 @@ .preferences-list__subtitle { @apply text-gray-750; @apply text-tiny; - @apply ml-4; - @screen md { - @apply ml-2; - } @media (max-width: $screen-sm) { margin-left: 0px; } From a1864cf945dadeafbc7ca63669bfc5e459dd45ed Mon Sep 17 00:00:00 2001 From: Jared White Date: Wed, 22 Jun 2022 18:06:35 -0700 Subject: [PATCH 013/598] 2611/listing card accessibility 2nd gen (#2788) * chore: upgrade ListingCard to 2nd gen, accessibility fixes * feat: finish up "Detroit" style ListingCard story * feat: 2nd gen updates to Tag and ImageCard * Fix code style issues with Prettier * Add docs and style variables for `ListingCard`, `ImageCard`, `Tag` Also change `Button` style API to explicit variables rather than fallbacks * fix: relocate href to the content header * chore: add aria-hidden support for footer buttons * Fix code style issues with Prettier * test: update cypress to use new card links Co-authored-by: Lint Action Co-authored-by: Sean Albert --- sites/public/cypress/support/commands.js | 4 +- sites/public/lib/helpers.tsx | 12 +- ui-components/src/actions/Button.docs.mdx | 12 +- ui-components/src/actions/Button.scss | 42 ++++-- ui-components/src/actions/Button.tsx | 2 + ui-components/src/actions/LinkButton.tsx | 1 + ui-components/src/blocks/ImageCard.docs.mdx | 30 ++++ ui-components/src/blocks/ImageCard.scss | 115 ++++++--------- .../src/blocks/ImageCard.stories.tsx | 27 +++- ui-components/src/blocks/ImageCard.tsx | 22 ++- ui-components/src/global/mixins.scss | 29 ++-- ui-components/src/global/text.scss | 48 ++++--- ui-components/src/global/tokens/sizes.scss | 2 + .../listing/ListingCard.docs.mdx | 43 ++++++ .../page_components/listing/ListingCard.scss | 103 ++++++++------ .../listing/ListingCard.stories.tsx | 119 ++++++++++++++-- .../page_components/listing/ListingCard.tsx | 53 ++++--- ui-components/src/text/Tag.docs.mdx | 64 +++++++++ ui-components/src/text/Tag.scss | 132 +++++++++++------- ui-components/src/text/Tag.stories.tsx | 11 +- 20 files changed, 607 insertions(+), 264 deletions(-) create mode 100644 ui-components/src/blocks/ImageCard.docs.mdx create mode 100644 ui-components/src/page_components/listing/ListingCard.docs.mdx create mode 100644 ui-components/src/text/Tag.docs.mdx diff --git a/sites/public/cypress/support/commands.js b/sites/public/cypress/support/commands.js index c7f11d9d77..8f04bf98ce 100644 --- a/sites/public/cypress/support/commands.js +++ b/sites/public/cypress/support/commands.js @@ -42,7 +42,7 @@ Cypress.Commands.add("checkErrorMessages", (command) => { Cypress.Commands.add("beginApplicationRejectAutofill", (listingName) => { cy.visit("/listings") - cy.get(`[alt="${listingName}"]`).click() + cy.get(".is-card-link").contains(listingName).click() cy.getByTestId("listing-view-apply-button").eq(1).click() cy.getByTestId("app-choose-language-sign-in-button").click() cy.get("[data-test-id=sign-in-email-field]").type("admin@example.com") @@ -65,7 +65,7 @@ Cypress.Commands.add("beginApplicationRejectAutofill", (listingName) => { Cypress.Commands.add("beginApplicationSignedIn", (listingName) => { cy.visit("/listings") - cy.get(`[alt="${listingName}"]`).click() + cy.get(".is-card-link").contains(listingName).click() cy.getByTestId("listing-view-apply-button").eq(1).click() cy.getByTestId("app-choose-language-button").eq(0).click() cy.getByTestId("app-next-step-button").click() diff --git a/sites/public/lib/helpers.tsx b/sites/public/lib/helpers.tsx index f4ff5aceae..d5549c20ea 100644 --- a/sites/public/lib/helpers.tsx +++ b/sites/public/lib/helpers.tsx @@ -121,7 +121,6 @@ export const getListings = (listings) => { imageCardProps={{ imageUrl: imageUrlFromListing(listing, parseInt(process.env.listingPhotoSize || "1302")) || "", - href: `/listing/${listing.id}/${listing.urlSlug}`, tags: listing.reservedCommunityType ? [ { @@ -139,10 +138,17 @@ export const getListings = (listings) => { cellClassName: "px-5 py-3", }} footerButtons={[ - { text: t("t.seeDetails"), href: `/listing/${listing.id}/${listing.urlSlug}` }, + { + text: t("t.seeDetails"), + href: `/listing/${listing.id}/${listing.urlSlug}`, + ariaHidden: true, + }, ]} contentProps={{ - contentHeader: { content: listing.name }, + contentHeader: { + content: listing.name, + href: `/listing/${listing.id}/${listing.urlSlug}`, + }, contentSubheader: { content: getListingCardSubtitle(listing.buildingAddress) }, tableHeader: generateTableSubHeader(listing), }} diff --git a/ui-components/src/actions/Button.docs.mdx b/ui-components/src/actions/Button.docs.mdx index 151ba6d157..9871c15179 100644 --- a/ui-components/src/actions/Button.docs.mdx +++ b/ui-components/src/actions/Button.docs.mdx @@ -50,12 +50,12 @@ You can apply CSS variables to the `.button` selector to customize the appearanc | `--alert-appearance-hover-background-color` | | Hover state alert background color | | `--alert-appearance-hover-border-color` | | Hover state alert border color | | `--alert-appearance-hover-label-color` | | Hover state alert label color | -| `--warn-appearance-background-color` | | Warning background color | -| `--warn-appearance-border-color` | | Warning border color | -| `--warn-appearance-label-color` | | Warning label color | -| `--warn-appearance-hover-background-color` | | Hover state warning background color | -| `--warn-appearance-hover-border-color` | | Hover state warning border color | -| `--warn-appearance-hover-label-color` | | Hover state warning label color | +| `--warning-appearance-background-color` | | Warning background color | +| `--warning-appearance-border-color` | | Warning border color | +| `--warning-appearance-label-color` | | Warning label color | +| `--warning-appearance-hover-background-color` | | Hover state warning background color | +| `--warning-appearance-hover-border-color` | | Hover state warning border color | +| `--warning-appearance-hover-label-color` | | Hover state warning label color | ### Typographic & Spacing Variables diff --git a/ui-components/src/actions/Button.scss b/ui-components/src/actions/Button.scss index cdef2a743f..6c7db25d16 100644 --- a/ui-components/src/actions/Button.scss +++ b/ui-components/src/actions/Button.scss @@ -1,16 +1,32 @@ @import "../global/mixins.scss"; .button { + /* Component Variables */ + --normal-rounded: var(--bloom-rounded); + --normal-padding: var(--bloom-s4) var(--bloom-s6); + --label-transform: uppercase; + --label-font: var(--bloom-font-alt-sans); + --label-letter-spacing: var(--bloom-letter-spacing-widest); + --normal-font-size: var(--bloom-font-size-sm); + --label-weight: bold; + --small-rounded: var(--bloom-rounded); + --small-padding: var(--bloom-s3) var(--bloom-s6); + --small-font-size: var(--bloom-font-size-xs); + --big-rounded: var(--bloom-rounded); + --big-padding: var(--bloom-s6); + --big-font-size: var(--bloom-font-size-sm); + + /* Base Styles */ position: relative; - border-radius: var(--normal-rounded, var(--bloom-rounded)); - padding: var(--normal-padding, var(--bloom-s4) var(--bloom-s6)); + border-radius: var(--normal-rounded); + padding: var(--normal-padding); text-align: center; - text-transform: var(--label-transform, uppercase); - font-family: var(--label-font, var(--bloom-font-alt-sans)); + text-transform: var(--label-transform); + font-family: var(--label-font); display: inline-block; - letter-spacing: var(--label-letter-spacing, var(--bloom-letter-spacing-widest)); - font-size: var(--normal-font-size, var(--bloom-font-size-sm)); - font-weight: var(--label-weight, bold); + letter-spacing: var(--label-letter-spacing); + font-size: var(--normal-font-size); + font-weight: var(--label-weight); line-height: var(--bloom-line-height-snug); &:focus { @@ -40,15 +56,15 @@ } &.is-small { - border-radius: var(--small-rounded, var(--bloom-rounded)); - padding: var(--small-padding, var(--bloom-s3) var(--bloom-s6)); - font-size: var(--small-font-size, var(--bloom-font-size-xs)); + border-radius: var(--small-rounded); + padding: var(--small-padding); + font-size: var(--small-font-size); } &.is-big { - border-radius: var(--big-rounded, var(--bloom-rounded)); - padding: var(--big-padding, var(--bloom-s6)); - font-size: var(--big-font-size, var(--bloom-font-size-sm)); + border-radius: var(--big-rounded); + padding: var(--big-padding); + font-size: var(--big-font-size); } &.is-normal-case { diff --git a/ui-components/src/actions/Button.tsx b/ui-components/src/actions/Button.tsx index 36a914c3da..4a569f25bf 100644 --- a/ui-components/src/actions/Button.tsx +++ b/ui-components/src/actions/Button.tsx @@ -19,6 +19,7 @@ export interface ButtonProps extends AppearanceProps { className?: string disabled?: boolean loading?: boolean + ariaHidden?: boolean ariaLabel?: string dataTestId?: string "data-test-id"?: string @@ -75,6 +76,7 @@ const Button = (props: ButtonProps) => { className={buttonClasses.join(" ")} onClick={props.onClick} disabled={props.disabled || props.loading} + aria-hidden={props.ariaHidden} aria-label={props.ariaLabel} data-test-id={props.dataTestId || props["data-test-id"]} > diff --git a/ui-components/src/actions/LinkButton.tsx b/ui-components/src/actions/LinkButton.tsx index 6f9d590c01..dfce74ccae 100644 --- a/ui-components/src/actions/LinkButton.tsx +++ b/ui-components/src/actions/LinkButton.tsx @@ -23,6 +23,7 @@ const LinkButton = (props: LinkButtonProps) => { return ( diff --git a/ui-components/src/blocks/ImageCard.docs.mdx b/ui-components/src/blocks/ImageCard.docs.mdx new file mode 100644 index 0000000000..9f328eeaa2 --- /dev/null +++ b/ui-components/src/blocks/ImageCard.docs.mdx @@ -0,0 +1,30 @@ +import { Canvas, Story, ArgsTable } from "@storybook/addon-docs" +import { Swatch } from "../prototypes/Swatch" +import { ImageCard } from "./ImageCard" + +# Image Card + +The image card component renders an image with optional tags at top and status bars below it. + + + + + +
+
+ +## Component Properties + + + +## Theming Variables + +You can apply CSS variables to the `.image-card` selector to customize the appearance of the component. Interior tags themselves are customizable separately, see the **Text/Tag** documentation for details. + +| Name | Type | Description | Default | +| ---------------------------- | -------------------------------------------- | ------------------------------------------------------------------ | ------------------------ | +| `--default-background-color` | | Background color when an image URL isn't available | `--bloom-color-gray-500` | +| `--border-radius` | Size | The border radius of the card | `0px` | +| `--image-height` | Size | A height limit for the image area | `auto` | +| `--tags-justify-mobile` | Flex Jusification | The horizontal alignment of tags over the image on mobile screens | `center` | +| `--tags-justify-desktop` | Flex Jusification | The horizontal alignment of tags over the image on desktop screens | `flex-start` | diff --git a/ui-components/src/blocks/ImageCard.scss b/ui-components/src/blocks/ImageCard.scss index b580c092de..3e19801fb7 100644 --- a/ui-components/src/blocks/ImageCard.scss +++ b/ui-components/src/blocks/ImageCard.scss @@ -1,92 +1,67 @@ @import "../global/mixins.scss"; -.image-card__wrapper { - @apply relative; +.image-card { + /* Component Variables */ + --default-background-color: var(--bloom-color-gray-500); + --border-radius: 0px; + --image-height: auto; + --tags-justify-mobile: center; + --tags-justify-desktop: flex-start; + + position: relative; } -.image-card { - @apply relative; - @apply bg-gray-500; +.image-card__inner { + position: relative; + background-color: var(--default-background-color); + border-radius: var(--border-radius); + img { - @apply w-full; + border-radius: var(--border-radius); + width: 100%; + height: var(--image-height); + object-fit: cover; } } .image-card-tag__wrapper { - @apply flex; - @apply items-center; - @apply justify-center; - @apply absolute; - @apply z-10; - @apply w-full; - @apply mt-1; - @apply px-4; - @apply flex-wrap; + --tags-justify: var(--tags-justify-mobile); - @screen md { - @apply items-start; - @apply justify-start; - } -} - -.image-card__figcaption { - @apply absolute; - @apply inset-x-0; - @apply bottom-0; - @apply py-1; -} + display: flex; + align-items: center; + justify-content: var(--tags-justify); + position: absolute; + z-index: 10; + width: 100%; + margin-block-start: var(--bloom-s1); + padding-inline: var(--bloom-s4); + flex-wrap: wrap; + max-width: 100%; -.image-card__placeholder { - height: 300px; -} + @media (min-width: $screen-sm) { + --tags-justify: var(--tags-justify-desktop); + } -.image-card__tag { - @apply absolute; - @apply top-0; - @apply p-3; - @apply m-5; -} + .tag { + margin-top: var(--bloom-s3); + margin-inline: var(--bloom-s2); -.image-card__title { - @apply text-white; - @apply text-left; - @apply text-lg; - @apply uppercase; - @apply font-alt-sans; - @apply relative; - @apply z-10; - @apply tracking-wider; - @apply px-5; - @apply pb-1; - @screen md { - @apply text-3xl; + .ui-icon { + margin-inline-end: var(--bloom-s2); + } } } -.image-card__subtitle { - @apply text-white; - @apply text-left; - @apply uppercase; - @apply font-alt-sans; - @apply text-sm; - @apply px-5; - @apply pb-3; - @apply relative; - @apply z-10; - @apply tracking-widest; +.image-card__placeholder { + height: 300px; } .image-card--leader { - @apply w-full; - - @screen md { - @apply w-2/3; - @apply pr-8; - @apply pt-8; - } + width: 100%; - .image-card__title { - @apply text-4xl; - @apply text-center; + @media (min-width: $screen-sm) { + width: var(--bloom-width-2-3rd); + margin-block-start: var(--bloom-s8); + margin-inline-end: var(--bloom-s8); } } diff --git a/ui-components/src/blocks/ImageCard.stories.tsx b/ui-components/src/blocks/ImageCard.stories.tsx index 4d0ae55994..06d4608289 100644 --- a/ui-components/src/blocks/ImageCard.stories.tsx +++ b/ui-components/src/blocks/ImageCard.stories.tsx @@ -1,12 +1,21 @@ import * as React from "react" +import { BADGES } from "../../.storybook/constants" import { ImageCard } from "./ImageCard" import { t } from "../helpers/translator" import { ApplicationStatusType } from "../global/ApplicationStatusType" import { IconFillColors } from "../icons/Icon" +import ImageCardDocumentation from "./ImageCard.docs.mdx" export default { - title: "Blocks/Image Card", + title: "Blocks/Image Card 🚩", + id: "blocks/image-card", decorators: [(storyFn: any) =>
{storyFn()}
], + parameters: { + docs: { + page: ImageCardDocumentation, + }, + badges: [BADGES.GEN2], + }, } export const image = () => @@ -15,12 +24,16 @@ export const withLink = () => +export const withOneStatusAndSmaller = () => ( +
+ +
) export const withDescriptionAsAlt = () => ( diff --git a/ui-components/src/blocks/ImageCard.tsx b/ui-components/src/blocks/ImageCard.tsx index 106181a811..861f97316f 100644 --- a/ui-components/src/blocks/ImageCard.tsx +++ b/ui-components/src/blocks/ImageCard.tsx @@ -20,27 +20,26 @@ export interface ImageTag { text?: string iconType?: UniversalIconType iconColor?: string + styleType?: AppearanceStyleType } export interface ImageCardProps { + /** A description of the image, used as alt text */ description?: string + /** A link, used to wrap the entire component */ href?: string + /** An image URL, used as the background image */ imageUrl?: string + /** A list of status indicators, an ApplicationStatus component is rendered for each item at the bottom of the card */ statuses?: StatusBarType[] + /** A list of image tags, a Tag component is rendered for each over the image */ tags?: ImageTag[] } /** * @component ImageCard * - * A component that renders an image with optional status bars below it - * - * @prop description - A description of the image, used as alt text - * @prop href - A link, used to wrap the entire component - * @prop imageUrl - An image URL, used as a background image - * @prop statuses - A list of status indicators, an ApplicationStatus component is rendered for each item at the bottom of the card - * @prop tags - A list of image tags, a Tag component is rendered for each over the image - * + * A component that renders an image with optional tags at top and status bars below it */ const ImageCard = (props: ImageCardProps) => { const getStatuses = () => { @@ -61,18 +60,17 @@ const ImageCard = (props: ImageCardProps) => { } const image = ( -
+
{props.tags?.map((tag, index) => { return ( - + {tag.iconType && ( )} {tag.text} @@ -81,7 +79,7 @@ const ImageCard = (props: ImageCardProps) => { ) })}
-
+
{props.imageUrl ? ( {props.description ) : ( diff --git a/ui-components/src/global/mixins.scss b/ui-components/src/global/mixins.scss index d69b02574f..95ca7b04fd 100644 --- a/ui-components/src/global/mixins.scss +++ b/ui-components/src/global/mixins.scss @@ -99,25 +99,32 @@ } &.is-alert { - background-color: var(--bloom-color-alert); - border-color: var(--bloom-color-alert); - color: var(--bloom-color-white); + background-color: var(--alert-appearance-background-color, var(--bloom-color-alert)); + border-color: var(--alert-appearance-border-color, var(--bloom-color-alert)); + color: var(--alert-appearance-label-color, var(--bloom-color-white)); &:hover { - background-color: var(--bloom-color-alert-dark); - border-color: var(--bloom-color-alert-dark); + background-color: var( + --alert-appearance-hover-background-color, + var(--bloom-color-alert-dark) + ); + border-color: var(--alert-appearance-hover-border-color, var(--bloom-color-alert-dark)); + color: var(--alert-appearance-hover-label-color, var(--bloom-color-white)); } } &.is-warning { - background-color: var(--bloom-color-warn); - border-color: var(--bloom-color-warn); - color: var(--bloom-color-gray-800); + background-color: var(--warning-appearance-background-color, var(--bloom-color-warn)); + border-color: var(--warning-appearance-border-color, var(--bloom-color-warn)); + color: var(--warning-appearance-label-color, var(--bloom-color-gray-800)); &:hover { - color: var(--bloom-color-white); - background-color: var(--bloom-color-warn-dark); - border-color: var(--bloom-color-warn-dark); + background-color: var( + --warning-appearance-hover-background-color, + var(--bloom-color-warn-dark) + ); + border-color: var(--warning-appearance-hover-border-color, var(--bloom-color-warn-dark)); + color: var(--warning-appearance-hover-label-color, var(--bloom-color-white)); } } diff --git a/ui-components/src/global/text.scss b/ui-components/src/global/text.scss index 28059498e6..004303675e 100644 --- a/ui-components/src/global/text.scss +++ b/ui-components/src/global/text.scss @@ -141,39 +141,41 @@ h1.title { } .card-header { - @apply font-alt-sans; - @apply font-semibold; - @apply text-blue-700; - @apply text-3xl; - @apply mb-3; - @apply leading-tight; + font-family: var(--card-header-font-family, var(--bloom-font-alt-sans)); + font-weight: var(--card-header-font-weight, 600); + color: var(--card-header-color, var(--bloom-color-blue-700)); + font-size: var(--card-header-font-size, var(--bloom-font-size-3xl)); + margin-block: var(--card-header-margin-block, 0 var(--bloom-s3)); + line-height: var(--card-header-line-height, var(--bloom-line-height-tight)); } .card-subheader { - @apply font-alt-sans; - @apply text-black; - @apply text-base; - @apply mb-3; + font-family: var(--card-subheader-font-family, var(--bloom-font-alt-sans)); + font-weight: var(--card-subheader-font-weight, normal); + color: var(--card-subheader-color, var(--bloom-color-black)); + font-size: var(--card-subheader-font-size, var(--bloom-font-size-base)); + margin-block: var(--card-subheader-margin-block, 0 var(--bloom-s3)); } .table-header { - @apply font-alt-sans; - @apply font-semibold; - @apply text-gray-800; - @apply text-sm; - @apply mb-1; + font-family: var(--table-header-font-family, var(--bloom-font-alt-sans)); + font-weight: var(--table-header-font-weight, 600); + color: var(--table-header-color, var(--bloom-color-gray-800)); + font-size: var(--table-header-font-size, var(--bloom-font-size-sm)); + margin-block: var(--table-header-margin-block, 0 var(--bloom-s1)); } .table-subheader { - @apply font-alt-sans; - @apply text-gray-750; - @apply text-sm; - @apply mb-3; + font-family: var(--table-subheader-font-family, var(--bloom-font-alt-sans)); + font-weight: var(--table-subheader-font-weight, normal); + color: var(--table-subheader-color, var(--bloom-color-gray-750)); + font-size: var(--table-subheader-font-size, var(--bloom-font-size-sm)); + margin-block: var(--table-header-margin-block, 0 var(--bloom-s3)); } .category-header { - @apply font-alt-sans; - @apply font-semibold; - @apply text-gray-700; - @apply text-sm; + font-family: var(--category-header-font-family, var(--bloom-font-alt-sans)); + font-weight: var(--category-header-font-weight, 600); + color: var(--category-header-color, var(--bloom-color-gray-700)); + font-size: var(--category-header-font-size, var(--bloom-font-size-sm)); } diff --git a/ui-components/src/global/tokens/sizes.scss b/ui-components/src/global/tokens/sizes.scss index 268c0f7c4a..8465516258 100644 --- a/ui-components/src/global/tokens/sizes.scss +++ b/ui-components/src/global/tokens/sizes.scss @@ -45,4 +45,6 @@ --bloom-width-6xl: 72rem; --bloom-width-7xl: 80rem; --bloom-width-prose: 65ch; + --bloom-width-1-3rd: 33.333333%; + --bloom-width-2-3rd: 66.666667%; } diff --git a/ui-components/src/page_components/listing/ListingCard.docs.mdx b/ui-components/src/page_components/listing/ListingCard.docs.mdx new file mode 100644 index 0000000000..bbec184ec8 --- /dev/null +++ b/ui-components/src/page_components/listing/ListingCard.docs.mdx @@ -0,0 +1,43 @@ +import { Canvas, Story, ArgsTable } from "@storybook/addon-docs" +import { ListingCard } from "./ListingCard" + +# ListingCard + +The listing card component renders an image with optional status bars below it, and a content section associated with the image which can include titles, a table, and custom content. + + + + + +
+
+ +## Variants + +Many of the display variations of the card are enabled by the interior content props you pass, and you can survey the various stories available to see some common arrangements. In addition: + +### Stacked Tables + +By setting `stackedTable` to true, the interior table component switches from `StandardTable` to `StackedTable` for a different UI presentation. + + + + + +## Component Properties + + + +**Note:** for accessbility reasons, if you have a link that should apply to the card as a whole, pass an `href` prop to the `contentHeader` key of `contentProps`. (If you previously passed `href` to `imageCardProps`, you should remove that.) You'll also want to pass `ariaHidden: true` as a prop for the footer button. + +## Theming Variables + +You can apply CSS variables to the `.listings-row` selector to customize the appearance of the component. + +| Name | Type | Description | Default | +| -------------------------- | ------- | -------------------------------------------------------------------------------------------------------------- | ------------------- | +| `--tags-flex-order` | Integer | Where content header tags are located relative to the heading text. Set to `2` to move tags below the headings | `0` | +| `--inter-row-gap` | Size | The margin between each card | `--bloom-s12` | +| `--max-width` | Size | The maximum width of the card on smaller screens | `--bloom-width-3xl` | +| `--max-width-large-screen` | Size | The maximum width of the card on larger screens | `--bloom-width-5xl` | +| `--cell-padding` | Size | The padding within both the image side and the content side | `--bloom-s3` | diff --git a/ui-components/src/page_components/listing/ListingCard.scss b/ui-components/src/page_components/listing/ListingCard.scss index 16cde8b44e..5b1b762838 100644 --- a/ui-components/src/page_components/listing/ListingCard.scss +++ b/ui-components/src/page_components/listing/ListingCard.scss @@ -1,64 +1,87 @@ .listings-row { - @apply flex; - @apply flex-row; - @apply flex-wrap; - @apply max-w-5xl; - @apply m-auto; - @apply mb-12; - max-width: 48rem; + --tags-flex-order: 0; + --inter-row-gap: var(--bloom-s12); + --max-width: var(--bloom-width-3xl); + --max-width-large-screen: var(--bloom-width-5xl); + --cell-padding: var(--bloom-s3); - &:first-of-type { - @apply mt-12; - } + display: flex; + flex-direction: row; + flex-wrap: wrap; + max-width: var(--max-width); + margin: var(--inter-row-gap) auto; + position: relative; - @screen lg { - @apply max-w-5xl; + @media (min-width: $screen-lg) { + --max-width: var(--max-width-large-screen); } } -.listings-row_figure { - @apply w-full; - @apply p-3; - @apply pb-0; +.listings-row_figure, +.listings-row_content { + width: 100%; + padding: var(--cell-padding); + padding-bottom: 0px; - @screen lg { - @apply w-6/12; + @media (min-width: $screen-lg) { + width: 50%; } } .listings-row_content { - @apply w-full; - @apply p-3; + .listings-row_headers { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; - @screen lg { - @apply w-6/12; - } + @media (min-width: $screen-md) { + align-items: flex-start; + text-align: left; + } - .listings-row_headers { - @apply flex; - @apply flex-col; - @apply items-center; - @apply text-center; - @screen md { - @apply items-start; - @apply text-left; + .listings-row_tags { + order: var(--tags-flex-order); + display: inline-flex; + width: 100%; + flex-wrap: wrap; + justify-content: flex-start; + + .tag { + margin-bottom: var(--bloom-s2); + margin-inline-end: var(--bloom-s2); + + &:last-of-type { + margin-inline-end: 0; + } + + .ui-icon { + margin-inline-end: var(--bloom-s2); + } + } } } + + .is-card-link::after { + content: ""; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + } } .listings-row_table { - @apply mb-4; + margin-block-end: var(--bloom-s4); } .listings-row_footer { - @apply flex; - @apply justify-end; - @apply w-full; + display: flex; + justify-content: flex-end; + width: 100%; + a:not(:first-child) { - @apply ml-1; + margin-inline-start: var(--bloom-s1); } } - -.listings-row_table { - @apply mb-4; -} diff --git a/ui-components/src/page_components/listing/ListingCard.stories.tsx b/ui-components/src/page_components/listing/ListingCard.stories.tsx index db2343ffba..37282ee54c 100644 --- a/ui-components/src/page_components/listing/ListingCard.stories.tsx +++ b/ui-components/src/page_components/listing/ListingCard.stories.tsx @@ -1,11 +1,21 @@ +import * as React from "react" +import { AppearanceStyleType } from "../../.." +import { BADGES } from "../../../.storybook/constants" import LinkButton from "../../actions/LinkButton" import Icon from "../../icons/Icon" -import * as React from "react" import { ListingCard } from "./ListingCard" +import ListingCardDocumentation from "./ListingCard.docs.mdx" export default { - title: "Listing/ListingCard", + title: "Listing/ListingCard 🚩", + id: "page-components/listing-card", component: ListingCard, + parameters: { + docs: { + page: ListingCardDocumentation, + }, + badges: [BADGES.GEN2], + }, } const standardImageCardProps = { @@ -23,9 +33,14 @@ const standardTableProps = { }, data: [ { - unitType: { content: "cellA" }, - minimumIncome: { content: "cellB" }, - rent: { content: "cellC" }, + unitType: { content: "Row 1 cellA" }, + minimumIncome: { content: "Row 1 cellB" }, + rent: { content: Row 1 cellC }, + }, + { + unitType: { content: "Row 2 cellA" }, + minimumIncome: { content: "Row 2 cellB" }, + rent: { content: Row 2 cellC }, }, ], responsiveCollapse: true, @@ -82,11 +97,11 @@ export const withStackedTable = () => { export const WithHeaders = () => { return ( { /> ) } + +export const detroitStyle = () => { + const cssVarsOverride = ` + .listing-card-overrides { + --bloom-font-sans: Montserrat; + --bloom-font-alt-sans: var(--bloom-font-sans); + --bloom-color-primary: rgb(41,126,115); + --bloom-color-primary-dark: rgb(0,68,69); + --primary-appearance-hover-background-color: white; + --primary-appearance-hover-label-color: var(--bloom-color-primary-dark); + --outlined-appearance-hover-background-color: var(--bloom-color-primary); + --outlined-appearance-hover-border-color: var(--bloom-color-primary); + --card-header-color: var(--bloom-color-primary-dark); + } + + .listing-card-overrides table { + font-family: var(--bloom-font-sans); + } + + .listing-card-overrides .image-card { + --tags-justify-mobile: flex-end; + --tags-justify-desktop: flex-end; + --border-radius: 24px; + --image-height: 340px; + --normal-font-size: var(--bloom-font-size-base); + } + .listing-card-overrides .tag { + --normal-padding: var(--bloom-s2) var(--bloom-s3); + --label-weight: 700; + } + + .listing-card-overrides .listings-row { + --tags-flex-order: 2; + } + + .listing-card-overrides .button { + --normal-rounded: 60px; + --normal-padding: 0.5rem 1rem; + --normal-font-size: var(--bloom-font-size-base); + --label-letter-spacing: normal; + --label-transform: none; + } + ` + + return ( + <> + + + +
+ + +
+ + ) +} diff --git a/ui-components/src/page_components/listing/ListingCard.tsx b/ui-components/src/page_components/listing/ListingCard.tsx index 70c3f34636..140cc7b937 100644 --- a/ui-components/src/page_components/listing/ListingCard.tsx +++ b/ui-components/src/page_components/listing/ListingCard.tsx @@ -1,4 +1,4 @@ -import * as React from "react" +import React, { useContext } from "react" import { ImageCard, ImageCardProps, ImageTag } from "../../blocks/ImageCard" import { LinkButton } from "../../actions/LinkButton" import { StackedTable, StackedTableProps } from "../../tables/StackedTable" @@ -8,17 +8,20 @@ import { Tag } from "../../text/Tag" import { AppearanceStyleType } from "../../global/AppearanceTypes" import { Icon, IconFillColors } from "../../icons/Icon" import "./ListingCard.scss" +import { NavigationContext } from "../../config/NavigationContext" interface ListingCardTableProps extends StandardTableProps, StackedTableProps {} export interface CardHeader { - customClass?: string content: string | React.ReactNode + href?: string + customClass?: string } export interface FooterButton { href: string text: string + ariaHidden?: boolean } export interface ListingCardContentProps { @@ -28,14 +31,23 @@ export interface ListingCardContentProps { tableSubheader?: CardHeader } export interface ListingCardProps { + /** A list of tags to be rendered below the content header, a Tag component is rendered for each */ cardTags?: ImageTag[] + /** Custom content rendered in the content section above the table */ children?: React.ReactElement + /** An object containing fields that render optional headers above the content section's table */ contentProps?: ListingCardContentProps + /** A list of buttons to render in the footer of the content section */ footerButtons?: FooterButton[] + /** A class name applied to the footer container of the content section */ footerContainerClass?: string + /** Custom content rendered below the content table */ footerContent?: React.ReactNode + /** Prop interface for the ImageCard component */ imageCardProps: ImageCardProps + /** Toggles on the StackedTable component in place of the default StandardTable component - they are functionally equivalent with differing UIs */ stackedTable?: boolean + /** Prop interface for the StandardTable and StackedTable components */ tableProps?: ListingCardTableProps } @@ -44,17 +56,6 @@ export interface ListingCardProps { * * A component that renders an image with optional status bars below it, * and a content section associated with the image which can include titles, a table, and custom content - * - * @prop cardTags -A list of tags to be rendered below the content header, a Tag component is rendered for each - * @prop children - Custom content rendered in the content section above the table - * @prop footerButtons - A list of buttons to render in the footer of the content section - * @prop footerContent - Custom content rendered below the content table - * @prop footerContainerClass - A class name applied to the footer container of the content section - * @prop imageCardProps - Prop interface for the ImageCard component - * @prop stackedTable - Toggles on the StackedTable component in place of the default StandardTable component - they are functionally equivalent with differing UIs - * @prop contentProps - An object containing fields that render optional headers above the content section's table - * @prop tableProps - Prop interface for the StandardTable and StackedTable components - * */ const ListingCard = (props: ListingCardProps) => { const { @@ -68,6 +69,7 @@ const ListingCard = (props: ListingCardProps) => { contentProps, tableProps, } = props + const { LinkComponent } = useContext(NavigationContext) const getHeader = ( header: CardHeader | undefined, @@ -78,7 +80,13 @@ const ListingCard = (props: ListingCardProps) => { if (header && header.content) { return ( - {header.content} + {header.href ? ( + + {header.content} + + ) : ( + header.content + )} ) } else { @@ -88,20 +96,19 @@ const ListingCard = (props: ListingCardProps) => { const getContentHeader = () => { return ( - <> +
{getHeader(contentProps?.contentHeader, 2, "cardHeader", "order-1")} {getHeader(contentProps?.contentSubheader, 3, "cardSubheader", "order-2")} {cardTags && cardTags?.length > 0 && ( -
+
{cardTags?.map((cardTag, index) => { return ( - + {cardTag.iconType && ( )} {cardTag.text} @@ -110,7 +117,7 @@ const ListingCard = (props: ListingCardProps) => { })}
)} - +
) } @@ -143,7 +150,11 @@ const ListingCard = (props: ListingCardProps) => {
{footerButtons?.map((footerButton, index) => { return ( - + {footerButton.text} ) @@ -161,7 +172,7 @@ const ListingCard = (props: ListingCardProps) => {
-
{getContentHeader()}
+ {getContentHeader()} {getContent()}
diff --git a/ui-components/src/text/Tag.docs.mdx b/ui-components/src/text/Tag.docs.mdx new file mode 100644 index 0000000000..fa8dd4d68d --- /dev/null +++ b/ui-components/src/text/Tag.docs.mdx @@ -0,0 +1,64 @@ +import { Canvas, Story, ArgsTable } from "@storybook/addon-docs" +import { Tag } from "./Tag" + +# Tag + +The tag component offers a simple way to present a content label or status indicator. + + + + + +
+
+ +## Variants + +There are several props which accept enums to adjust style type and size. For example, to choose the "primary" style and a "small" size, you would pass the `AppearanceStyleType.primary` enum to `styleType` prop and `AppearanceSizeType.small` enum to `size` prop. + +### Normal Shape + +(Default, no prop required.) + + + + + +### Pill + +Set the `pillStyle` property to `true`. + + + + + +### Fill Container + +Set the `fillContainer` property to `true` for a block tag which spans the entire width of its container. + + + + + +## Component Properties + + + +## Theming Variables + +You can apply CSS variables to the `.tag` selector to customize the appearance of the component. + +| Name | Type | Description | Default | +| ------------------------ | ----------- | ----------------------------------------------- | --------------------------------------------------- | +| `--normal-padding` | Size | Padding of the normal (no variant) tag | `--bloom-s3` (top/bottom) `--bloom-s5` (left/right) | +| `--normal-rounded` | Size | The border radius of the normal tag | `--bloom-rounded-md` | +| `--normal-font-size` | Size | The font size of the normal tag | `--bloom-font-size-tiny` | +| `--normal-font-weight` | Font Weight | The font weight of the normal tag | `normal` | +| `--pill-padding` | Size | Padding of the pill variant tag | `--bloom-s2` (top/bottom) `--bloom-s4` (left/right) | +| `--pill-rounded` | Size | The border radius of the pill tag | `--bloom-rounded-full` | +| `--pill-font-size` | Size | The font size of the pill tag | `--bloom-font-size-tiny` | +| `--pill-font-weight` | Font Weight | The font weight of the pill tag | `600` | +| `--pill-text-transform` | Case | Text case of of the pill tag | `uppercase` | +| `--pill-letter-spacing` | Size | The average gap between letters of the pill tag | `--bloom-letter-spacing-ultrawide` | +| `--small-pill-padding` | Size | Padding of the small-size pill tag | `--bloom-s1` (top/bottom) `--bloom-s3` (left/right) | | +| `--small-pill-font-size` | Size | The font size of the small-size pill tag | `--bloom-font-size-2xs` | diff --git a/ui-components/src/text/Tag.scss b/ui-components/src/text/Tag.scss index 5926ea8db2..3b8788d82e 100644 --- a/ui-components/src/text/Tag.scss +++ b/ui-components/src/text/Tag.scss @@ -1,94 +1,132 @@ .tag { - @apply py-3; - @apply px-5; - @apply text-white; - @apply text-tiny; - @apply bg-primary; - @apply rounded-md; - @apply text-center; + /* Component Variables */ + --normal-padding: var(--bloom-s3) var(--bloom-s5); + --normal-rounded: var(--bloom-rounded-md); + --normal-font-size: var(--bloom-font-size-tiny); + --normal-font-weight: normal; + + --pill-padding: var(--bloom-s2) var(--bloom-s4); + --pill-rounded: var(--bloom-rounded-full); + --pill-font-size: var(--bloom-font-size-tiny); + --pill-font-weight: 600; + --pill-text-transform: uppercase; + --pill-letter-spacing: var(--bloom-letter-spacing-ultrawide); + + --small-pill-padding: var(--bloom-s1) var(--bloom-s3); + --small-pill-font-size: var(--bloom-font-size-2xs); + + /* Default Styles */ + padding: var(--normal-padding); + border-radius: var(--normal-rounded); + font-size: var(--normal-font-size); + font-weight: var(--normal-font-weight); + color: var(--primary-appearance-label-color, var(--bloom-color-white)); + font-family: var(--bloom-font-sans); + background-color: var(--primary-appearance-border-color, var(--bloom-color-primary)); + text-align: center; &.is-pill { - @apply px-4; - @apply py-2; - @apply bg-primary-light; - @apply text-primary-dark; - @apply rounded-full; - @apply font-semibold; - @apply font-alt-sans; - @apply uppercase; - @apply tracking-ultrawide; - @apply inline; - @apply text-tiny; - @apply leading-4; + padding: var(--pill-padding); + background-color: var(--bloom-color-primary-light); + color: var(--bloom-color-primary-dark); + border-radius: var(--pill-rounded); + font-weight: var(--pill-font-weight); + font-family: var(--bloom-font-alt-sans); + text-transform: var(--pill-text-transform); + letter-spacing: var(--pill-letter-spacing); + display: inline; + font-size: var(--pill-font-size); + line-height: var(--bloom-line-height-4); &.is-small { - @apply text-2xs; - @apply py-1; - @apply px-3; - @apply leading-3; + font-size: var(--small-pill-font-size); + padding: var(--small-pill-padding); + line-height: var(--bloom-line-height-3); } } &.fill-container { - @apply w-full; - @apply inline-block; + width: 100%; + display: inline-block; } &.is-warning { - @apply bg-accent-warm-dark; + color: var(--warning-appearance-label-color, var(--bloom-color-white)); + background-color: var( + --warning-appearance-background-color, + var(--bloom-color-accent-warm-dark) + ); } &.is-secondary { - @apply text-white; - @apply bg-secondary; + color: var(--secondary-appearance-label-color, var(--bloom-color-white)); + background-color: var(--secondary-appearance-background-color, var(--bloom-color-secondary)); } &.is-success { &:not(.is-light-mode) { - @apply text-white; - @apply bg-success; + color: var(--success-appearance-label-color, var(--bloom-color-white)); + background-color: var(--success-appearance-background-color, var(--bloom-color-success)); } + &.is-light-mode { - @apply text-gray-900; - @apply bg-success-light; + color: var(--success-light-appearance-label-color, var(--bloom-color-gray-900)); + background-color: var( + --success-light-appearance-background-color, + var(--bloom-color-success-light) + ); } } &.is-accent-cool { &:not(.is-light-mode) { - @apply text-white; - @apply bg-accent-cool-dark; + color: var(--accent-cool-appearance-label-color, var(--bloom-color-white)); + background-color: var( + --accent-cool-appearance-background-color, + var(--bloom-color-accent-cool-dark) + ); } + &.is-light-mode { - @apply text-gray-900; - @apply bg-accent-cool; + color: var(--accent-cool-light-appearance-label-color, var(--bloom-color-gray-900)); + background-color: var( + --accent-cool-light-appearance-background-color, + var(--bloom-color-accent-cool) + ); } } &.is-accent-warm { &:not(.is-light-mode) { - @apply text-white; - @apply bg-accent-warm-dark; + color: var(--accent-warm-appearance-label-color, var(--bloom-color-white)); + background-color: var( + --accent-warm-appearance-background-color, + var(--bloom-color-accent-warm-dark) + ); } + &.is-light-mode { - @apply text-gray-900; - @apply bg-accent-warm; + color: var(--accent-warm-light-appearance-label-color, var(--bloom-color-gray-900)); + background-color: var( + --accent-warm-light-appearance-background-color, + var(--bloom-color-accent-warm) + ); } } &.is-closed { - @apply text-white; - @apply bg-gray-750; + color: var(--closed-appearance-label-color, var(--bloom-color-white)); + background-color: var(--closed-appearance-background-color, var(--bloom-color-gray-750)); } &.is-info { - @apply text-gray-800; - @apply bg-warn-light; + color: var(--info-appearance-label-color, var(--bloom-color-gray-800)); + background-color: var(--info-appearance-background-color, var(--bloom-color-warn-light)); } &.is-small { - @apply text-sm; - @apply py-2; - @apply px-4; + font-size: var(--bloom-font-size-sm); + padding-block: var(--bloom-s2); + padding-inline: var(--bloom-s4); } } diff --git a/ui-components/src/text/Tag.stories.tsx b/ui-components/src/text/Tag.stories.tsx index c1048c9d2f..4fb4625261 100644 --- a/ui-components/src/text/Tag.stories.tsx +++ b/ui-components/src/text/Tag.stories.tsx @@ -3,12 +3,21 @@ import { AppearanceStyleType, AppearanceShadeType, } from "../global/AppearanceTypes" +import { BADGES } from "../../.storybook/constants" import * as React from "react" import { Tag } from "./Tag" +import TagDocumentation from "./Tag.docs.mdx" export default { - title: "Text/Tag", + title: "Text/Tag 🚩", + id: "text/tag", decorators: [(storyFn: any) =>
{storyFn()}
], + parameters: { + docs: { + page: TagDocumentation, + }, + badges: [BADGES.GEN2], + }, } export const standard = () => Tag From f04434904aa50d2a8012266329dc53f08168defa Mon Sep 17 00:00:00 2001 From: "github.context.workflow" Date: Thu, 23 Jun 2022 01:07:15 +0000 Subject: [PATCH 014/598] chore(release): version - @bloom-housing/shared-helpers@5.0.1-alpha.4 - @bloom-housing/partners@5.0.1-alpha.4 - @bloom-housing/public@5.0.1-alpha.4 - @bloom-housing/ui-components@5.0.1-alpha.2 --- shared-helpers/CHANGELOG.md | 8 ++++++++ shared-helpers/package.json | 4 ++-- sites/partners/CHANGELOG.md | 8 ++++++++ sites/partners/package.json | 6 +++--- sites/public/CHANGELOG.md | 8 ++++++++ sites/public/package.json | 6 +++--- ui-components/CHANGELOG.md | 8 ++++++++ ui-components/package.json | 2 +- 8 files changed, 41 insertions(+), 9 deletions(-) diff --git a/shared-helpers/CHANGELOG.md b/shared-helpers/CHANGELOG.md index bdbcdff2ce..c8f5d446db 100644 --- a/shared-helpers/CHANGELOG.md +++ b/shared-helpers/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.4](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.3...@bloom-housing/shared-helpers@5.0.1-alpha.4) (2022-06-23) + +**Note:** Version bump only for package @bloom-housing/shared-helpers + + + + + ## [5.0.1-alpha.3](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.2...@bloom-housing/shared-helpers@5.0.1-alpha.3) (2022-06-22) **Note:** Version bump only for package @bloom-housing/shared-helpers diff --git a/shared-helpers/package.json b/shared-helpers/package.json index c7314f672a..20412bcb9e 100644 --- a/shared-helpers/package.json +++ b/shared-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/shared-helpers", - "version": "5.0.1-alpha.3", + "version": "5.0.1-alpha.4", "description": "Shared helpers for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared-helpers", "main": "index.js", @@ -17,7 +17,7 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/ui-components": "^5.0.1-alpha.1" + "@bloom-housing/ui-components": "^5.0.1-alpha.2" }, "devDependencies": { "@bloom-housing/ui-components": "^3.0.1-alpha.15", diff --git a/sites/partners/CHANGELOG.md b/sites/partners/CHANGELOG.md index f302ba9e2c..cf3a72c1a7 100644 --- a/sites/partners/CHANGELOG.md +++ b/sites/partners/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.4](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.3...@bloom-housing/partners@5.0.1-alpha.4) (2022-06-23) + +**Note:** Version bump only for package @bloom-housing/partners + + + + + ## [5.0.1-alpha.3](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.2...@bloom-housing/partners@5.0.1-alpha.3) (2022-06-22) **Note:** Version bump only for package @bloom-housing/partners diff --git a/sites/partners/package.json b/sites/partners/package.json index 32e9b661cd..1a894cf1c0 100644 --- a/sites/partners/package.json +++ b/sites/partners/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/partners", - "version": "5.0.1-alpha.3", + "version": "5.0.1-alpha.4", "author": "Sean Albert ", "description": "Partners app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -27,8 +27,8 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.3", - "@bloom-housing/ui-components": "^5.0.1-alpha.1", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.4", + "@bloom-housing/ui-components": "^5.0.1-alpha.2", "@mapbox/mapbox-sdk": "^0.13.0", "ag-grid-community": "^26.0.0", "ag-grid-react": "^26.0.0", diff --git a/sites/public/CHANGELOG.md b/sites/public/CHANGELOG.md index 9ba7962391..ec653d075c 100644 --- a/sites/public/CHANGELOG.md +++ b/sites/public/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.4](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.3...@bloom-housing/public@5.0.1-alpha.4) (2022-06-23) + +**Note:** Version bump only for package @bloom-housing/public + + + + + ## [5.0.1-alpha.3](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.2...@bloom-housing/public@5.0.1-alpha.3) (2022-06-22) **Note:** Version bump only for package @bloom-housing/public diff --git a/sites/public/package.json b/sites/public/package.json index dfdb8440ae..6e23e9b8fa 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/public", - "version": "5.0.1-alpha.3", + "version": "5.0.1-alpha.4", "author": "Sean Albert ", "description": "Public web app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -25,8 +25,8 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.3", - "@bloom-housing/ui-components": "^5.0.1-alpha.1", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.4", + "@bloom-housing/ui-components": "^5.0.1-alpha.2", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1", diff --git a/ui-components/CHANGELOG.md b/ui-components/CHANGELOG.md index a0e4dbbab4..49c5190710 100644 --- a/ui-components/CHANGELOG.md +++ b/ui-components/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.2](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.1...@bloom-housing/ui-components@5.0.1-alpha.2) (2022-06-23) + +**Note:** Version bump only for package @bloom-housing/ui-components + + + + + ## [5.0.1-alpha.1](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.0...@bloom-housing/ui-components@5.0.1-alpha.1) (2022-06-21) diff --git a/ui-components/package.json b/ui-components/package.json index 4bc769a02a..db9027b988 100644 --- a/ui-components/package.json +++ b/ui-components/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/ui-components", - "version": "5.0.1-alpha.1", + "version": "5.0.1-alpha.2", "author": "Sean Albert ", "description": "Shared user interface components for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared/ui-components", From 32a3df66a883dcffca8b304ba769f8a2d8c5c240 Mon Sep 17 00:00:00 2001 From: Sean Albert Date: Thu, 23 Jun 2022 13:25:29 -0700 Subject: [PATCH 015/598] fix: mfa verificication fields (#2842) --- .../src/page_components/sign-in/FormSignInMFAType.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-components/src/page_components/sign-in/FormSignInMFAType.tsx b/ui-components/src/page_components/sign-in/FormSignInMFAType.tsx index e211b9cc10..be8b8e328d 100644 --- a/ui-components/src/page_components/sign-in/FormSignInMFAType.tsx +++ b/ui-components/src/page_components/sign-in/FormSignInMFAType.tsx @@ -75,7 +75,7 @@ const FormSignInMFAType = ({ @@ -84,7 +84,7 @@ const FormSignInMFAType = ({ From 3b285f8ac72726998cb3d2179948d6bababb7216 Mon Sep 17 00:00:00 2001 From: "github.context.workflow" Date: Thu, 23 Jun 2022 20:26:01 +0000 Subject: [PATCH 016/598] chore(release): version - @bloom-housing/shared-helpers@5.0.1-alpha.5 - @bloom-housing/partners@5.0.1-alpha.5 - @bloom-housing/public@5.0.1-alpha.5 - @bloom-housing/ui-components@5.0.1-alpha.3 --- shared-helpers/CHANGELOG.md | 8 ++++++++ shared-helpers/package.json | 4 ++-- sites/partners/CHANGELOG.md | 8 ++++++++ sites/partners/package.json | 6 +++--- sites/public/CHANGELOG.md | 8 ++++++++ sites/public/package.json | 6 +++--- ui-components/CHANGELOG.md | 11 +++++++++++ ui-components/package.json | 2 +- 8 files changed, 44 insertions(+), 9 deletions(-) diff --git a/shared-helpers/CHANGELOG.md b/shared-helpers/CHANGELOG.md index c8f5d446db..b1485799ec 100644 --- a/shared-helpers/CHANGELOG.md +++ b/shared-helpers/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.5](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.4...@bloom-housing/shared-helpers@5.0.1-alpha.5) (2022-06-23) + +**Note:** Version bump only for package @bloom-housing/shared-helpers + + + + + ## [5.0.1-alpha.4](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.3...@bloom-housing/shared-helpers@5.0.1-alpha.4) (2022-06-23) **Note:** Version bump only for package @bloom-housing/shared-helpers diff --git a/shared-helpers/package.json b/shared-helpers/package.json index 20412bcb9e..18c2a81cc8 100644 --- a/shared-helpers/package.json +++ b/shared-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/shared-helpers", - "version": "5.0.1-alpha.4", + "version": "5.0.1-alpha.5", "description": "Shared helpers for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared-helpers", "main": "index.js", @@ -17,7 +17,7 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/ui-components": "^5.0.1-alpha.2" + "@bloom-housing/ui-components": "^5.0.1-alpha.3" }, "devDependencies": { "@bloom-housing/ui-components": "^3.0.1-alpha.15", diff --git a/sites/partners/CHANGELOG.md b/sites/partners/CHANGELOG.md index cf3a72c1a7..d128ac8567 100644 --- a/sites/partners/CHANGELOG.md +++ b/sites/partners/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.5](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.4...@bloom-housing/partners@5.0.1-alpha.5) (2022-06-23) + +**Note:** Version bump only for package @bloom-housing/partners + + + + + ## [5.0.1-alpha.4](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.3...@bloom-housing/partners@5.0.1-alpha.4) (2022-06-23) **Note:** Version bump only for package @bloom-housing/partners diff --git a/sites/partners/package.json b/sites/partners/package.json index 1a894cf1c0..33aa226bf8 100644 --- a/sites/partners/package.json +++ b/sites/partners/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/partners", - "version": "5.0.1-alpha.4", + "version": "5.0.1-alpha.5", "author": "Sean Albert ", "description": "Partners app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -27,8 +27,8 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.4", - "@bloom-housing/ui-components": "^5.0.1-alpha.2", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.5", + "@bloom-housing/ui-components": "^5.0.1-alpha.3", "@mapbox/mapbox-sdk": "^0.13.0", "ag-grid-community": "^26.0.0", "ag-grid-react": "^26.0.0", diff --git a/sites/public/CHANGELOG.md b/sites/public/CHANGELOG.md index ec653d075c..34d2136af8 100644 --- a/sites/public/CHANGELOG.md +++ b/sites/public/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.5](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.4...@bloom-housing/public@5.0.1-alpha.5) (2022-06-23) + +**Note:** Version bump only for package @bloom-housing/public + + + + + ## [5.0.1-alpha.4](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.3...@bloom-housing/public@5.0.1-alpha.4) (2022-06-23) **Note:** Version bump only for package @bloom-housing/public diff --git a/sites/public/package.json b/sites/public/package.json index 6e23e9b8fa..d3f2277cc0 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/public", - "version": "5.0.1-alpha.4", + "version": "5.0.1-alpha.5", "author": "Sean Albert ", "description": "Public web app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -25,8 +25,8 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.4", - "@bloom-housing/ui-components": "^5.0.1-alpha.2", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.5", + "@bloom-housing/ui-components": "^5.0.1-alpha.3", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1", diff --git a/ui-components/CHANGELOG.md b/ui-components/CHANGELOG.md index 49c5190710..2ef8b42910 100644 --- a/ui-components/CHANGELOG.md +++ b/ui-components/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.3](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.2...@bloom-housing/ui-components@5.0.1-alpha.3) (2022-06-23) + + +### Bug Fixes + +* mfa verificication fields ([#2842](https://github.com/bloom-housing/bloom/issues/2842)) ([32a3df6](https://github.com/bloom-housing/bloom/commit/32a3df66a883dcffca8b304ba769f8a2d8c5c240)) + + + + + ## [5.0.1-alpha.2](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.1...@bloom-housing/ui-components@5.0.1-alpha.2) (2022-06-23) **Note:** Version bump only for package @bloom-housing/ui-components diff --git a/ui-components/package.json b/ui-components/package.json index db9027b988..2164920663 100644 --- a/ui-components/package.json +++ b/ui-components/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/ui-components", - "version": "5.0.1-alpha.2", + "version": "5.0.1-alpha.3", "author": "Sean Albert ", "description": "Shared user interface components for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared/ui-components", From f360ef3b7b393283704a287717939eec4e29f110 Mon Sep 17 00:00:00 2001 From: "github.context.workflow" Date: Thu, 23 Jun 2022 22:52:52 +0000 Subject: [PATCH 017/598] chore(release): version - @bloom-housing/shared-helpers@5.0.1-alpha.6 - @bloom-housing/partners@5.0.1-alpha.6 - @bloom-housing/public@5.0.1-alpha.6 - @bloom-housing/ui-components@5.0.1-alpha.4 --- shared-helpers/CHANGELOG.md | 8 ++++++++ shared-helpers/package.json | 4 ++-- sites/partners/CHANGELOG.md | 8 ++++++++ sites/partners/package.json | 6 +++--- sites/public/CHANGELOG.md | 8 ++++++++ sites/public/package.json | 6 +++--- ui-components/CHANGELOG.md | 13 +++++++++++++ ui-components/package.json | 2 +- 8 files changed, 46 insertions(+), 9 deletions(-) diff --git a/shared-helpers/CHANGELOG.md b/shared-helpers/CHANGELOG.md index b1485799ec..63d9631210 100644 --- a/shared-helpers/CHANGELOG.md +++ b/shared-helpers/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.6](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.5...@bloom-housing/shared-helpers@5.0.1-alpha.6) (2022-06-23) + +**Note:** Version bump only for package @bloom-housing/shared-helpers + + + + + ## [5.0.1-alpha.5](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.4...@bloom-housing/shared-helpers@5.0.1-alpha.5) (2022-06-23) **Note:** Version bump only for package @bloom-housing/shared-helpers diff --git a/shared-helpers/package.json b/shared-helpers/package.json index 18c2a81cc8..5defe1d975 100644 --- a/shared-helpers/package.json +++ b/shared-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/shared-helpers", - "version": "5.0.1-alpha.5", + "version": "5.0.1-alpha.6", "description": "Shared helpers for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared-helpers", "main": "index.js", @@ -17,7 +17,7 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/ui-components": "^5.0.1-alpha.3" + "@bloom-housing/ui-components": "^5.0.1-alpha.4" }, "devDependencies": { "@bloom-housing/ui-components": "^3.0.1-alpha.15", diff --git a/sites/partners/CHANGELOG.md b/sites/partners/CHANGELOG.md index d128ac8567..9548b6a11d 100644 --- a/sites/partners/CHANGELOG.md +++ b/sites/partners/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.6](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.5...@bloom-housing/partners@5.0.1-alpha.6) (2022-06-23) + +**Note:** Version bump only for package @bloom-housing/partners + + + + + ## [5.0.1-alpha.5](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.4...@bloom-housing/partners@5.0.1-alpha.5) (2022-06-23) **Note:** Version bump only for package @bloom-housing/partners diff --git a/sites/partners/package.json b/sites/partners/package.json index 33aa226bf8..8e1a9b1713 100644 --- a/sites/partners/package.json +++ b/sites/partners/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/partners", - "version": "5.0.1-alpha.5", + "version": "5.0.1-alpha.6", "author": "Sean Albert ", "description": "Partners app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -27,8 +27,8 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.5", - "@bloom-housing/ui-components": "^5.0.1-alpha.3", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.6", + "@bloom-housing/ui-components": "^5.0.1-alpha.4", "@mapbox/mapbox-sdk": "^0.13.0", "ag-grid-community": "^26.0.0", "ag-grid-react": "^26.0.0", diff --git a/sites/public/CHANGELOG.md b/sites/public/CHANGELOG.md index 34d2136af8..ec01f77451 100644 --- a/sites/public/CHANGELOG.md +++ b/sites/public/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.6](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.5...@bloom-housing/public@5.0.1-alpha.6) (2022-06-23) + +**Note:** Version bump only for package @bloom-housing/public + + + + + ## [5.0.1-alpha.5](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.4...@bloom-housing/public@5.0.1-alpha.5) (2022-06-23) **Note:** Version bump only for package @bloom-housing/public diff --git a/sites/public/package.json b/sites/public/package.json index d3f2277cc0..c04c611c0c 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/public", - "version": "5.0.1-alpha.5", + "version": "5.0.1-alpha.6", "author": "Sean Albert ", "description": "Public web app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -25,8 +25,8 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.5", - "@bloom-housing/ui-components": "^5.0.1-alpha.3", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.6", + "@bloom-housing/ui-components": "^5.0.1-alpha.4", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1", diff --git a/ui-components/CHANGELOG.md b/ui-components/CHANGELOG.md index 2ef8b42910..9c7aceb684 100644 --- a/ui-components/CHANGELOG.md +++ b/ui-components/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.4](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.3...@bloom-housing/ui-components@5.0.1-alpha.4) (2022-06-23) + + +### Features + +* **preferenceslist:** add storybook example without subtitle ([4a32106](https://github.com/bloom-housing/bloom/commit/4a32106d0f171822961e4dcee5ee9013ee23b329)) +* **preferenceslist:** move ordinal at mobile bp; update text color for contrast ([b063f5d](https://github.com/bloom-housing/bloom/commit/b063f5dd0813778f1d77efa510ee3a2d788be717)), closes [#2827](https://github.com/bloom-housing/bloom/issues/2827) +* **preferenceslist:** move ordinal left; make other elements align horizontally ([7034cc7](https://github.com/bloom-housing/bloom/commit/7034cc76a04e8c395ae3a51b2f2c4e5cef944086)), closes [Issue#2827](https://github.com/Issue/issues/2827) + + + + + ## [5.0.1-alpha.3](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.2...@bloom-housing/ui-components@5.0.1-alpha.3) (2022-06-23) diff --git a/ui-components/package.json b/ui-components/package.json index 2164920663..0028667426 100644 --- a/ui-components/package.json +++ b/ui-components/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/ui-components", - "version": "5.0.1-alpha.3", + "version": "5.0.1-alpha.4", "author": "Sean Albert ", "description": "Shared user interface components for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared/ui-components", From 62a870e3696849dc751e410282279e23071db544 Mon Sep 17 00:00:00 2001 From: dominikx96 Date: Mon, 27 Jun 2022 18:28:43 +0200 Subject: [PATCH 018/598] fix: application due date sentence (#2822) * refactor: break translations to separate rows * refactor: render due date text if exists --- sites/public/pages/applications/review/terms.tsx | 10 +++++++++- ui-components/src/locales/es.json | 3 ++- ui-components/src/locales/general.json | 3 ++- ui-components/src/locales/tl.json | 3 ++- ui-components/src/locales/vi.json | 3 ++- ui-components/src/locales/zh.json | 3 ++- 6 files changed, 19 insertions(+), 6 deletions(-) diff --git a/sites/public/pages/applications/review/terms.tsx b/sites/public/pages/applications/review/terms.tsx index 5e2210583c..5e4b2473ab 100644 --- a/sites/public/pages/applications/review/terms.tsx +++ b/sites/public/pages/applications/review/terms.tsx @@ -111,8 +111,16 @@ const ApplicationTerms = () => {
+ {listing?.applicationDueDate && ( + + {t("application.review.terms.textSubmissionDate", { + applicationDueDate: applicationDueDate, + })} + + )} + - {t("application.review.terms.text", { applicationDueDate: applicationDueDate })} + {t("application.review.terms.text")}
diff --git a/ui-components/src/locales/es.json b/ui-components/src/locales/es.json index 527a153145..84f7311ec4 100644 --- a/ui-components/src/locales/es.json +++ b/ui-components/src/locales/es.json @@ -322,7 +322,8 @@ "application.review.sameAddressAsApplicant": "Misma dirección que el solicitante", "application.review.takeAMomentToReview": "Dedique un momento a revisar su información antes de enviar su solicitud.", "application.review.terms.confirmCheckboxText": "Convengo y comprendo que no puedo cambiar nada después de enviar la solicitud.", - "application.review.terms.text": "Esta solicitud debe enviarse antes del %{applicationDueDate}.

El agente inmobiliario se comunicará con los solicitantes por sorteo y orden de preferencia o por lista de espera hasta que todas las vacantes estén completas. Toda la información será revisada y se confirmará su elegibilidad. En caso de haber realizado alguna declaración fraudulenta_ su solicitud y las solicitudes duplicadas del mismo hogar pueden eliminarse de la lista de espera ya que solo se permite una solicitud por hogar. En caso que usted desee que su solicitud sea revisada_ deberá completar una solicitud más detallada y brindar los documentos de respaldo que sean requeridos. Para obtener más información_ por favor_ contáctese con la empresa constructora o el agente de arrendamiento publicado en el anuncio. Puede contactarse directamente directamente con la empresa constructora o con el administrador de la propiedad si hay alguna actualización en su solicitud.

En caso de no poder verificar alguna preferencia para el sorteo de vivienda que haya reclamado_ no recibirá la preferencia pero tampoco recibirá una penalización.< br>
El hecho de haber completado la solicitud de vivienda no le da derecho al acceso de la vivienda ni indica que es elegible para vivienda; todos los solicitantes serán seleccionados tal como se indica en los Criterios de selección de residentes de la propiedad. No ofrecemos garantías sobre la obtención de vivienda.

Una vez que haya enviado su solicitud en línea_ no podrá modificarla.

Por la presente_ declaro que lo anterior es verdadero y exacto_ y reconozco que cualquier declaración errónea realizada de manera fraudulenta o negligente en esta solicitud puede ser eliminada de la lotería.

", + "application.review.terms.textSubmissionDate": "Esta solicitud debe enviarse antes del %{applicationDueDate}.

", + "application.review.terms.text": "El agente inmobiliario se comunicará con los solicitantes por sorteo y orden de preferencia o por lista de espera hasta que todas las vacantes estén completas. Toda la información será revisada y se confirmará su elegibilidad. En caso de haber realizado alguna declaración fraudulenta_ su solicitud y las solicitudes duplicadas del mismo hogar pueden eliminarse de la lista de espera ya que solo se permite una solicitud por hogar. En caso que usted desee que su solicitud sea revisada_ deberá completar una solicitud más detallada y brindar los documentos de respaldo que sean requeridos. Para obtener más información_ por favor_ contáctese con la empresa constructora o el agente de arrendamiento publicado en el anuncio. Puede contactarse directamente directamente con la empresa constructora o con el administrador de la propiedad si hay alguna actualización en su solicitud.

En caso de no poder verificar alguna preferencia para el sorteo de vivienda que haya reclamado_ no recibirá la preferencia pero tampoco recibirá una penalización.< br>
El hecho de haber completado la solicitud de vivienda no le da derecho al acceso de la vivienda ni indica que es elegible para vivienda; todos los solicitantes serán seleccionados tal como se indica en los Criterios de selección de residentes de la propiedad. No ofrecemos garantías sobre la obtención de vivienda.

Una vez que haya enviado su solicitud en línea_ no podrá modificarla.

Por la presente_ declaro que lo anterior es verdadero y exacto_ y reconozco que cualquier declaración errónea realizada de manera fraudulenta o negligente en esta solicitud puede ser eliminada de la lotería.

", "application.review.terms.title": "Términos", "application.review.voucherOrSubsidy": "Cupón de vivienda o subsidio de alquiler", "application.start.whatToExpect.info1": "Primero, le haremos preguntas sobre usted y las personas con las que piensa vivir. Luego, le haremos preguntas sobre sus ingresos. Finalmente, veremos si usted reúne los requisitos de alguna preferencia de lotería para vivienda de precio accesible.", diff --git a/ui-components/src/locales/general.json b/ui-components/src/locales/general.json index 9435e82a3f..8ab1fb7feb 100644 --- a/ui-components/src/locales/general.json +++ b/ui-components/src/locales/general.json @@ -438,7 +438,8 @@ "application.review.sameAddressAsApplicant": "Same Address as Applicant", "application.review.takeAMomentToReview": "Take a moment to review your information before submitting your application.", "application.review.terms.confirmCheckboxText": "I agree and understand that I cannot change anything after I submit.", - "application.review.terms.text": "This application must be submitted by %{applicationDueDate}.

Applicants will be contacted by the leasing agent in lottery and preference order or waitlist order until vacancies are filled. All of the information that you have provided will be verified and your eligibility confirmed. Your application may be removed from the waitlist if you have made any fraudulent statements and duplicate applications from the same household may be removed as only one application per household is permitted. Should your application be chosen for review, be prepared to fill out a more detailed application and provide required supporting documents. For more information, please contact the developer or leasing agent posted in the listing. Please contact the developer/property manager directly if there are any updates to your application.

If we cannot verify a housing lottery preference that you have claimed, you will not receive the preference but will not be otherwise penalized.

Completing this housing application does not entitle you to housing or indicate you are eligible for housing; all applicants will be screened as outlined in the property’s Resident Selection Criteria. We offer no guarantees about obtaining housing.

You cannot change your online application after you submit.

I declare that the foregoing is true and accurate, and acknowledge that any misstatement fraudulently or negligently made on this application may result in removal from the lottery.

", + "application.review.terms.textSubmissionDate": "This application must be submitted by %{applicationDueDate}.

", + "application.review.terms.text": "Applicants will be contacted by the leasing agent in lottery and preference order or waitlist order until vacancies are filled. All of the information that you have provided will be verified and your eligibility confirmed. Your application may be removed from the waitlist if you have made any fraudulent statements and duplicate applications from the same household may be removed as only one application per household is permitted. Should your application be chosen for review, be prepared to fill out a more detailed application and provide required supporting documents. For more information, please contact the developer or leasing agent posted in the listing. Please contact the developer/property manager directly if there are any updates to your application.

If we cannot verify a housing lottery preference that you have claimed, you will not receive the preference but will not be otherwise penalized.

Completing this housing application does not entitle you to housing or indicate you are eligible for housing; all applicants will be screened as outlined in the property’s Resident Selection Criteria. We offer no guarantees about obtaining housing.

You cannot change your online application after you submit.

I declare that the foregoing is true and accurate, and acknowledge that any misstatement fraudulently or negligently made on this application may result in removal from the lottery.

", "application.review.terms.title": "Terms", "application.review.voucherOrSubsidy": "Housing Voucher or Rental Subsidy", "application.start.whatToExpect.info1": "First we'll ask about you and the people you plan to live with. Then, we'll ask about your income. Finally, we'll see if you qualify for any affordable housing lottery preference.", diff --git a/ui-components/src/locales/tl.json b/ui-components/src/locales/tl.json index d2025db223..48a875f6e5 100644 --- a/ui-components/src/locales/tl.json +++ b/ui-components/src/locales/tl.json @@ -275,7 +275,8 @@ "application.review.sameAddressAsApplicant": "Parehong Address ng Aplikante", "application.review.takeAMomentToReview": "Maglaan ng ilang sandali upang suriin ang iyong impormasyon bago isumite ang iyong application.", "application.review.terms.confirmCheckboxText": "Sumasang-ayon ako at nauunawaan na hindi ko mababago ang anuman pagkatapos kong magsumite.", - "application.review.terms.text": "Ang application na ito ay dapat na isumite bago ang %{applicationDueDate}.

Makikipag-ugnayan ang mga aplikante ng ahente sa lottery ng pagpaparenta at preference order o waitlist order hanggang sa mapunan ang mga bakante. Ang lahat ng impormasyon na iyong ibinigay ay i-ve-verify ang kukumpirmahin kung kwalipikado ka. Maaaring alisin ang iyong application sa waitlist kung gumawa ka ng anumang mga mapanlinlang na pahayag at maaaring alisin ang mga dobleng application mula sa parehong sambahayan dahil isang application lamang sa bawat sambahayan ang pinahihintulutan. Kung mapili ang iyong application para sa pagsusuri_ maging handa na punan ang isang mas detalyadong application at magbigay ng mga kinakailangang karagdagang dokumento. Para sa higit pang impormasyon_ makipag-ugnayan sa developer o ahente sa pagpapaupa na naka-post sa listahan. Makipag-ugnayan nang direkta sa developer/property manager kung mayroong anumang mga update sa iyong application.

Kung hindi namin ma-verify ang isang kagustuhan sa lottery sa pabahay na iyong nakuha_ hindi mo matatanggap ang kagustuhan ngunit hindi mapaparusahan.< br>
Ang pagkumpleto ng application sa pabahay na ito ay hindi nagbibigay sa iyo ng karapatan sa pabahay o nagpapahiwatig na ikaw ay karapat-dapat para sa pabahay; lahat ng mga aplikante ay sasalain ayon sa nakabalangkas sa Pamantayan sa Pagpili ng Residente ng property. Hindi kami nag-aalok ng mga garantiya tungkol sa pagkuha ng pabahay.

Hindi mo mababago ang iyong online na application pagkatapos mong isumite.

Ipinapahayag ko na ang nabanggit ay totoo at tumpak_ at kinikilala na ang anumang maling pahayag ay mapanlinlang o kapabayaan na ginawa sa ang application na ito ay maaaring magresulta sa pag-alis mula sa lottery.

", + "application.review.terms.textSubmissionDate": "Ang application na ito ay dapat na isumite bago ang %{applicationDueDate}.

", + "application.review.terms.text": "Makikipag-ugnayan ang mga aplikante ng ahente sa lottery ng pagpaparenta at preference order o waitlist order hanggang sa mapunan ang mga bakante. Ang lahat ng impormasyon na iyong ibinigay ay i-ve-verify ang kukumpirmahin kung kwalipikado ka. Maaaring alisin ang iyong application sa waitlist kung gumawa ka ng anumang mga mapanlinlang na pahayag at maaaring alisin ang mga dobleng application mula sa parehong sambahayan dahil isang application lamang sa bawat sambahayan ang pinahihintulutan. Kung mapili ang iyong application para sa pagsusuri_ maging handa na punan ang isang mas detalyadong application at magbigay ng mga kinakailangang karagdagang dokumento. Para sa higit pang impormasyon_ makipag-ugnayan sa developer o ahente sa pagpapaupa na naka-post sa listahan. Makipag-ugnayan nang direkta sa developer/property manager kung mayroong anumang mga update sa iyong application.

Kung hindi namin ma-verify ang isang kagustuhan sa lottery sa pabahay na iyong nakuha_ hindi mo matatanggap ang kagustuhan ngunit hindi mapaparusahan.< br>
Ang pagkumpleto ng application sa pabahay na ito ay hindi nagbibigay sa iyo ng karapatan sa pabahay o nagpapahiwatig na ikaw ay karapat-dapat para sa pabahay; lahat ng mga aplikante ay sasalain ayon sa nakabalangkas sa Pamantayan sa Pagpili ng Residente ng property. Hindi kami nag-aalok ng mga garantiya tungkol sa pagkuha ng pabahay.

Hindi mo mababago ang iyong online na application pagkatapos mong isumite.

Ipinapahayag ko na ang nabanggit ay totoo at tumpak_ at kinikilala na ang anumang maling pahayag ay mapanlinlang o kapabayaan na ginawa sa ang application na ito ay maaaring magresulta sa pag-alis mula sa lottery.

", "application.review.terms.title": "Mga Tuntunin", "application.review.voucherOrSubsidy": "Voucher ng Pabahay o Subsidiya ng Pagrenta", "application.start.whatToExpect.info1": "Magtatanong muna kami tungkol sa iyo at sa mga taong pinaplano mong makasama. Pagkatapos_ tatanungin namin ang iyong kita. Sa huli_ titingnan natin kung kwalipikado ka para sa anumang pagpili sa lottery ng abot-kayang pabahay.", diff --git a/ui-components/src/locales/vi.json b/ui-components/src/locales/vi.json index c4ba6c74c9..167ac37406 100644 --- a/ui-components/src/locales/vi.json +++ b/ui-components/src/locales/vi.json @@ -322,7 +322,8 @@ "application.review.sameAddressAsApplicant": "Cùng Địa chỉ với Ứng viên", "application.review.takeAMomentToReview": "Hãy dành một chút thời gian để xem lại thông tin của quý vị trước khi nộp đơn ghi danh.", "application.review.terms.confirmCheckboxText": "Tôi đồng ý và hiểu rằng tôi không thể thay đổi bất cứ thông tin nào sau khi tôi nộp đơn.", - "application.review.terms.text": "Đơn đăng ký này phải được gửi trước %{applicationDueDate}.

Người đăng ký sẽ được đại lý cho thuê liên hệ theo thứ tự bốc thăm và thứ tự ưu tiên hoặc thứ tự danh sách chờ cho đến khi hết căn hộ trống. Tất cả thông tin quý vị đã cung cấp sẽ được xác minh và xác nhận tính đủ điều kiện. Đơn đăng ký của quý vị có thể bị loại khỏi danh sách chờ nếu quý vị có bất kỳ tuyên bố gian dối nào_ các đơn đăng ký trùng lặp từ cùng một hộ gia đình có thể bị loại vì mỗi hộ gia đình chỉ được phép đăng ký một đơn. Nếu đơn đăng ký của quý vị được chọn để xem xét_ hãy chuẩn bị để điền vào đơn đăng ký chi tiết hơn và cung cấp các tài liệu hỗ trợ cần thiết. Để biết thêm thông tin_ vui lòng liên hệ với chủ đầu tư hoặc đại lý cho thuê có tên trong danh sách. Vui lòng liên hệ trực tiếp với chủ đầu tư/người quản lý khu nhà nếu có bất kỳ cập nhật nào đối với đơn đăng ký của quý vị.

Nếu chúng tôi không thể xác minh mức ưu tiên bốc thăm nhà ở mà quý vị đã yêu cầu_ quý vị sẽ không nhận được ưu tiên đó nhưng sẽ không bị phạt.

Việc hoàn thành đơn đăng ký nhà ở này không đồng nghĩa với việc quý vị sẽ có được nhà ở hoặc cho thấy quý vị đủ điều kiện nhận nhà ở; tất cả những người nộp đơn sẽ được sàng lọc như đã nêu trong Tiêu chí Lựa chọn Cư dân của khu nhà. Chúng tôi không đảm bảo về việc có được nhà ở.

Quý vị không thể thay đổi đơn đăng ký trực tuyến sau khi gửi.

Tôi tuyên bố rằng những điều nêu trên là đúng và chính xác_ đồng thời thừa nhận rằng bất kỳ sai sót nào do gian lận hoặc do sơ suất trong đơn đăng ký này đều có thể dẫn đến việc bị loại khỏi bốc thăm.

", + "application.review.terms.textSubmissionDate": "Đơn đăng ký này phải được gửi trước %{applicationDueDate}.

", + "application.review.terms.text": "Người đăng ký sẽ được đại lý cho thuê liên hệ theo thứ tự bốc thăm và thứ tự ưu tiên hoặc thứ tự danh sách chờ cho đến khi hết căn hộ trống. Tất cả thông tin quý vị đã cung cấp sẽ được xác minh và xác nhận tính đủ điều kiện. Đơn đăng ký của quý vị có thể bị loại khỏi danh sách chờ nếu quý vị có bất kỳ tuyên bố gian dối nào_ các đơn đăng ký trùng lặp từ cùng một hộ gia đình có thể bị loại vì mỗi hộ gia đình chỉ được phép đăng ký một đơn. Nếu đơn đăng ký của quý vị được chọn để xem xét_ hãy chuẩn bị để điền vào đơn đăng ký chi tiết hơn và cung cấp các tài liệu hỗ trợ cần thiết. Để biết thêm thông tin_ vui lòng liên hệ với chủ đầu tư hoặc đại lý cho thuê có tên trong danh sách. Vui lòng liên hệ trực tiếp với chủ đầu tư/người quản lý khu nhà nếu có bất kỳ cập nhật nào đối với đơn đăng ký của quý vị.

Nếu chúng tôi không thể xác minh mức ưu tiên bốc thăm nhà ở mà quý vị đã yêu cầu_ quý vị sẽ không nhận được ưu tiên đó nhưng sẽ không bị phạt.

Việc hoàn thành đơn đăng ký nhà ở này không đồng nghĩa với việc quý vị sẽ có được nhà ở hoặc cho thấy quý vị đủ điều kiện nhận nhà ở; tất cả những người nộp đơn sẽ được sàng lọc như đã nêu trong Tiêu chí Lựa chọn Cư dân của khu nhà. Chúng tôi không đảm bảo về việc có được nhà ở.

Quý vị không thể thay đổi đơn đăng ký trực tuyến sau khi gửi.

Tôi tuyên bố rằng những điều nêu trên là đúng và chính xác_ đồng thời thừa nhận rằng bất kỳ sai sót nào do gian lận hoặc do sơ suất trong đơn đăng ký này đều có thể dẫn đến việc bị loại khỏi bốc thăm.

", "application.review.terms.title": "Các điều khoản", "application.review.voucherOrSubsidy": "Phiếu chọn Nhà hoặc Trợ cấp Tiền thuê nhà", "application.start.whatToExpect.info1": "Trước tiên, chúng tôi sẽ hỏi về quý vị và những người quý vị dự định sống cùng. Sau đó, chúng tôi sẽ hỏi về thu nhập của quý vị. Cuối cùng, chúng tôi sẽ xem liệu quý vị có hội đủ điều kiện cho bất kỳ lựa chọn ưu tiên rút thăm nhà ở giá phải chăng nào không.", diff --git a/ui-components/src/locales/zh.json b/ui-components/src/locales/zh.json index 4245d95707..8c7bcc26fc 100644 --- a/ui-components/src/locales/zh.json +++ b/ui-components/src/locales/zh.json @@ -322,7 +322,8 @@ "application.review.sameAddressAsApplicant": "與申請人同一地址", "application.review.takeAMomentToReview": "在提交申請前,請花一點時間檢視您的資料。", "application.review.terms.confirmCheckboxText": "本人同意並明白,在提交申請後,本人便不能更改任何內容。", - "application.review.terms.text": "此申請必須於 %{applicationDueDate} 前完成提交。

租屋仲介人員聯絡申請人時會採用抽籤、優先權利順序或候補名單順序的方式聯絡申請人,直到補滿缺額。您提供的所有資訊會經過驗證,且您的資格會受到確認。如果您做出任何虛假陳述,即會從候補名單中遭到移除,且一戶家戶僅限申請一次,因此我們也會移除相同家戶的重複申請案件。如果您的申請進入審核程序,請準備填寫更詳細的申請文件,並提供必要的佐證文件。如需更多資訊,請聯絡於刊登資訊中發佈的開發商或租屋仲介人員。如申請出現任何更新內容,請直接聯絡開發商/房地產經理。

如果我們無法驗證您聲稱的住房抽籤優先權利,您將無法取得優先權利,但也不會受罰。

填寫此住房申請不會讓您有權享受住房,亦不表示您符合住房資格;所有申請人皆必須接受如房地產中居民選擇標準概述內容的篩選程序。我們不保證取得住房資格。

您在提交文件後無法變更線上申請。

我聲明前述內容皆為真實且準確,且知悉此次申請如有任何虛假或過失陳述,即可能導致失去抽籤資格。

", + "application.review.terms.textSubmissionDate": "此申請必須於 %{applicationDueDate} 前完成提交。

", + "application.review.terms.text": "租屋仲介人員聯絡申請人時會採用抽籤、優先權利順序或候補名單順序的方式聯絡申請人,直到補滿缺額。您提供的所有資訊會經過驗證,且您的資格會受到確認。如果您做出任何虛假陳述,即會從候補名單中遭到移除,且一戶家戶僅限申請一次,因此我們也會移除相同家戶的重複申請案件。如果您的申請進入審核程序,請準備填寫更詳細的申請文件,並提供必要的佐證文件。如需更多資訊,請聯絡於刊登資訊中發佈的開發商或租屋仲介人員。如申請出現任何更新內容,請直接聯絡開發商/房地產經理。

如果我們無法驗證您聲稱的住房抽籤優先權利,您將無法取得優先權利,但也不會受罰。

填寫此住房申請不會讓您有權享受住房,亦不表示您符合住房資格;所有申請人皆必須接受如房地產中居民選擇標準概述內容的篩選程序。我們不保證取得住房資格。

您在提交文件後無法變更線上申請。

我聲明前述內容皆為真實且準確,且知悉此次申請如有任何虛假或過失陳述,即可能導致失去抽籤資格。

", "application.review.terms.title": "條款", "application.review.voucherOrSubsidy": "房屋補助券或租金補貼", "application.start.whatToExpect.info1": "首先,我們會詢問關於您本人以及您打算與之同住者的資料。接着,我們會詢問您的收入。最後,我們會了解您是否符合資格獲得任何可負擔房屋的抽籤優先權。", From dead9ad5ace1c611522db9b0185da8121da2adf6 Mon Sep 17 00:00:00 2001 From: "github.context.workflow" Date: Mon, 27 Jun 2022 16:29:27 +0000 Subject: [PATCH 019/598] chore(release): version - @bloom-housing/shared-helpers@5.0.1-alpha.7 - @bloom-housing/partners@5.0.1-alpha.7 - @bloom-housing/public@5.0.1-alpha.7 - @bloom-housing/ui-components@5.0.1-alpha.5 --- shared-helpers/CHANGELOG.md | 8 ++++++++ shared-helpers/package.json | 4 ++-- sites/partners/CHANGELOG.md | 8 ++++++++ sites/partners/package.json | 6 +++--- sites/public/CHANGELOG.md | 11 +++++++++++ sites/public/package.json | 6 +++--- ui-components/CHANGELOG.md | 11 +++++++++++ ui-components/package.json | 2 +- 8 files changed, 47 insertions(+), 9 deletions(-) diff --git a/shared-helpers/CHANGELOG.md b/shared-helpers/CHANGELOG.md index 63d9631210..56558b4620 100644 --- a/shared-helpers/CHANGELOG.md +++ b/shared-helpers/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.7](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.6...@bloom-housing/shared-helpers@5.0.1-alpha.7) (2022-06-27) + +**Note:** Version bump only for package @bloom-housing/shared-helpers + + + + + ## [5.0.1-alpha.6](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.5...@bloom-housing/shared-helpers@5.0.1-alpha.6) (2022-06-23) **Note:** Version bump only for package @bloom-housing/shared-helpers diff --git a/shared-helpers/package.json b/shared-helpers/package.json index 5defe1d975..6e1c6fd317 100644 --- a/shared-helpers/package.json +++ b/shared-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/shared-helpers", - "version": "5.0.1-alpha.6", + "version": "5.0.1-alpha.7", "description": "Shared helpers for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared-helpers", "main": "index.js", @@ -17,7 +17,7 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/ui-components": "^5.0.1-alpha.4" + "@bloom-housing/ui-components": "^5.0.1-alpha.5" }, "devDependencies": { "@bloom-housing/ui-components": "^3.0.1-alpha.15", diff --git a/sites/partners/CHANGELOG.md b/sites/partners/CHANGELOG.md index 9548b6a11d..e0468f5e24 100644 --- a/sites/partners/CHANGELOG.md +++ b/sites/partners/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.7](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.6...@bloom-housing/partners@5.0.1-alpha.7) (2022-06-27) + +**Note:** Version bump only for package @bloom-housing/partners + + + + + ## [5.0.1-alpha.6](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.5...@bloom-housing/partners@5.0.1-alpha.6) (2022-06-23) **Note:** Version bump only for package @bloom-housing/partners diff --git a/sites/partners/package.json b/sites/partners/package.json index 8e1a9b1713..08d24a93ab 100644 --- a/sites/partners/package.json +++ b/sites/partners/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/partners", - "version": "5.0.1-alpha.6", + "version": "5.0.1-alpha.7", "author": "Sean Albert ", "description": "Partners app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -27,8 +27,8 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.6", - "@bloom-housing/ui-components": "^5.0.1-alpha.4", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.7", + "@bloom-housing/ui-components": "^5.0.1-alpha.5", "@mapbox/mapbox-sdk": "^0.13.0", "ag-grid-community": "^26.0.0", "ag-grid-react": "^26.0.0", diff --git a/sites/public/CHANGELOG.md b/sites/public/CHANGELOG.md index ec01f77451..3e3be346f4 100644 --- a/sites/public/CHANGELOG.md +++ b/sites/public/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.7](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.6...@bloom-housing/public@5.0.1-alpha.7) (2022-06-27) + + +### Bug Fixes + +* application due date sentence ([#2822](https://github.com/bloom-housing/bloom/issues/2822)) ([62a870e](https://github.com/bloom-housing/bloom/commit/62a870e3696849dc751e410282279e23071db544)) + + + + + ## [5.0.1-alpha.6](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.5...@bloom-housing/public@5.0.1-alpha.6) (2022-06-23) **Note:** Version bump only for package @bloom-housing/public diff --git a/sites/public/package.json b/sites/public/package.json index c04c611c0c..c8277dd516 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/public", - "version": "5.0.1-alpha.6", + "version": "5.0.1-alpha.7", "author": "Sean Albert ", "description": "Public web app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -25,8 +25,8 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.6", - "@bloom-housing/ui-components": "^5.0.1-alpha.4", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.7", + "@bloom-housing/ui-components": "^5.0.1-alpha.5", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1", diff --git a/ui-components/CHANGELOG.md b/ui-components/CHANGELOG.md index 9c7aceb684..6595ab99ee 100644 --- a/ui-components/CHANGELOG.md +++ b/ui-components/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.5](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.4...@bloom-housing/ui-components@5.0.1-alpha.5) (2022-06-27) + + +### Bug Fixes + +* application due date sentence ([#2822](https://github.com/bloom-housing/bloom/issues/2822)) ([62a870e](https://github.com/bloom-housing/bloom/commit/62a870e3696849dc751e410282279e23071db544)) + + + + + ## [5.0.1-alpha.4](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.3...@bloom-housing/ui-components@5.0.1-alpha.4) (2022-06-23) diff --git a/ui-components/package.json b/ui-components/package.json index 0028667426..5b4a10e8b8 100644 --- a/ui-components/package.json +++ b/ui-components/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/ui-components", - "version": "5.0.1-alpha.4", + "version": "5.0.1-alpha.5", "author": "Sean Albert ", "description": "Shared user interface components for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared/ui-components", From e4e9a7b0cb25abc4e412595e4c96c31f2fa857e0 Mon Sep 17 00:00:00 2001 From: Jared White Date: Tue, 28 Jun 2022 15:30:14 -0700 Subject: [PATCH 020/598] fix: use lottery date in confirmation screen (#2847) chore: relocate `getLotteryEvent` to shared helpers --- shared-helpers/index.ts | 1 + shared-helpers/src/events.ts | 7 +++++++ sites/partners/lib/helpers.ts | 9 --------- .../sections/DetailRankingsAndResults.tsx | 2 +- .../PaperListingForm/sections/RankingsAndResults.tsx | 2 +- sites/public/pages/applications/review/confirmation.tsx | 6 ++++-- 6 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 shared-helpers/src/events.ts diff --git a/shared-helpers/index.ts b/shared-helpers/index.ts index 61c86d30a2..321e036a47 100644 --- a/shared-helpers/index.ts +++ b/shared-helpers/index.ts @@ -4,6 +4,7 @@ export * from "./src/RequireLogin" export * from "./src/Timeout" export * from "./src/blankApplication" export * from "./src/catchNetworkError" +export * from "./src/events" export * from "./src/formKeys" export * from "./src/gtm" export * from "./src/nextjs" diff --git a/shared-helpers/src/events.ts b/shared-helpers/src/events.ts new file mode 100644 index 0000000000..d0959c36af --- /dev/null +++ b/shared-helpers/src/events.ts @@ -0,0 +1,7 @@ +import { Listing, ListingEvent, ListingEventType } from "@bloom-housing/backend-core/types" + +export const getLotteryEvent = (listing: Listing): ListingEvent | undefined => { + return listing?.events.find( + (event) => event.type === ListingEventType.publicLottery && event.startTime + ) +} diff --git a/sites/partners/lib/helpers.ts b/sites/partners/lib/helpers.ts index 1ce04327fd..32afb81b77 100644 --- a/sites/partners/lib/helpers.ts +++ b/sites/partners/lib/helpers.ts @@ -115,15 +115,6 @@ export const isNullOrUndefined = (value: unknown): boolean => { return value === null || value === undefined } -export const getLotteryEvent = (listing: FormListing): ListingEvent | undefined => { - const lotteryEvents = listing?.events.filter( - (event) => event.type === ListingEventType.publicLottery - ) - return lotteryEvents && lotteryEvents.length && lotteryEvents[0].startTime - ? lotteryEvents[0] - : null -} - export function arrayToFormOptions( arr: T[], label: string, diff --git a/sites/partners/src/listings/PaperListingDetails/sections/DetailRankingsAndResults.tsx b/sites/partners/src/listings/PaperListingDetails/sections/DetailRankingsAndResults.tsx index 60482868c2..af95545876 100644 --- a/sites/partners/src/listings/PaperListingDetails/sections/DetailRankingsAndResults.tsx +++ b/sites/partners/src/listings/PaperListingDetails/sections/DetailRankingsAndResults.tsx @@ -4,7 +4,7 @@ import utc from "dayjs/plugin/utc" dayjs.extend(utc) import { t, GridSection, ViewItem, GridCell } from "@bloom-housing/ui-components" import { ListingContext } from "../../ListingContext" -import { getLotteryEvent } from "../../../../lib/helpers" +import { getLotteryEvent } from "@bloom-housing/shared-helpers" import { ListingReviewOrder } from "@bloom-housing/backend-core/types" import { getDetailFieldNumber, getDetailFieldString, getDetailBoolean } from "./helpers" diff --git a/sites/partners/src/listings/PaperListingForm/sections/RankingsAndResults.tsx b/sites/partners/src/listings/PaperListingForm/sections/RankingsAndResults.tsx index bbf4e8588c..bd876df4bc 100644 --- a/sites/partners/src/listings/PaperListingForm/sections/RankingsAndResults.tsx +++ b/sites/partners/src/listings/PaperListingForm/sections/RankingsAndResults.tsx @@ -16,8 +16,8 @@ import { import { YesNoAnswer } from "../../../applications/PaperApplicationForm/FormTypes" import { FormListing } from "../formTypes" -import { getLotteryEvent, fieldHasError, fieldMessage } from "../../../../lib/helpers" import { ListingReviewOrder } from "@bloom-housing/backend-core/types" +import { getLotteryEvent } from "@bloom-housing/shared-helpers" type RankingsAndResultsProps = { listing?: FormListing diff --git a/sites/public/pages/applications/review/confirmation.tsx b/sites/public/pages/applications/review/confirmation.tsx index 92c9ef363a..2546dc92b5 100644 --- a/sites/public/pages/applications/review/confirmation.tsx +++ b/sites/public/pages/applications/review/confirmation.tsx @@ -20,6 +20,7 @@ import { PageView, pushGtmEvent, AuthContext, + getLotteryEvent, } from "@bloom-housing/shared-helpers" import FormsLayout from "../../../layouts/forms" import { AppSubmissionContext } from "../../../lib/AppSubmissionContext" @@ -35,11 +36,12 @@ const ApplicationConfirmation = () => { const reviewOrder = useMemo(() => { if (listing) { if (listing.reviewOrderType == ListingReviewOrder.lottery) { + const lotteryEvent = getLotteryEvent(listing) const lotteryText = [] - if (listing.applicationDueDate) { + if (lotteryEvent?.startTime) { lotteryText.push( t("application.review.confirmation.eligibleApplicants.lotteryDate", { - lotteryDate: dayjs(listing.applicationDueDate).format("MMMM D, YYYY"), + lotteryDate: dayjs(lotteryEvent?.startTime).format("MMMM D, YYYY"), }) ) } From 9a830a37548f58ad2fb775794caa30d8bd2e434b Mon Sep 17 00:00:00 2001 From: "github.context.workflow" Date: Tue, 28 Jun 2022 22:30:45 +0000 Subject: [PATCH 021/598] chore(release): version - @bloom-housing/shared-helpers@5.0.1-alpha.8 - @bloom-housing/partners@5.0.1-alpha.8 - @bloom-housing/public@5.0.1-alpha.8 --- shared-helpers/CHANGELOG.md | 11 +++++++++++ shared-helpers/package.json | 2 +- sites/partners/CHANGELOG.md | 11 +++++++++++ sites/partners/package.json | 4 ++-- sites/public/CHANGELOG.md | 11 +++++++++++ sites/public/package.json | 4 ++-- 6 files changed, 38 insertions(+), 5 deletions(-) diff --git a/shared-helpers/CHANGELOG.md b/shared-helpers/CHANGELOG.md index 56558b4620..e4b188d2fd 100644 --- a/shared-helpers/CHANGELOG.md +++ b/shared-helpers/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.8](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.7...@bloom-housing/shared-helpers@5.0.1-alpha.8) (2022-06-28) + + +### Bug Fixes + +* use lottery date in confirmation screen ([#2847](https://github.com/bloom-housing/bloom/issues/2847)) ([e4e9a7b](https://github.com/bloom-housing/bloom/commit/e4e9a7b0cb25abc4e412595e4c96c31f2fa857e0)) + + + + + ## [5.0.1-alpha.7](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.6...@bloom-housing/shared-helpers@5.0.1-alpha.7) (2022-06-27) **Note:** Version bump only for package @bloom-housing/shared-helpers diff --git a/shared-helpers/package.json b/shared-helpers/package.json index 6e1c6fd317..9a42321d0f 100644 --- a/shared-helpers/package.json +++ b/shared-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/shared-helpers", - "version": "5.0.1-alpha.7", + "version": "5.0.1-alpha.8", "description": "Shared helpers for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared-helpers", "main": "index.js", diff --git a/sites/partners/CHANGELOG.md b/sites/partners/CHANGELOG.md index e0468f5e24..febf508ca9 100644 --- a/sites/partners/CHANGELOG.md +++ b/sites/partners/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.8](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.7...@bloom-housing/partners@5.0.1-alpha.8) (2022-06-28) + + +### Bug Fixes + +* use lottery date in confirmation screen ([#2847](https://github.com/bloom-housing/bloom/issues/2847)) ([e4e9a7b](https://github.com/bloom-housing/bloom/commit/e4e9a7b0cb25abc4e412595e4c96c31f2fa857e0)) + + + + + ## [5.0.1-alpha.7](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.6...@bloom-housing/partners@5.0.1-alpha.7) (2022-06-27) **Note:** Version bump only for package @bloom-housing/partners diff --git a/sites/partners/package.json b/sites/partners/package.json index 08d24a93ab..c74fe2d9ec 100644 --- a/sites/partners/package.json +++ b/sites/partners/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/partners", - "version": "5.0.1-alpha.7", + "version": "5.0.1-alpha.8", "author": "Sean Albert ", "description": "Partners app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -27,7 +27,7 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.7", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.8", "@bloom-housing/ui-components": "^5.0.1-alpha.5", "@mapbox/mapbox-sdk": "^0.13.0", "ag-grid-community": "^26.0.0", diff --git a/sites/public/CHANGELOG.md b/sites/public/CHANGELOG.md index 3e3be346f4..53e9460848 100644 --- a/sites/public/CHANGELOG.md +++ b/sites/public/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.8](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.7...@bloom-housing/public@5.0.1-alpha.8) (2022-06-28) + + +### Bug Fixes + +* use lottery date in confirmation screen ([#2847](https://github.com/bloom-housing/bloom/issues/2847)) ([e4e9a7b](https://github.com/bloom-housing/bloom/commit/e4e9a7b0cb25abc4e412595e4c96c31f2fa857e0)) + + + + + ## [5.0.1-alpha.7](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.6...@bloom-housing/public@5.0.1-alpha.7) (2022-06-27) diff --git a/sites/public/package.json b/sites/public/package.json index c8277dd516..d8542f5d70 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/public", - "version": "5.0.1-alpha.7", + "version": "5.0.1-alpha.8", "author": "Sean Albert ", "description": "Public web app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -25,7 +25,7 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.7", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.8", "@bloom-housing/ui-components": "^5.0.1-alpha.5", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", From 8e96fddb0ec9bd9be66c0891749e8f72683adcb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pleba=C5=84ski?= Date: Wed, 29 Jun 2022 02:02:29 +0200 Subject: [PATCH 022/598] Flatten property relation (#2830) * fix(backend): flatten property relation * fix(backend): seeds after property relation removal * fix(backend): listings service property references * fix(backend): fix property related unit tests * feat(backend): remove property related models from swagger * Fix code style issues with Prettier * 2611/listing card accessibility 2nd gen (#2788) * chore: upgrade ListingCard to 2nd gen, accessibility fixes * feat: finish up "Detroit" style ListingCard story * feat: 2nd gen updates to Tag and ImageCard * Fix code style issues with Prettier * Add docs and style variables for `ListingCard`, `ImageCard`, `Tag` Also change `Button` style API to explicit variables rather than fallbacks * fix: relocate href to the content header * chore: add aria-hidden support for footer buttons * Fix code style issues with Prettier * test: update cypress to use new card links Co-authored-by: Lint Action Co-authored-by: Sean Albert * chore(release): version - @bloom-housing/shared-helpers@5.0.1-alpha.4 - @bloom-housing/partners@5.0.1-alpha.4 - @bloom-housing/public@5.0.1-alpha.4 - @bloom-housing/ui-components@5.0.1-alpha.2 * fix: mfa verificication fields (#2842) * chore(release): version - @bloom-housing/shared-helpers@5.0.1-alpha.5 - @bloom-housing/partners@5.0.1-alpha.5 - @bloom-housing/public@5.0.1-alpha.5 - @bloom-housing/ui-components@5.0.1-alpha.3 * feat(preferenceslist): move ordinal at mobile bp; update text color for contrast re: dahlia ticket to allow more room at responsive breakpoints in pref list and increase contrast to meet AAA standards Issue #2827 * Fix code style issues with Prettier * feat(preferenceslist): add storybook example without subtitle * feat(preferenceslist): move ordinal left; make other elements align horizontally to avoid an unwanted indention when subtitle is omitted, modify styles to support empty subtitle Issue#2827 * chore(release): version - @bloom-housing/shared-helpers@5.0.1-alpha.6 - @bloom-housing/partners@5.0.1-alpha.6 - @bloom-housing/public@5.0.1-alpha.6 - @bloom-housing/ui-components@5.0.1-alpha.4 * fix: application due date sentence (#2822) * refactor: break translations to separate rows * refactor: render due date text if exists * chore(release): version - @bloom-housing/shared-helpers@5.0.1-alpha.7 - @bloom-housing/partners@5.0.1-alpha.7 - @bloom-housing/public@5.0.1-alpha.7 - @bloom-housing/ui-components@5.0.1-alpha.5 Co-authored-by: Lint Action Co-authored-by: Jared White Co-authored-by: Sean Albert Co-authored-by: github.context.workflow Co-authored-by: Mark Buckner Co-authored-by: dominikx96 --- backend/core/src/app.module.ts | 4 - .../listings/dto/filter-type-to-field-map.ts | 2 +- .../src/listings/dto/listing-create.dto.ts | 78 --- .../src/listings/dto/listing-update.dto.ts | 78 --- backend/core/src/listings/dto/listing.dto.ts | 95 +--- .../src/listings/entities/listing.entity.ts | 91 +++- backend/core/src/listings/listings.module.ts | 2 - backend/core/src/listings/listings.service.ts | 29 +- .../listings/tests/listings.service.spec.ts | 18 +- backend/core/src/listings/views/config.ts | 16 +- backend/core/src/listings/views/view.spec.ts | 28 +- backend/core/src/listings/views/view.ts | 2 +- .../1655821661726-remove-property-relation.ts | 161 ++++++ .../property-groups/dto/property-group.dto.ts | 27 - .../entities/property-group.entity.ts | 43 -- .../property-groups.controller.ts | 67 --- .../property-groups/property-groups.module.ts | 13 - .../property-groups.service.ts | 11 - backend/core/src/property/dto/property.dto.ts | 89 --- .../src/property/entities/property.entity.ts | 138 ----- .../src/property/properties.controller.ts | 60 --- .../core/src/property/properties.module.ts | 13 - .../core/src/property/properties.service.ts | 9 - backend/core/src/seeder/seeder.module.ts | 2 - .../seeds/listings/listing-coliseum-seed.ts | 133 +++-- .../listing-default-bmr-chart-seed.ts | 23 +- .../listings/listing-default-missing-ami.ts | 17 +- ...ng-default-multiple-ami-and-percentages.ts | 17 +- .../listings/listing-default-multiple-ami.ts | 17 +- .../listings/listing-default-sanjose-seed.ts | 47 +- .../seeds/listings/listing-default-seed.ts | 77 ++- .../seeds/listings/listing-triton-seed.ts | 105 ++-- .../src/seeder/seeds/listings/listings.ts | 30 +- .../core/src/seeder/seeds/listings/shared.ts | 48 +- backend/core/src/shared/url-helper.ts | 4 +- .../services/translations.service.spec.ts | 3 +- .../services/translations.service.ts | 14 +- backend/core/src/units/dto/unit.dto.ts | 4 +- .../core/src/units/entities/unit.entity.ts | 6 +- .../test/properties/properties.e2e-spec.ts | 55 -- backend/core/types/src/backend-swagger.ts | 507 ++---------------- 41 files changed, 572 insertions(+), 1611 deletions(-) create mode 100644 backend/core/src/migration/1655821661726-remove-property-relation.ts delete mode 100644 backend/core/src/property-groups/dto/property-group.dto.ts delete mode 100644 backend/core/src/property-groups/entities/property-group.entity.ts delete mode 100644 backend/core/src/property-groups/property-groups.controller.ts delete mode 100644 backend/core/src/property-groups/property-groups.module.ts delete mode 100644 backend/core/src/property-groups/property-groups.service.ts delete mode 100644 backend/core/src/property/dto/property.dto.ts delete mode 100644 backend/core/src/property/entities/property.entity.ts delete mode 100644 backend/core/src/property/properties.controller.ts delete mode 100644 backend/core/src/property/properties.module.ts delete mode 100644 backend/core/src/property/properties.service.ts delete mode 100644 backend/core/test/properties/properties.e2e-spec.ts diff --git a/backend/core/src/app.module.ts b/backend/core/src/app.module.ts index 4f1ce39127..d3792b76f7 100644 --- a/backend/core/src/app.module.ts +++ b/backend/core/src/app.module.ts @@ -17,8 +17,6 @@ import { ListingsModule } from "./listings/listings.module" import { ApplicationsModule } from "./applications/applications.module" import { PreferencesModule } from "./preferences/preferences.module" import { UnitsModule } from "./units/units.module" -import { PropertyGroupsModule } from "./property-groups/property-groups.module" -import { PropertiesModule } from "./property/properties.module" import { AmiChartsModule } from "./ami-charts/ami-charts.module" import { ApplicationFlaggedSetsModule } from "./application-flagged-sets/application-flagged-sets.module" import * as bodyParser from "body-parser" @@ -94,8 +92,6 @@ export class AppModule { PaperApplicationsModule, PreferencesModule, ProgramsModule, - PropertiesModule, - PropertyGroupsModule, ProgramsModule, ReservedCommunityTypesModule, SharedModule, diff --git a/backend/core/src/listings/dto/filter-type-to-field-map.ts b/backend/core/src/listings/dto/filter-type-to-field-map.ts index df640b0b88..ff90028fe9 100644 --- a/backend/core/src/listings/dto/filter-type-to-field-map.ts +++ b/backend/core/src/listings/dto/filter-type-to-field-map.ts @@ -4,7 +4,7 @@ import { ListingFilterKeys } from "../../.." export const filterTypeToFieldMap: Record = { status: "listings.status", name: "listings.name", - neighborhood: "property.neighborhood", + neighborhood: "listings.neighborhood", bedrooms: "unitTypeRef.num_bedrooms", zipcode: "buildingAddress.zipCode", leasingAgents: "leasingAgents.id", diff --git a/backend/core/src/listings/dto/listing-create.dto.ts b/backend/core/src/listings/dto/listing-create.dto.ts index 4a3c7fbb0f..fcf6335e49 100644 --- a/backend/core/src/listings/dto/listing-create.dto.ts +++ b/backend/core/src/listings/dto/listing-create.dto.ts @@ -37,20 +37,7 @@ export class ListingCreateDto extends OmitType(ListingDto, [ "urlSlug", "showWaitlist", "units", - "accessibility", - "amenities", "buildingAddress", - "buildingTotalUnits", - "developer", - "householdSizeMax", - "householdSizeMin", - "neighborhood", - "petPolicy", - "smokingPolicy", - "unitsAvailable", - "unitAmenities", - "servicesOffered", - "yearBuilt", "unitsSummarized", "jurisdiction", "reservedCommunityType", @@ -124,16 +111,6 @@ export class ListingCreateDto extends OmitType(ListingDto, [ @Type(() => UnitCreateDto) units: UnitCreateDto[] - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - accessibility?: string | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - amenities?: string | null - @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsDefined({ groups: [ValidationsGroupsEnum.default] }) @@ -141,61 +118,6 @@ export class ListingCreateDto extends OmitType(ListingDto, [ @Type(() => AddressCreateDto) buildingAddress?: AddressCreateDto | null - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - buildingTotalUnits?: number | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - developer?: string | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - householdSizeMax?: number | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - householdSizeMin?: number | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - neighborhood?: string | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - petPolicy?: string | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - smokingPolicy?: string | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - unitsAvailable?: number | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - unitAmenities?: string | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - servicesOffered?: string | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - yearBuilt?: number | null - @Expose() @IsDefined({ groups: [ValidationsGroupsEnum.default] }) @ValidateNested({ groups: [ValidationsGroupsEnum.default] }) diff --git a/backend/core/src/listings/dto/listing-update.dto.ts b/backend/core/src/listings/dto/listing-update.dto.ts index 0dc97de120..933430a15f 100644 --- a/backend/core/src/listings/dto/listing-update.dto.ts +++ b/backend/core/src/listings/dto/listing-update.dto.ts @@ -39,20 +39,7 @@ export class ListingUpdateDto extends OmitType(ListingDto, [ "leasingAgents", "showWaitlist", "units", - "accessibility", - "amenities", "buildingAddress", - "buildingTotalUnits", - "developer", - "householdSizeMax", - "householdSizeMin", - "neighborhood", - "petPolicy", - "smokingPolicy", - "unitsAvailable", - "unitAmenities", - "servicesOffered", - "yearBuilt", "unitsSummarized", "jurisdiction", "reservedCommunityType", @@ -143,16 +130,6 @@ export class ListingUpdateDto extends OmitType(ListingDto, [ @Type(() => UnitUpdateDto) units: UnitUpdateDto[] - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - accessibility?: string | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - amenities?: string | null - @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsDefined({ groups: [ValidationsGroupsEnum.default] }) @@ -160,61 +137,6 @@ export class ListingUpdateDto extends OmitType(ListingDto, [ @Type(() => AddressUpdateDto) buildingAddress?: AddressUpdateDto | null - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - buildingTotalUnits?: number | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - developer?: string | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - householdSizeMax?: number | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - householdSizeMin?: number | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - neighborhood?: string | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - petPolicy?: string | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - smokingPolicy?: string | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - unitsAvailable?: number | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - unitAmenities?: string | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - servicesOffered?: string | null - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - yearBuilt?: number | null - @Expose() @IsDefined({ groups: [ValidationsGroupsEnum.default] }) @ValidateNested({ groups: [ValidationsGroupsEnum.default] }) diff --git a/backend/core/src/listings/dto/listing.dto.ts b/backend/core/src/listings/dto/listing.dto.ts index 163b83f9a4..7e182b6f4f 100644 --- a/backend/core/src/listings/dto/listing.dto.ts +++ b/backend/core/src/listings/dto/listing.dto.ts @@ -1,5 +1,5 @@ import { Listing } from "../entities/listing.entity" -import { Expose, plainToClass, Transform, Type } from "class-transformer" +import { Expose, Transform, Type } from "class-transformer" import { IsDefined, IsEnum, IsNumber, IsOptional, IsString, ValidateNested } from "class-validator" import { ApiProperty, OmitType } from "@nestjs/swagger" import { AddressDto } from "../../shared/dto/address.dto" @@ -33,11 +33,12 @@ export class ListingDto extends OmitType(Listing, [ "leasingAgentAddress", "listingPreferences", "listingPrograms", - "property", "reservedCommunityType", "result", "unitsSummary", "features", + "units", + "buildingAddress", ] as const) { @Expose() @IsDefined({ groups: [ValidationsGroupsEnum.default] }) @@ -133,167 +134,77 @@ export class ListingDto extends OmitType(Listing, [ @Expose() @Type(() => UnitDto) - @Transform( - (value, obj: Listing) => { - return plainToClass(UnitDto, obj.property?.units) - }, - { toClassOnly: true } - ) units: UnitDto[] @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsString({ groups: [ValidationsGroupsEnum.default] }) - @Transform( - (value, obj: Listing) => { - return obj.property?.accessibility - }, - { toClassOnly: true } - ) accessibility?: string | null @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsString({ groups: [ValidationsGroupsEnum.default] }) - @Transform( - (value, obj: Listing) => { - return obj.property?.amenities - }, - { toClassOnly: true } - ) amenities?: string | null @Expose() @IsDefined({ groups: [ValidationsGroupsEnum.default] }) @ValidateNested({ groups: [ValidationsGroupsEnum.default] }) @Type(() => AddressDto) - @Transform( - (value, obj: Listing) => { - return plainToClass(AddressDto, obj.property.buildingAddress) - }, - { toClassOnly: true } - ) buildingAddress: AddressDto @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - @Transform( - (value, obj: Listing) => { - return obj.property?.buildingTotalUnits - }, - { toClassOnly: true } - ) buildingTotalUnits?: number | null @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsString({ groups: [ValidationsGroupsEnum.default] }) - @Transform( - (value, obj: Listing) => { - return obj.property?.developer - }, - { toClassOnly: true } - ) developer?: string | null @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - @Transform( - (value, obj: Listing) => { - return obj.property?.householdSizeMax - }, - { toClassOnly: true } - ) householdSizeMax?: number | null @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - @Transform( - (value, obj: Listing) => { - return obj.property?.householdSizeMin - }, - { toClassOnly: true } - ) householdSizeMin?: number | null @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsString({ groups: [ValidationsGroupsEnum.default] }) - @Transform( - (value, obj: Listing) => { - return obj.property?.neighborhood - }, - { toClassOnly: true } - ) neighborhood?: string | null @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsString({ groups: [ValidationsGroupsEnum.default] }) - @Transform( - (value, obj: Listing) => { - return obj.property?.petPolicy - }, - { toClassOnly: true } - ) petPolicy?: string | null @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsString({ groups: [ValidationsGroupsEnum.default] }) - @Transform( - (value, obj: Listing) => { - return obj.property?.smokingPolicy - }, - { toClassOnly: true } - ) smokingPolicy?: string | null @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - @Transform( - (value, obj: Listing) => { - return obj.property?.unitsAvailable - }, - { toClassOnly: true } - ) unitsAvailable?: number | null @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsString({ groups: [ValidationsGroupsEnum.default] }) - @Transform( - (value, obj: Listing) => { - return obj.property?.unitAmenities - }, - { toClassOnly: true } - ) unitAmenities?: string | null @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsString({ groups: [ValidationsGroupsEnum.default] }) - @Transform( - (value, obj: Listing) => { - return obj.property?.servicesOffered - }, - { toClassOnly: true } - ) servicesOffered?: string | null @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - @Transform( - (value, obj: Listing) => { - return obj.property?.yearBuilt - }, - { toClassOnly: true } - ) yearBuilt?: number | null @Expose() diff --git a/backend/core/src/listings/entities/listing.entity.ts b/backend/core/src/listings/entities/listing.entity.ts index 5b2b053411..7cb92e13c4 100644 --- a/backend/core/src/listings/entities/listing.entity.ts +++ b/backend/core/src/listings/entities/listing.entity.ts @@ -19,6 +19,7 @@ import { Expose, Type } from "class-transformer" import { IsBoolean, IsDate, + IsDefined, IsEmail, IsEnum, IsNumber, @@ -29,7 +30,6 @@ import { ValidateNested, } from "class-validator" import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger" -import { Property } from "../../property/entities/property.entity" import { ValidationsGroupsEnum } from "../../shared/types/validations-groups-enum" import { ListingStatus } from "../types/listing-status-enum" import { Jurisdiction } from "../../jurisdictions/entities/jurisdiction.entity" @@ -51,6 +51,7 @@ import { ListingProgram } from "../../program/entities/listing-program.entity" import { EnforceLowerCase } from "../../shared/decorators/enforceLowerCase.decorator" import { ListingPreference } from "../../preferences/entities/listing-preference.entity" import { ListingImage } from "./listing-image.entity" +import Unit from "../../units/entities/unit.entity" @Entity({ name: "listings" }) @Index(["jurisdiction"]) @@ -142,10 +143,94 @@ class Listing extends BaseEntity { @Type(() => ListingEvent) events: ListingEvent[] - @ManyToOne(() => Property, { nullable: false, cascade: true }) + @OneToMany(() => Unit, (unit) => unit.listing, { eager: true, cascade: true }) + units: Unit[] + + @Column({ type: "text", nullable: true }) @Expose() + @IsOptional({ groups: [ValidationsGroupsEnum.default] }) + @IsString({ groups: [ValidationsGroupsEnum.default] }) + accessibility?: string | null + + @Column({ type: "text", nullable: true }) + @Expose() + @IsOptional({ groups: [ValidationsGroupsEnum.default] }) + @IsString({ groups: [ValidationsGroupsEnum.default] }) + amenities?: string | null + + @OneToOne(() => Address, { eager: true, cascade: true }) + @JoinColumn() + @Expose() + @IsDefined({ groups: [ValidationsGroupsEnum.default] }) @ValidateNested({ groups: [ValidationsGroupsEnum.default] }) - property: Property + @Type(() => Address) + buildingAddress: Address + + @Column({ type: "integer", nullable: true }) + @Expose() + @IsOptional({ groups: [ValidationsGroupsEnum.default] }) + @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) + buildingTotalUnits?: number | null + + @Column({ type: "text", nullable: true }) + @Expose() + @IsOptional({ groups: [ValidationsGroupsEnum.default] }) + @IsString({ groups: [ValidationsGroupsEnum.default] }) + developer?: string | null + + @Column({ type: "integer", nullable: true }) + @Expose() + @IsOptional({ groups: [ValidationsGroupsEnum.default] }) + @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) + householdSizeMax?: number | null + + @Column({ type: "integer", nullable: true }) + @Expose() + @IsOptional({ groups: [ValidationsGroupsEnum.default] }) + @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) + householdSizeMin?: number | null + + @Column({ type: "text", nullable: true }) + @Expose() + @IsOptional({ groups: [ValidationsGroupsEnum.default] }) + @IsString({ groups: [ValidationsGroupsEnum.default] }) + neighborhood?: string | null + + @Column({ type: "text", nullable: true }) + @Expose() + @IsOptional({ groups: [ValidationsGroupsEnum.default] }) + @IsString({ groups: [ValidationsGroupsEnum.default] }) + petPolicy?: string | null + + @Column({ type: "text", nullable: true }) + @Expose() + @IsOptional({ groups: [ValidationsGroupsEnum.default] }) + @IsString({ groups: [ValidationsGroupsEnum.default] }) + smokingPolicy?: string | null + + @Column({ type: "integer", nullable: true }) + @Expose() + @IsOptional({ groups: [ValidationsGroupsEnum.default] }) + @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) + unitsAvailable?: number | null + + @Column({ type: "text", nullable: true }) + @Expose() + @IsOptional({ groups: [ValidationsGroupsEnum.default] }) + @IsString({ groups: [ValidationsGroupsEnum.default] }) + unitAmenities?: string | null + + @Column({ type: "text", nullable: true }) + @Expose() + @IsOptional({ groups: [ValidationsGroupsEnum.default] }) + @IsString({ groups: [ValidationsGroupsEnum.default] }) + servicesOffered?: string | null + + @Column({ type: "integer", nullable: true }) + @Expose() + @IsOptional({ groups: [ValidationsGroupsEnum.default] }) + @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) + yearBuilt?: number | null @OneToMany(() => Application, (application) => application.listing) @Expose() diff --git a/backend/core/src/listings/listings.module.ts b/backend/core/src/listings/listings.module.ts index 17fcb0f023..161352f2f0 100644 --- a/backend/core/src/listings/listings.module.ts +++ b/backend/core/src/listings/listings.module.ts @@ -7,7 +7,6 @@ import { Unit } from "../units/entities/unit.entity" import { Preference } from "../preferences/entities/preference.entity" import { AuthModule } from "../auth/auth.module" import { User } from "../auth/entities/user.entity" -import { Property } from "../property/entities/property.entity" import { TranslationsModule } from "../translations/translations.module" import { AmiChart } from "../ami-charts/entities/ami-chart.entity" import { ListingFeatures } from "./entities/listing-features.entity" @@ -21,7 +20,6 @@ import { ListingRepository } from "./repositories/listing.repository" Preference, Unit, User, - Property, AmiChart, ListingRepository, ListingFeatures, diff --git a/backend/core/src/listings/listings.service.ts b/backend/core/src/listings/listings.service.ts index 0f48257dfb..5d6ebca2f7 100644 --- a/backend/core/src/listings/listings.service.ts +++ b/backend/core/src/listings/listings.service.ts @@ -9,10 +9,8 @@ import { import { InjectRepository } from "@nestjs/typeorm" import { Pagination } from "nestjs-typeorm-paginate" import { In, Repository } from "typeorm" -import { plainToClass } from "class-transformer" import { Interval } from "@nestjs/schedule" import { Listing } from "./entities/listing.entity" -import { PropertyCreateDto, PropertyUpdateDto } from "../property/dto/property.dto" import { addFilters } from "../shared/query-filter" import { getView } from "./views/view" import { summarizeUnits } from "../shared/units-transformations" @@ -60,10 +58,9 @@ export class ListingsService { const innerFilteredQuery = this.listingRepository .createQueryBuilder("listings") .select("listings.id", "listings_id") - .leftJoin("listings.property", "property") .leftJoin("listings.leasingAgents", "leasingAgents") - .leftJoin("property.buildingAddress", "buildingAddress") - .leftJoin("property.units", "units") + .leftJoin("listings.buildingAddress", "buildingAddress") + .leftJoin("listings.units", "units") .leftJoin("units.unitType", "unitTypeRef") const orderByConditions = ListingsService.buildOrderByConditions(params) @@ -75,7 +72,7 @@ export class ListingsService { ) } - innerFilteredQuery.groupBy("listings.id").addGroupBy("property.id") + innerFilteredQuery.groupBy("listings.id") if (params.filter) { addFilters, typeof filterTypeToFieldMap>( @@ -168,7 +165,6 @@ export class ListingsService { ...listingDto, publishedAt: listingDto.status === ListingStatus.active ? new Date() : null, closedAt: listingDto.status === ListingStatus.closed ? new Date() : null, - property: plainToClass(PropertyCreateDto, listingDto), }) return await listing.save() @@ -197,7 +193,7 @@ export class ListingsService { listingDto.unitsAvailable = availableUnits Object.assign(listing, { - ...plainToClass(Listing, listingDto, { excludeExtraneousValues: true }), + ...listingDto, publishedAt: listing.status !== ListingStatus.active && listingDto.status === ListingStatus.active ? new Date() @@ -206,17 +202,6 @@ export class ListingsService { listing.status !== ListingStatus.closed && listingDto.status === ListingStatus.closed ? new Date() : listing.closedAt, - property: plainToClass( - PropertyUpdateDto, - { - // NOTE: Create a property out of fields encapsulated in listingDto - ...listingDto, - // NOTE: Since we use the entire listingDto to create a property object the listing ID - // would overwrite propertyId fetched from DB - id: listing.property.id, - }, - { excludeExtraneousValues: true } - ), }) return await this.listingRepository.save(listing) @@ -272,11 +257,11 @@ export class ListingsService { } private async addUnitsSummarized(listing: Listing) { - if (Array.isArray(listing.property.units) && listing.property.units.length > 0) { + if (Array.isArray(listing.units) && listing.units.length > 0) { const amiCharts = await this.amiChartsRepository.find({ - where: { id: In(listing.property.units.map((unit) => unit.amiChartId)) }, + where: { id: In(listing.units.map((unit) => unit.amiChartId)) }, }) - listing.unitsSummarized = summarizeUnits(listing.property.units, amiCharts, listing) + listing.unitsSummarized = summarizeUnits(listing.units, amiCharts, listing) } return listing } diff --git a/backend/core/src/listings/tests/listings.service.spec.ts b/backend/core/src/listings/tests/listings.service.spec.ts index 3fb961fd33..c4fd662d46 100644 --- a/backend/core/src/listings/tests/listings.service.spec.ts +++ b/backend/core/src/listings/tests/listings.service.spec.ts @@ -22,49 +22,49 @@ let service: ListingsService const mockListings = [ { id: "asdf1", - property: { id: "test-property1", units: [] }, + units: [], preferences: [], status: "closed", unitsSummarized: { byUnitTypeAndRent: [] }, }, { id: "asdf2", - property: { id: "test-property2", units: [] }, + units: [], preferences: [], status: "closed", unitsSummarized: { byUnitTypeAndRent: [] }, }, { id: "asdf3", - property: { id: "test-property3", units: [] }, + units: [], preferences: [], status: "closed", unitsSummarized: { byUnitTypeAndRent: [] }, }, { id: "asdf4", - property: { id: "test-property4", units: [] }, + units: [], preferences: [], status: "closed", unitsSummarized: { byUnitTypeAndRent: [] }, }, { id: "asdf5", - property: { id: "test-property5", units: [] }, + units: [], preferences: [], status: "closed", unitsSummarized: { byUnitTypeAndRent: [] }, }, { id: "asdf6", - property: { id: "test-property6", units: [] }, + units: [], preferences: [], status: "closed", unitsSummarized: { byUnitTypeAndRent: [] }, }, { id: "asdf7", - property: { id: "test-property7", units: [] }, + units: [], preferences: [], status: "closed", unitsSummarized: { byUnitTypeAndRent: [] }, @@ -166,7 +166,7 @@ describe("ListingsService", () => { expect(listings.items).toEqual(mockListings) expect(mockInnerQueryBuilder.andWhere).toHaveBeenCalledWith( - "(LOWER(CAST(property.neighborhood as text)) = LOWER(:neighborhood_0))", + "(LOWER(CAST(listings.neighborhood as text)) = LOWER(:neighborhood_0))", { neighborhood_0: expectedNeighborhood, } @@ -194,7 +194,7 @@ describe("ListingsService", () => { expect(listings.items).toEqual(mockListings) expect(mockInnerQueryBuilder.andWhere).toHaveBeenCalledWith( - "(LOWER(CAST(property.neighborhood as text)) IN (:...neighborhood_0))", + "(LOWER(CAST(listings.neighborhood as text)) IN (:...neighborhood_0))", { neighborhood_0: expectedNeighborhoodArray, } diff --git a/backend/core/src/listings/views/config.ts b/backend/core/src/listings/views/config.ts index a43dc9b1f4..0e4e0999fb 100644 --- a/backend/core/src/listings/views/config.ts +++ b/backend/core/src/listings/views/config.ts @@ -19,8 +19,7 @@ const views: Views = { "jurisdiction.name", "reservedCommunityType.id", "reservedCommunityType.name", - "property.id", - "property.unitsAvailable", + "listings.unitsAvailable", ...getBaseAddressSelect(["buildingAddress"]), "units.id", "units.floor", @@ -60,9 +59,8 @@ const views: Views = { ], leftJoins: [ { join: "listings.jurisdiction", alias: "jurisdiction" }, - { join: "listings.property", alias: "property" }, - { join: "property.buildingAddress", alias: "buildingAddress" }, - { join: "property.units", alias: "units" }, + { join: "listings.buildingAddress", alias: "buildingAddress" }, + { join: "listings.units", alias: "units" }, { join: "units.unitType", alias: "unitType" }, { join: "units.amiChartOverride", alias: "amiChartOverride" }, { join: "listings.reservedCommunityType", alias: "reservedCommunityType" }, @@ -85,9 +83,8 @@ views.partnerList = { "listings.status", "listings.waitlistMaxSize", "listings.waitlistCurrentSize", - "property.unitsAvailable", + "listings.unitsAvailable", ], - leftJoins: [{ join: "listings.property", alias: "property" }], } views.detail = { @@ -205,9 +202,8 @@ views.full = { ["listings.leasingAgents", "leasingAgents"], ["listings.listingPreferences", "listingPreferences"], ["listingPreferences.preference", "listingPreferencesPreference"], - ["listings.property", "property"], - ["property.buildingAddress", "buildingAddress"], - ["property.units", "units"], + ["listings.buildingAddress", "buildingAddress"], + ["listings.units", "units"], ["units.amiChartOverride", "amiChartOverride"], ["units.unitType", "unitTypeRef"], ["units.unitRentType", "unitRentType"], diff --git a/backend/core/src/listings/views/view.spec.ts b/backend/core/src/listings/views/view.spec.ts index eb9955c665..f059b85a11 100644 --- a/backend/core/src/listings/views/view.spec.ts +++ b/backend/core/src/listings/views/view.spec.ts @@ -20,23 +20,19 @@ const mockUnitTypes = [ const mockListings = [ { id: "listing-1", - property: { - units: [ - { unitType: mockUnitTypes[0], minimumIncome: "0", rent: "100" }, - { unitType: mockUnitTypes[0], minimumIncome: "1", rent: "101" }, - { unitType: mockUnitTypes[1], minimumIncome: "0", rent: "100" }, - ], - }, + units: [ + { unitType: mockUnitTypes[0], minimumIncome: "0", rent: "100" }, + { unitType: mockUnitTypes[0], minimumIncome: "1", rent: "101" }, + { unitType: mockUnitTypes[1], minimumIncome: "0", rent: "100" }, + ], }, { id: "listing-2", - property: { - units: [ - { unitType: mockUnitTypes[0], minimumIncome: "0", rent: "100" }, - { unitType: mockUnitTypes[1], minimumIncome: "1", rent: "101" }, - { unitType: mockUnitTypes[2], minimumIncome: "2", rent: "102" }, - ], - }, + units: [ + { unitType: mockUnitTypes[0], minimumIncome: "0", rent: "100" }, + { unitType: mockUnitTypes[1], minimumIncome: "1", rent: "101" }, + { unitType: mockUnitTypes[2], minimumIncome: "2", rent: "102" }, + ], }, ] @@ -59,7 +55,7 @@ describe("listing views", () => { view.getViewQb() expect(mockQueryBuilder.select).toHaveBeenCalledTimes(1) - expect(mockQueryBuilder.leftJoin).toHaveBeenCalledTimes(14) + expect(mockQueryBuilder.leftJoin).toHaveBeenCalledTimes(13) }) it("should map unitSummary to listings", () => { @@ -80,7 +76,7 @@ describe("listing views", () => { view.getViewQb() - expect(mockQueryBuilder.leftJoinAndSelect).toHaveBeenCalledTimes(29) + expect(mockQueryBuilder.leftJoinAndSelect).toHaveBeenCalledTimes(28) }) }) diff --git a/backend/core/src/listings/views/view.ts b/backend/core/src/listings/views/view.ts index 7b627faa13..67c098e995 100644 --- a/backend/core/src/listings/views/view.ts +++ b/backend/core/src/listings/views/view.ts @@ -38,7 +38,7 @@ export class BaseListingView extends BaseView { return listings.map((listing) => ({ ...listing, unitsSummarized: { - byUnitTypeAndRent: summarizeUnitsByTypeAndRent(listing.property.units, listing), + byUnitTypeAndRent: summarizeUnitsByTypeAndRent(listing.units, listing), }, })) } diff --git a/backend/core/src/migration/1655821661726-remove-property-relation.ts b/backend/core/src/migration/1655821661726-remove-property-relation.ts new file mode 100644 index 0000000000..c3f07a8845 --- /dev/null +++ b/backend/core/src/migration/1655821661726-remove-property-relation.ts @@ -0,0 +1,161 @@ +import { MigrationInterface, QueryRunner } from "typeorm" + +export class removePropertyRelation1655821661726 implements MigrationInterface { + name = "removePropertyRelation1655821661726" + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "units" DROP CONSTRAINT "FK_f221e6d7bfd686266003b982b5f"`) + await queryRunner.query( + `ALTER TABLE "listings" DROP CONSTRAINT "FK_9eef913a9013d6e3d09a92ec075"` + ) + + await queryRunner.query(`ALTER TABLE "listings" ADD "accessibility" text`) + await queryRunner.query(`ALTER TABLE "listings" ADD "amenities" text`) + await queryRunner.query(`ALTER TABLE "listings" ADD "building_total_units" integer`) + await queryRunner.query(`ALTER TABLE "listings" ADD "developer" text`) + await queryRunner.query(`ALTER TABLE "listings" ADD "household_size_max" integer`) + await queryRunner.query(`ALTER TABLE "listings" ADD "household_size_min" integer`) + await queryRunner.query(`ALTER TABLE "listings" ADD "neighborhood" text`) + await queryRunner.query(`ALTER TABLE "listings" ADD "pet_policy" text`) + await queryRunner.query(`ALTER TABLE "listings" ADD "smoking_policy" text`) + await queryRunner.query(`ALTER TABLE "listings" ADD "units_available" integer`) + await queryRunner.query(`ALTER TABLE "listings" ADD "unit_amenities" text`) + await queryRunner.query(`ALTER TABLE "listings" ADD "services_offered" text`) + await queryRunner.query(`ALTER TABLE "listings" ADD "year_built" integer`) + await queryRunner.query(`ALTER TABLE "listings" ADD "building_address_id" uuid`) + + await queryRunner.query(`UPDATE listings + SET accessibility = property.accessibility, + amenities = property.amenities, + building_total_units = property.building_total_units, + developer = property.developer, + household_size_max = property.household_size_max, + household_size_min = property.household_size_min, + neighborhood = property.neighborhood, + pet_policy = property.pet_policy, + smoking_policy = property.smoking_policy, + units_available = property.units_available, + unit_amenities = property.unit_amenities, + services_offered = property.services_offered, + year_built = property.year_built, + building_address_id = property.building_address_id + FROM property INNER JOIN listings l ON l.property_id = property.id`) + + await queryRunner.query(`ALTER TABLE "units" ADD COLUMN "listing_id" uuid`) + + const listings: [{ id: string; property_id: string }] = await queryRunner.query( + `SELECT id, property_id FROM LISTINGS` + ) + for (const l of listings) { + await queryRunner.query(`UPDATE units SET listing_id = ($1) WHERE property_id = ($2)`, [ + l.id, + l.property_id, + ]) + } + + await queryRunner.query(`ALTER TABLE "units" DROP COLUMN "property_id"`) + await queryRunner.query(`ALTER TABLE "listings" DROP COLUMN "property_id"`) + await queryRunner.query(`DROP TABLE "property_group_properties_property"`) + await queryRunner.query( + `ALTER TABLE "property" DROP CONSTRAINT "UQ_f0f7062f34738e0b338163786fd"` + ) + await queryRunner.query(`DROP TABLE "property"`) + + await queryRunner.query( + `ALTER TABLE "units" ADD CONSTRAINT "FK_9aebcde52d6e054e5ac5d26228c" FOREIGN KEY ("listing_id") REFERENCES "listings"("id") ON DELETE CASCADE ON UPDATE CASCADE` + ) + await queryRunner.query( + `ALTER TABLE "listings" ADD CONSTRAINT "FK_e5d5291cd6ab92cbec304aab905" FOREIGN KEY ("building_address_id") REFERENCES "address"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` + ) + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "listings" DROP CONSTRAINT "FK_e5d5291cd6ab92cbec304aab905"` + ) + await queryRunner.query(`ALTER TABLE "units" DROP CONSTRAINT "FK_9aebcde52d6e054e5ac5d26228c"`) + + await queryRunner.query(`CREATE TABLE "property"`) + await queryRunner.query(`ALTER TABLE "property" ADD "accessibility" text`) + await queryRunner.query(`ALTER TABLE "property" ADD "amenities" text`) + await queryRunner.query(`ALTER TABLE "property" ADD "building_total_units" integer`) + await queryRunner.query(`ALTER TABLE "property" ADD "developer" text`) + await queryRunner.query(`ALTER TABLE "property" ADD "household_size_max" integer`) + await queryRunner.query(`ALTER TABLE "property" ADD "household_size_min" integer`) + await queryRunner.query(`ALTER TABLE "property" ADD "neighborhood" text`) + await queryRunner.query(`ALTER TABLE "property" ADD "pet_policy" text`) + await queryRunner.query(`ALTER TABLE "property" ADD "smoking_policy" text`) + await queryRunner.query(`ALTER TABLE "property" ADD "units_available" integer`) + await queryRunner.query(`ALTER TABLE "property" ADD "unit_amenities" text`) + await queryRunner.query(`ALTER TABLE "property" ADD "services_offered" text`) + await queryRunner.query(`ALTER TABLE "property" ADD "year_built" integer`) + await queryRunner.query(`ALTER TABLE "property" ADD "building_address_id" uuid`) + + await queryRunner.query(`ALTER TABLE "units" ADD COLUMN "property_id" uuid`) + await queryRunner.query(`ALTER TABLE "listings" ADD COLUMN "property_id" uuid`) + + const listings = await queryRunner.query(`SELECT * FROM listings`) + + for (const l of listings) { + const [newProperty] = await queryRunner.query( + `INSERT INTO "property" (accessibility, amenities, + building_total_units, developer, household_size_max, household_size_min, + neighborhood, pet_policy, smoking_policy, units_available, unit_amenities, + services_offered, year_built, building_address_id) + VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) + RETURNING id`, + [ + l.accessibility, + l.amenities, + l.building_total_units, + l.developer, + l.household_size_max, + l.household_size_min, + l.neighborhood, + l.pet_policy, + l.smoking_policy, + l.units_available, + l.unit_amenities, + l.services_offered, + l.year_built, + l.building_address_id, + ] + ) + await queryRunner.query(`UPDATE listings SET property_id = ($1) WHERE id = ($2)`, [ + newProperty.id, + l.id, + ]) + await queryRunner.query(`UPDATE units SET property_id = ($1) WHERE listing_id = ($2)`, [ + newProperty.id, + l.id, + ]) + } + + await queryRunner.query(`ALTER TABLE "units" ALTER COLUMN "property_id" SET NOT NULL`) + await queryRunner.query(`ALTER TABLE "listings" ALTER COLUMN "property_id" SET NOT NULL`) + + await queryRunner.query(`ALTER TABLE "listings" DROP COLUMN "building_address_id"`) + await queryRunner.query(`ALTER TABLE "listings" DROP COLUMN "year_built"`) + await queryRunner.query(`ALTER TABLE "listings" DROP COLUMN "services_offered"`) + await queryRunner.query(`ALTER TABLE "listings" DROP COLUMN "unit_amenities"`) + await queryRunner.query(`ALTER TABLE "listings" DROP COLUMN "units_available"`) + await queryRunner.query(`ALTER TABLE "listings" DROP COLUMN "smoking_policy"`) + await queryRunner.query(`ALTER TABLE "listings" DROP COLUMN "pet_policy"`) + await queryRunner.query(`ALTER TABLE "listings" DROP COLUMN "neighborhood"`) + await queryRunner.query(`ALTER TABLE "listings" DROP COLUMN "household_size_min"`) + await queryRunner.query(`ALTER TABLE "listings" DROP COLUMN "household_size_max"`) + await queryRunner.query(`ALTER TABLE "listings" DROP COLUMN "developer"`) + await queryRunner.query(`ALTER TABLE "listings" DROP COLUMN "building_total_units"`) + await queryRunner.query(`ALTER TABLE "listings" DROP COLUMN "amenities"`) + await queryRunner.query(`ALTER TABLE "listings" DROP COLUMN "accessibility"`) + + await queryRunner.query(`ALTER TABLE "units" DROP COLUMN "listing_id"`) + + await queryRunner.query( + `ALTER TABLE "listings" ADD CONSTRAINT "FK_9eef913a9013d6e3d09a92ec075" FOREIGN KEY ("property_id") REFERENCES "property"("id") ON DELETE NO ACTION ON UPDATE NO ACTION` + ) + await queryRunner.query( + `ALTER TABLE "units" ADD CONSTRAINT "FK_f221e6d7bfd686266003b982b5f" FOREIGN KEY ("property_id") REFERENCES "property"("id") ON DELETE CASCADE ON UPDATE CASCADE` + ) + } +} diff --git a/backend/core/src/property-groups/dto/property-group.dto.ts b/backend/core/src/property-groups/dto/property-group.dto.ts deleted file mode 100644 index afd8e8de77..0000000000 --- a/backend/core/src/property-groups/dto/property-group.dto.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Expose, Type } from "class-transformer" -import { IsDefined, IsString, IsUUID, ValidateNested } from "class-validator" -import { IdDto } from "../../shared/dto/id.dto" -import { PropertyGroup } from "../entities/property-group.entity" -import { OmitType } from "@nestjs/swagger" -import { ValidationsGroupsEnum } from "../../shared/types/validations-groups-enum" - -export class PropertyGroupDto extends OmitType(PropertyGroup, ["properties"] as const) { - @Expose() - @IsDefined({ groups: [ValidationsGroupsEnum.default] }) - @ValidateNested({ groups: [ValidationsGroupsEnum.default], each: true }) - @Type(() => IdDto) - properties: IdDto[] -} - -export class PropertyGroupCreateDto extends OmitType(PropertyGroupDto, [ - "id", - "createdAt", - "updatedAt", -] as const) {} - -export class PropertyGroupUpdateDto extends PropertyGroupCreateDto { - @Expose() - @IsString({ groups: [ValidationsGroupsEnum.default] }) - @IsUUID() - id: string -} diff --git a/backend/core/src/property-groups/entities/property-group.entity.ts b/backend/core/src/property-groups/entities/property-group.entity.ts deleted file mode 100644 index 4cbfb5916c..0000000000 --- a/backend/core/src/property-groups/entities/property-group.entity.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { - Column, - CreateDateColumn, - Entity, - JoinTable, - ManyToMany, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from "typeorm" -import { Expose, Type } from "class-transformer" -import { IsDate, IsString, IsUUID } from "class-validator" -import { Property } from "../../property/entities/property.entity" -import { ValidationsGroupsEnum } from "../../shared/types/validations-groups-enum" - -@Entity() -export class PropertyGroup { - @PrimaryGeneratedColumn("uuid") - @Expose() - @IsString({ groups: [ValidationsGroupsEnum.default] }) - @IsUUID(4, { groups: [ValidationsGroupsEnum.default] }) - id: string - - @CreateDateColumn() - @Expose() - @IsDate({ groups: [ValidationsGroupsEnum.default] }) - @Type(() => Date) - createdAt: Date - - @UpdateDateColumn() - @Expose() - @IsDate({ groups: [ValidationsGroupsEnum.default] }) - @Type(() => Date) - updatedAt: Date - - @Column() - @Expose() - @IsString({ groups: [ValidationsGroupsEnum.default] }) - name: string - - @ManyToMany(() => Property) - @JoinTable() - properties: Property[] -} diff --git a/backend/core/src/property-groups/property-groups.controller.ts b/backend/core/src/property-groups/property-groups.controller.ts deleted file mode 100644 index 2dc50a2df3..0000000000 --- a/backend/core/src/property-groups/property-groups.controller.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { ApiBearerAuth, ApiOperation, ApiTags } from "@nestjs/swagger" -import { DefaultAuthGuard } from "../auth/guards/default.guard" -import { AuthzGuard } from "../auth/guards/authz.guard" -import { ResourceType } from "../auth/decorators/resource-type.decorator" -import { mapTo } from "../shared/mapTo" -import { PropertyGroupsService } from "./property-groups.service" -import { - PropertyGroupCreateDto, - PropertyGroupDto, - PropertyGroupUpdateDto, -} from "./dto/property-group.dto" -import { - Body, - Controller, - Delete, - Get, - Param, - Post, - Put, - UseGuards, - UsePipes, - ValidationPipe, -} from "@nestjs/common" -import { defaultValidationPipeOptions } from "../shared/default-validation-pipe-options" - -@Controller("propertyGroups") -@ApiTags("propertyGroups") -@ApiBearerAuth() -@ResourceType("propertyGroup") -@UseGuards(DefaultAuthGuard, AuthzGuard) -@UsePipes(new ValidationPipe(defaultValidationPipeOptions)) -export class PropertyGroupsController { - constructor(private readonly propertyGroupsService: PropertyGroupsService) {} - - @Get() - @ApiOperation({ summary: "List propertyGroups", operationId: "list" }) - async list(): Promise { - return mapTo(PropertyGroupDto, await this.propertyGroupsService.list()) - } - - @Post() - @ApiOperation({ summary: "Create propertyGroup", operationId: "create" }) - async create(@Body() propertyGroup: PropertyGroupCreateDto): Promise { - return mapTo(PropertyGroupDto, await this.propertyGroupsService.create(propertyGroup)) - } - - @Put(`:propertyGroupId`) - @ApiOperation({ summary: "Update propertyGroup", operationId: "update" }) - async update(@Body() propertyGroup: PropertyGroupUpdateDto): Promise { - return mapTo(PropertyGroupDto, await this.propertyGroupsService.update(propertyGroup)) - } - - @Get(`:propertyGroupId`) - @ApiOperation({ summary: "Get propertyGroup by id", operationId: "retrieve" }) - async retrieve(@Param("propertyGroupId") propertyGroupId: string): Promise { - return mapTo( - PropertyGroupDto, - await this.propertyGroupsService.findOne({ where: { id: propertyGroupId } }) - ) - } - - @Delete(`:propertyGroupId`) - @ApiOperation({ summary: "Delete propertyGroup by id", operationId: "delete" }) - async delete(@Param("propertyGroupId") propertyGroupId: string): Promise { - return await this.propertyGroupsService.delete(propertyGroupId) - } -} diff --git a/backend/core/src/property-groups/property-groups.module.ts b/backend/core/src/property-groups/property-groups.module.ts deleted file mode 100644 index 9d311d5909..0000000000 --- a/backend/core/src/property-groups/property-groups.module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Module } from "@nestjs/common" -import { PropertyGroupsController } from "./property-groups.controller" -import { PropertyGroupsService } from "./property-groups.service" -import { TypeOrmModule } from "@nestjs/typeorm" -import { AuthModule } from "../auth/auth.module" -import { PropertyGroup } from "./entities/property-group.entity" - -@Module({ - imports: [TypeOrmModule.forFeature([PropertyGroup]), AuthModule], - controllers: [PropertyGroupsController], - providers: [PropertyGroupsService], -}) -export class PropertyGroupsModule {} diff --git a/backend/core/src/property-groups/property-groups.service.ts b/backend/core/src/property-groups/property-groups.service.ts deleted file mode 100644 index 9a4772cb08..0000000000 --- a/backend/core/src/property-groups/property-groups.service.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { PropertyGroupCreateDto, PropertyGroupUpdateDto } from "./dto/property-group.dto" -import { PropertyGroup } from "./entities/property-group.entity" -import { AbstractServiceFactory } from "../shared/services/abstract-service" -import { Injectable } from "@nestjs/common" - -@Injectable() -export class PropertyGroupsService extends AbstractServiceFactory< - PropertyGroup, - PropertyGroupCreateDto, - PropertyGroupUpdateDto ->(PropertyGroup) {} diff --git a/backend/core/src/property/dto/property.dto.ts b/backend/core/src/property/dto/property.dto.ts deleted file mode 100644 index 58b303fd5e..0000000000 --- a/backend/core/src/property/dto/property.dto.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { ApiHideProperty, OmitType } from "@nestjs/swagger" -import { Exclude, Expose, Type } from "class-transformer" -import { IsDate, IsDefined, IsOptional, IsUUID, ValidateNested } from "class-validator" -import { Property } from "../entities/property.entity" -import { AddressDto, AddressUpdateDto } from "../../shared/dto/address.dto" -import { UnitDto } from "../../units/dto/unit.dto" -import { ValidationsGroupsEnum } from "../../shared/types/validations-groups-enum" -import { UnitCreateDto } from "../../units/dto/unit-create.dto" -import { UnitUpdateDto } from "../../units/dto/unit-update.dto" - -export class PropertyDto extends OmitType(Property, [ - "units", - "propertyGroups", - "buildingAddress", -] as const) { - @Exclude() - @ApiHideProperty() - propertyGroups - - @Expose() - @IsDefined({ groups: [ValidationsGroupsEnum.default] }) - @ValidateNested({ groups: [ValidationsGroupsEnum.default], each: true }) - @Type(() => UnitDto) - units: UnitDto[] - - @ValidateNested({ groups: [ValidationsGroupsEnum.default] }) - @Expose() - @IsDefined({ groups: [ValidationsGroupsEnum.default] }) - @ValidateNested({ groups: [ValidationsGroupsEnum.default] }) - @Type(() => AddressDto) - buildingAddress: AddressDto -} - -export class PropertyCreateDto extends OmitType(PropertyDto, [ - "id", - "createdAt", - "updatedAt", - "buildingAddress", - "units", -] as const) { - @Expose() - @IsDefined({ groups: [ValidationsGroupsEnum.default] }) - @ValidateNested({ groups: [ValidationsGroupsEnum.default] }) - @Type(() => AddressUpdateDto) - buildingAddress: AddressUpdateDto - - @Expose() - @IsDefined({ groups: [ValidationsGroupsEnum.default] }) - @ValidateNested({ groups: [ValidationsGroupsEnum.default], each: true }) - @Type(() => UnitCreateDto) - units: UnitCreateDto[] -} - -export class PropertyUpdateDto extends OmitType(PropertyDto, [ - "id", - "createdAt", - "updatedAt", - "buildingAddress", - "units", -] as const) { - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsUUID(4, { groups: [ValidationsGroupsEnum.default] }) - id?: string - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsDate({ groups: [ValidationsGroupsEnum.default] }) - @Type(() => Date) - createdAt?: Date - - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsDate({ groups: [ValidationsGroupsEnum.default] }) - @Type(() => Date) - updatedAt?: Date - - @Expose() - @IsDefined({ groups: [ValidationsGroupsEnum.default] }) - @ValidateNested({ groups: [ValidationsGroupsEnum.default] }) - @Type(() => AddressUpdateDto) - buildingAddress: AddressUpdateDto - - @Expose() - @IsDefined({ groups: [ValidationsGroupsEnum.default] }) - @ValidateNested({ groups: [ValidationsGroupsEnum.default], each: true }) - @Type(() => UnitUpdateDto) - units: UnitUpdateDto[] -} diff --git a/backend/core/src/property/entities/property.entity.ts b/backend/core/src/property/entities/property.entity.ts deleted file mode 100644 index 3fcb47c732..0000000000 --- a/backend/core/src/property/entities/property.entity.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { - Column, - CreateDateColumn, - Entity, - JoinColumn, - ManyToMany, - OneToMany, - OneToOne, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from "typeorm" -import { Expose, Type } from "class-transformer" -import { - IsDate, - IsDefined, - IsNumber, - IsOptional, - IsString, - IsUUID, - ValidateNested, -} from "class-validator" -import { Unit } from "../../units/entities/unit.entity" -import { PropertyGroup } from "../../property-groups/entities/property-group.entity" -import { Address } from "../../shared/entities/address.entity" -import { ValidationsGroupsEnum } from "../../shared/types/validations-groups-enum" - -@Entity() -export class Property { - @PrimaryGeneratedColumn("uuid") - @Expose() - @IsString({ groups: [ValidationsGroupsEnum.default] }) - @IsUUID(4, { groups: [ValidationsGroupsEnum.default] }) - id: string - - @CreateDateColumn() - @Expose() - @IsDate({ groups: [ValidationsGroupsEnum.default] }) - @Type(() => Date) - createdAt: Date - - @UpdateDateColumn() - @Expose() - @IsDate({ groups: [ValidationsGroupsEnum.default] }) - @Type(() => Date) - updatedAt: Date - - @OneToMany(() => Unit, (unit) => unit.property, { eager: true, cascade: true }) - units: Unit[] - - @ManyToMany(() => PropertyGroup) - propertyGroups: PropertyGroup[] - - @Column({ type: "text", nullable: true }) - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - accessibility?: string | null - - @Column({ type: "text", nullable: true }) - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - amenities?: string | null - - @OneToOne(() => Address, { eager: true, cascade: true }) - @JoinColumn() - @Expose() - @IsDefined({ groups: [ValidationsGroupsEnum.default] }) - @ValidateNested({ groups: [ValidationsGroupsEnum.default] }) - @Type(() => Address) - buildingAddress: Address - - @Column({ type: "integer", nullable: true }) - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - buildingTotalUnits?: number | null - - @Column({ type: "text", nullable: true }) - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - developer?: string | null - - @Column({ type: "integer", nullable: true }) - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - householdSizeMax?: number | null - - @Column({ type: "integer", nullable: true }) - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - householdSizeMin?: number | null - - @Column({ type: "text", nullable: true }) - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - neighborhood?: string | null - - @Column({ type: "text", nullable: true }) - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - petPolicy?: string | null - - @Column({ type: "text", nullable: true }) - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - smokingPolicy?: string | null - - @Column({ type: "integer", nullable: true }) - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - unitsAvailable?: number | null - - @Column({ type: "text", nullable: true }) - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - unitAmenities?: string | null - - @Column({ type: "text", nullable: true }) - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsString({ groups: [ValidationsGroupsEnum.default] }) - servicesOffered?: string | null - - @Column({ type: "integer", nullable: true }) - @Expose() - @IsOptional({ groups: [ValidationsGroupsEnum.default] }) - @IsNumber({}, { groups: [ValidationsGroupsEnum.default] }) - yearBuilt?: number | null -} diff --git a/backend/core/src/property/properties.controller.ts b/backend/core/src/property/properties.controller.ts deleted file mode 100644 index 1179b761d7..0000000000 --- a/backend/core/src/property/properties.controller.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { - Body, - Controller, - Delete, - Get, - Param, - Post, - Put, - UseGuards, - UsePipes, - ValidationPipe, -} from "@nestjs/common" -import { ApiBearerAuth, ApiOperation, ApiTags } from "@nestjs/swagger" -import { DefaultAuthGuard } from "../auth/guards/default.guard" -import { AuthzGuard } from "../auth/guards/authz.guard" -import { ResourceType } from "../auth/decorators/resource-type.decorator" -import { mapTo } from "../shared/mapTo" -import { PropertiesService } from "./properties.service" -import { PropertyCreateDto, PropertyDto, PropertyUpdateDto } from "./dto/property.dto" -import { defaultValidationPipeOptions } from "../shared/default-validation-pipe-options" - -@Controller("properties") -@ApiTags("properties") -@ApiBearerAuth() -@ResourceType("property") -@UseGuards(DefaultAuthGuard, AuthzGuard) -@UsePipes(new ValidationPipe(defaultValidationPipeOptions)) -export class PropertiesController { - constructor(private readonly propertiesService: PropertiesService) {} - - @Get() - @ApiOperation({ summary: "List properties", operationId: "list" }) - async list(): Promise { - return mapTo(PropertyDto, await this.propertiesService.list()) - } - - @Post() - @ApiOperation({ summary: "Create property", operationId: "create" }) - async create(@Body() property: PropertyCreateDto): Promise { - return mapTo(PropertyDto, await this.propertiesService.create(property)) - } - - @Put(`:propertyId`) - @ApiOperation({ summary: "Update property", operationId: "update" }) - async update(@Body() property: PropertyUpdateDto): Promise { - return mapTo(PropertyDto, await this.propertiesService.update(property)) - } - - @Get(`:propertyId`) - @ApiOperation({ summary: "Get property by id", operationId: "retrieve" }) - async retrieve(@Param("propertyId") propertyId: string): Promise { - return mapTo(PropertyDto, await this.propertiesService.findOne({ where: { id: propertyId } })) - } - - @Delete(`:propertyId`) - @ApiOperation({ summary: "Delete property by id", operationId: "delete" }) - async delete(@Param("propertyId") propertyId: string): Promise { - return await this.propertiesService.delete(propertyId) - } -} diff --git a/backend/core/src/property/properties.module.ts b/backend/core/src/property/properties.module.ts deleted file mode 100644 index dbfebdde59..0000000000 --- a/backend/core/src/property/properties.module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Module } from "@nestjs/common" -import { PropertiesController } from "./properties.controller" -import { PropertiesService } from "./properties.service" -import { TypeOrmModule } from "@nestjs/typeorm" -import { Property } from "./entities/property.entity" -import { AuthModule } from "../auth/auth.module" - -@Module({ - imports: [TypeOrmModule.forFeature([Property]), AuthModule], - controllers: [PropertiesController], - providers: [PropertiesService], -}) -export class PropertiesModule {} diff --git a/backend/core/src/property/properties.service.ts b/backend/core/src/property/properties.service.ts deleted file mode 100644 index 4a70bc55a5..0000000000 --- a/backend/core/src/property/properties.service.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { AbstractServiceFactory } from "../shared/services/abstract-service" -import { PropertyCreateDto, PropertyUpdateDto } from "./dto/property.dto" -import { Property } from "./entities/property.entity" - -export class PropertiesService extends AbstractServiceFactory< - Property, - PropertyCreateDto, - PropertyUpdateDto ->(Property) {} diff --git a/backend/core/src/seeder/seeder.module.ts b/backend/core/src/seeder/seeder.module.ts index 6fed34e6ec..9e1895758e 100644 --- a/backend/core/src/seeder/seeder.module.ts +++ b/backend/core/src/seeder/seeder.module.ts @@ -17,7 +17,6 @@ import { ReservedCommunityType } from "../reserved-community-type/entities/reser import { UnitType } from "../unit-types/entities/unit-type.entity" import { UnitRentType } from "../unit-rent-types/entities/unit-rent-type.entity" import { AmiChart } from "../ami-charts/entities/ami-chart.entity" -import { Property } from "../property/entities/property.entity" import { Unit } from "../units/entities/unit.entity" import { User } from "../auth/entities/user.entity" import { UserRoles } from "../auth/entities/user-roles.entity" @@ -75,7 +74,6 @@ export class SeederModule { ReservedCommunityType, UnitRentType, AmiChart, - Property, Unit, User, UserRoles, diff --git a/backend/core/src/seeder/seeds/listings/listing-coliseum-seed.ts b/backend/core/src/seeder/seeds/listings/listing-coliseum-seed.ts index 6afd7fd16a..fcb84e38bb 100644 --- a/backend/core/src/seeder/seeds/listings/listing-coliseum-seed.ts +++ b/backend/core/src/seeder/seeds/listings/listing-coliseum-seed.ts @@ -1,4 +1,4 @@ -import { ListingSeedType, PropertySeedType, UnitSeedType } from "./listings" +import { ListingSeedType, UnitSeedType } from "./listings" import { getDate, getDefaultAssets, @@ -18,30 +18,6 @@ import { UnitCreateDto } from "../../../units/dto/unit-create.dto" import { Listing } from "../../../listings/entities/listing.entity" import { ListingAvailability } from "../../../listings/types/listing-availability-enum" -const coliseumProperty: PropertySeedType = { - accessibility: - "Fifteen (15) units are designed for residents with mobility impairments per HUD/U.F.A.S. guidelines with one (1) of these units further designed for residents with auditory or visual impairments. There are two (2) additional units with features for those with auditory or visual impairments. All the other units are adaptable. Accessible features in the property include: * 36” wide entries and doorways * Kitchens built to the accessibility standards of the California Building Code, including appliance controls and switch outlets within reach, and work surfaces and storage at accessible heights * Bathrooms built to the accessibility standards of the California Building Code, including grab bars, flexible shower spray hose, switch outlets within reach, and in-tub seats. * Closet rods and shelves at mobility height. * Window blinds/shades able to be used without grasping or twisting * Units for the Hearing & Visually Impaired will have a horn & strobe for fire alarm and a flashing light doorbell. The 44 non-ADA units are built to Adaptable standards.", - amenities: "Community room, bike parking, courtyard off the community room, 2nd floor courtyard.", - buildingAddress: { - county: "Alameda", - city: "Oakland", - street: "3300 Hawley Street", - zipCode: "94621", - state: "CA", - latitude: 37.7549632, - longitude: -122.1968792, - }, - buildingTotalUnits: 58, - developer: "Resources for Community Development", - neighborhood: "Coliseum", - petPolicy: "Permitted", - servicesOffered: - "Residential supportive services are provided to all residents on a volunteer basis.", - smokingPolicy: "No Smoking", - unitAmenities: null, - yearBuilt: 2021, -} - const coliseumListing: ListingSeedType = { jurisdictionName: "Alameda", digitalApplication: false, @@ -65,6 +41,27 @@ const coliseumListing: ListingSeedType = { latitude: 37.7549632, longitude: -122.1968792, }, + accessibility: + "Fifteen (15) units are designed for residents with mobility impairments per HUD/U.F.A.S. guidelines with one (1) of these units further designed for residents with auditory or visual impairments. There are two (2) additional units with features for those with auditory or visual impairments. All the other units are adaptable. Accessible features in the property include: * 36” wide entries and doorways * Kitchens built to the accessibility standards of the California Building Code, including appliance controls and switch outlets within reach, and work surfaces and storage at accessible heights * Bathrooms built to the accessibility standards of the California Building Code, including grab bars, flexible shower spray hose, switch outlets within reach, and in-tub seats. * Closet rods and shelves at mobility height. * Window blinds/shades able to be used without grasping or twisting * Units for the Hearing & Visually Impaired will have a horn & strobe for fire alarm and a flashing light doorbell. The 44 non-ADA units are built to Adaptable standards.", + amenities: "Community room, bike parking, courtyard off the community room, 2nd floor courtyard.", + buildingAddress: { + county: "Alameda", + city: "Oakland", + street: "3300 Hawley Street", + zipCode: "94621", + state: "CA", + latitude: 37.7549632, + longitude: -122.1968792, + }, + buildingTotalUnits: 58, + developer: "Resources for Community Development", + neighborhood: "Coliseum", + petPolicy: "Permitted", + servicesOffered: + "Residential supportive services are provided to all residents on a volunteer basis.", + smokingPolicy: "No Smoking", + unitAmenities: null, + yearBuilt: 2021, images: [], applicationPickUpAddressOfficeHours: null, buildingSelectionCriteria: null, @@ -930,56 +927,11 @@ export class ListingColiseumSeed extends ListingDefaultSeed { }, ] - const property = await this.propertyRepository.save({ - ...coliseumProperty, - unitsAvailable: coliseumUnits.length, - }) - - const unitsToBeCreated: Array> = coliseumUnits.map( - (unit) => { - return { - ...unit, - property: { - id: property.id, - }, - amiChart, - } - } - ) - - // Assign priorityTypes - for (let i = 0; i < 3; i++) { - unitsToBeCreated[i].priorityType = priorityTypeMobilityAndMobilityWithHearingAndVisual - } - for (let i = 3; i < 14; i++) { - unitsToBeCreated[i].priorityType = priorityTypeMobilityAndHearingWithVisual - } - for (let i = 14; i < 27; i++) { - unitsToBeCreated[i].priorityType = priorityTypeMobilityAndHearing - } - for (let i = 27; i < 46; i++) { - unitsToBeCreated[i].priorityType = priorityMobility - } - - // Assign unit types - for (let i = 0; i < 4; i++) { - unitsToBeCreated[i].unitType = unitTypeOneBdrm - } - for (let i = 4; i < 27; i++) { - unitsToBeCreated[i].unitType = unitTypeTwoBdrm - } - for (let i = 27; i < 46; i++) { - unitsToBeCreated[i].unitType = unitTypeThreeBdrm - } - - await this.unitsRepository.save(unitsToBeCreated) - const listingCreateDto: Omit< DeepPartial, keyof BaseEntity | "urlSlug" | "showWaitlist" > = { ...coliseumListing, - property: property, assets: getDefaultAssets(), listingPreferences: [ { @@ -1018,6 +970,45 @@ export class ListingColiseumSeed extends ListingDefaultSeed { ], } - return await this.listingRepository.save(listingCreateDto) + const listing = await this.listingRepository.save(listingCreateDto) + + const unitsToBeCreated: Array> = coliseumUnits.map( + (unit) => { + return { + ...unit, + amiChart, + listing: { id: listing.id }, + } + } + ) + + // Assign priorityTypes + for (let i = 0; i < 3; i++) { + unitsToBeCreated[i].priorityType = priorityTypeMobilityAndMobilityWithHearingAndVisual + } + for (let i = 3; i < 14; i++) { + unitsToBeCreated[i].priorityType = priorityTypeMobilityAndHearingWithVisual + } + for (let i = 14; i < 27; i++) { + unitsToBeCreated[i].priorityType = priorityTypeMobilityAndHearing + } + for (let i = 27; i < 46; i++) { + unitsToBeCreated[i].priorityType = priorityMobility + } + + // Assign unit types + for (let i = 0; i < 4; i++) { + unitsToBeCreated[i].unitType = unitTypeOneBdrm + } + for (let i = 4; i < 27; i++) { + unitsToBeCreated[i].unitType = unitTypeTwoBdrm + } + for (let i = 27; i < 46; i++) { + unitsToBeCreated[i].unitType = unitTypeThreeBdrm + } + + await this.unitsRepository.save(unitsToBeCreated) + + return listing } } diff --git a/backend/core/src/seeder/seeds/listings/listing-default-bmr-chart-seed.ts b/backend/core/src/seeder/seeds/listings/listing-default-bmr-chart-seed.ts index 957ec2eefc..f8ea923561 100644 --- a/backend/core/src/seeder/seeds/listings/listing-default-bmr-chart-seed.ts +++ b/backend/core/src/seeder/seeds/listings/listing-default-bmr-chart-seed.ts @@ -1,5 +1,5 @@ import { ListingDefaultSeed } from "./listing-default-seed" -import { getDefaultUnits, getDefaultProperty } from "./shared" +import { getDefaultUnits } from "./shared" import { BaseEntity } from "typeorm" import { defaultAmiChart } from "../ami-charts/default-ami-chart" import { UnitCreateDto } from "../../../units/dto/unit-create.dto" @@ -21,22 +21,22 @@ export class ListingDefaultBmrChartSeed extends ListingDefaultSeed { jurisdiction: alamedaJurisdiction, }) - const property = await this.propertyRepository.save({ - ...getDefaultProperty(), - }) - const bmrUnits = [ { ...defaultUnits[0], bmrProgramChart: true, monthlyIncomeMin: "700", monthlyRent: "350" }, { ...defaultUnits[1], bmrProgramChart: true, monthlyIncomeMin: "800", monthlyRent: "400" }, ] + const newListing = await this.listingRepository.save({ + ...listing, + name: "Test: Default, BMR Chart", + preferences: [], + }) + const unitsToBeCreated: Array> = bmrUnits.map((unit) => { return { ...unit, - property: { - id: property.id, - }, amiChart, + listing: { id: newListing.id }, } }) @@ -45,11 +45,6 @@ export class ListingDefaultBmrChartSeed extends ListingDefaultSeed { await this.unitsRepository.save(unitsToBeCreated) - return await this.listingRepository.save({ - ...listing, - name: "Test: Default, BMR Chart", - preferences: [], - property, - }) + return newListing } } diff --git a/backend/core/src/seeder/seeds/listings/listing-default-missing-ami.ts b/backend/core/src/seeder/seeds/listings/listing-default-missing-ami.ts index f7338ddeb2..b1947ea34b 100644 --- a/backend/core/src/seeder/seeds/listings/listing-default-missing-ami.ts +++ b/backend/core/src/seeder/seeds/listings/listing-default-missing-ami.ts @@ -1,5 +1,4 @@ import { ListingDefaultSeed } from "./listing-default-seed" -import { getDefaultProperty } from "./shared" import { BaseEntity } from "typeorm" import { UnitSeedType } from "./listings" import { CountyCode } from "../../../shared/types/county-code" @@ -108,9 +107,9 @@ export class ListingDefaultMissingAMI extends ListingDefaultSeed { }, ] - const property = await this.propertyRepository.save({ - ...getDefaultProperty(), - unitsAvailable: missingAmiLevelsUnits.length, + const newListing = await this.listingRepository.save({ + ...listing, + name: "Test: Default, Missing Household Levels in AMI", }) const unitsToBeCreated: Array> = missingAmiLevelsUnits.map((unit) => { return { ...unit, - property: { - id: property.id, - }, amiChart, + listing: { id: newListing.id }, } }) @@ -132,10 +129,6 @@ export class ListingDefaultMissingAMI extends ListingDefaultSeed { await this.unitsRepository.save(unitsToBeCreated) - return await this.listingRepository.save({ - ...listing, - property: property, - name: "Test: Default, Missing Household Levels in AMI", - }) + return newListing } } diff --git a/backend/core/src/seeder/seeds/listings/listing-default-multiple-ami-and-percentages.ts b/backend/core/src/seeder/seeds/listings/listing-default-multiple-ami-and-percentages.ts index bc5169cfc6..70c5f50183 100644 --- a/backend/core/src/seeder/seeds/listings/listing-default-multiple-ami-and-percentages.ts +++ b/backend/core/src/seeder/seeds/listings/listing-default-multiple-ami-and-percentages.ts @@ -1,5 +1,4 @@ import { ListingDefaultSeed } from "./listing-default-seed" -import { getDefaultProperty } from "./shared" import { BaseEntity } from "typeorm" import { UnitSeedType } from "./listings" import { CountyCode } from "../../../shared/types/county-code" @@ -94,18 +93,16 @@ export class ListingDefaultMultipleAMIAndPercentages extends ListingDefaultSeed }, ] - const property = await this.propertyRepository.save({ - ...getDefaultProperty(), - unitsAvailable: multipleAMIUnits.length, + const newListing = await this.listingRepository.save({ + ...listing, + name: "Test: Default, Multiple AMI and Percentages", }) const unitsToBeCreated: Array> = multipleAMIUnits.map( (unit) => { return { ...unit, - property: { - id: property.id, - }, + listing: { id: newListing.id }, } } ) @@ -117,10 +114,6 @@ export class ListingDefaultMultipleAMIAndPercentages extends ListingDefaultSeed await this.unitsRepository.save(unitsToBeCreated) - return await this.listingRepository.save({ - ...listing, - property: property, - name: "Test: Default, Multiple AMI and Percentages", - }) + return newListing } } diff --git a/backend/core/src/seeder/seeds/listings/listing-default-multiple-ami.ts b/backend/core/src/seeder/seeds/listings/listing-default-multiple-ami.ts index c3443dbfdd..b621f29174 100644 --- a/backend/core/src/seeder/seeds/listings/listing-default-multiple-ami.ts +++ b/backend/core/src/seeder/seeds/listings/listing-default-multiple-ami.ts @@ -1,5 +1,5 @@ import { ListingDefaultSeed } from "./listing-default-seed" -import { getDefaultUnits, getDefaultProperty } from "./shared" +import { getDefaultUnits } from "./shared" import { BaseEntity } from "typeorm" import { CountyCode } from "../../../shared/types/county-code" import { UnitCreateDto } from "../../../units/dto/unit-create.dto" @@ -22,18 +22,17 @@ export class ListingDefaultMultipleAMI extends ListingDefaultSeed { jurisdiction: alamedaJurisdiction, }) - const property = await this.propertyRepository.save({ - ...getDefaultProperty(), + const newListing = await this.listingRepository.save({ + ...listing, + name: "Test: Default, Multiple AMI", }) const unitsToBeCreated: Array> = getDefaultUnits().map( (unit, index) => { return { ...unit, - property: { - id: property.id, - }, amiChart: index % 2 === 0 ? amiChartOne : amiChartTwo, + listing: { id: newListing.id }, } } ) @@ -43,10 +42,6 @@ export class ListingDefaultMultipleAMI extends ListingDefaultSeed { await this.unitsRepository.save(unitsToBeCreated) - return await this.listingRepository.save({ - ...listing, - property: property, - name: "Test: Default, Multiple AMI", - }) + return newListing } } diff --git a/backend/core/src/seeder/seeds/listings/listing-default-sanjose-seed.ts b/backend/core/src/seeder/seeds/listings/listing-default-sanjose-seed.ts index 213bd33700..9cb920c12c 100644 --- a/backend/core/src/seeder/seeds/listings/listing-default-sanjose-seed.ts +++ b/backend/core/src/seeder/seeds/listings/listing-default-sanjose-seed.ts @@ -5,7 +5,6 @@ import { getDefaultAssets, getDefaultListing, getDefaultListingEvents, - getDefaultProperty, getDefaultUnits, getDisplaceePreference, getLiveWorkPreference, @@ -16,7 +15,6 @@ import { UnitAccessibilityPriorityType } from "../../../unit-accessbility-priori import { UnitType } from "../../../unit-types/entities/unit-type.entity" import { ReservedCommunityType } from "../../../reserved-community-type/entities/reserved-community-type.entity" import { AmiChart } from "../../../ami-charts/entities/ami-chart.entity" -import { Property } from "../../../property/entities/property.entity" import { Unit } from "../../../units/entities/unit.entity" import { User } from "../../../auth/entities/user.entity" import { ApplicationMethod } from "../../../application-methods/entities/application-method.entity" @@ -35,7 +33,6 @@ export class ListingDefaultSanJoseSeed { @InjectRepository(ReservedCommunityType) protected readonly reservedTypeRepository: Repository, @InjectRepository(AmiChart) protected readonly amiChartRepository: Repository, - @InjectRepository(Property) protected readonly propertyRepository: Repository, @InjectRepository(Unit) protected readonly unitsRepository: Repository, @InjectRepository(User) protected readonly userRepository: Repository, @InjectRepository(ApplicationMethod) @@ -58,16 +55,30 @@ export class ListingDefaultSanJoseSeed { jurisdiction: alamedaJurisdiction, }) - const property = await this.propertyRepository.save({ - ...getDefaultProperty(), - }) + const listingCreateDto: Omit< + DeepPartial, + keyof BaseEntity | "urlSlug" | "showWaitlist" + > = { + ...getDefaultListing(), + + name: "Test: Default, Two Preferences (San Jose)", + assets: getDefaultAssets(), + preferences: [ + getLiveWorkPreference(alamedaJurisdiction.name), + { ...getDisplaceePreference(alamedaJurisdiction.name), ordinal: 2 }, + ], + events: getDefaultListingEvents(), + jurisdictionName: "San Jose", + } + + let listing = await this.listingRepository.save(listingCreateDto) const unitsToBeCreated: Array> = getDefaultUnits().map( (unit) => { return { ...unit, - property: { - id: property.id, + listing: { + id: listing.id, }, amiChart, } @@ -80,11 +91,8 @@ export class ListingDefaultSanJoseSeed { unitsToBeCreated[1].unitType = unitTypeTwoBdrm const newUnits = await this.unitsRepository.save(unitsToBeCreated) - const listingCreateDto: Omit< - DeepPartial, - keyof BaseEntity | "urlSlug" | "showWaitlist" - > = { - ...getDefaultListing(), + listing = await this.listingRepository.save({ + ...listing, amiChartOverrides: [ { unit: { id: newUnits[0].id }, @@ -97,17 +105,8 @@ export class ListingDefaultSanJoseSeed { ], }, ], - name: "Test: Default, Two Preferences (San Jose)", - property: property, - assets: getDefaultAssets(), - preferences: [ - getLiveWorkPreference(alamedaJurisdiction.name), - { ...getDisplaceePreference(alamedaJurisdiction.name), ordinal: 2 }, - ], - events: getDefaultListingEvents(), - jurisdictionName: "San Jose", - } + }) - return await this.listingRepository.save(listingCreateDto) + return listing } } diff --git a/backend/core/src/seeder/seeds/listings/listing-default-seed.ts b/backend/core/src/seeder/seeds/listings/listing-default-seed.ts index 580e10a02f..e98d5e7516 100644 --- a/backend/core/src/seeder/seeds/listings/listing-default-seed.ts +++ b/backend/core/src/seeder/seeds/listings/listing-default-seed.ts @@ -5,7 +5,6 @@ import { getDefaultAssets, getDefaultListing, getDefaultListingEvents, - getDefaultProperty, getDefaultUnits, getDisabilityOrMentalIllnessProgram, getDisplaceePreference, @@ -21,7 +20,6 @@ import { UnitAccessibilityPriorityType } from "../../../unit-accessbility-priori import { UnitType } from "../../../unit-types/entities/unit-type.entity" import { ReservedCommunityType } from "../../../reserved-community-type/entities/reserved-community-type.entity" import { AmiChart } from "../../../ami-charts/entities/ami-chart.entity" -import { Property } from "../../../property/entities/property.entity" import { Unit } from "../../../units/entities/unit.entity" import { User } from "../../../auth/entities/user.entity" import { ApplicationMethod } from "../../../application-methods/entities/application-method.entity" @@ -43,7 +41,6 @@ export class ListingDefaultSeed { @InjectRepository(ReservedCommunityType) protected readonly reservedTypeRepository: Repository, @InjectRepository(AmiChart) protected readonly amiChartRepository: Repository, - @InjectRepository(Property) protected readonly propertyRepository: Repository, @InjectRepository(Unit) protected readonly unitsRepository: Repository, @InjectRepository(User) protected readonly userRepository: Repository, @InjectRepository(ApplicationMethod) @@ -71,28 +68,6 @@ export class ListingDefaultSeed { jurisdiction: alamedaJurisdiction, }) - const property = await this.propertyRepository.save({ - ...getDefaultProperty(), - }) - - const unitsToBeCreated: Array> = getDefaultUnits().map( - (unit) => { - return { - ...unit, - property: { - id: property.id, - }, - amiChart, - } - } - ) - - unitsToBeCreated[0].priorityType = priorityTypeMobilityAndHearing - unitsToBeCreated[1].priorityType = priorityTypeMobilityAndHearing - unitsToBeCreated[0].unitType = unitTypeOneBdrm - unitsToBeCreated[1].unitType = unitTypeTwoBdrm - const newUnits = await this.unitsRepository.save(unitsToBeCreated) - const defaultImage = await this.assetsRepository.save(getDefaultAssets()[0]) const listingCreateDto: Omit< @@ -100,20 +75,8 @@ export class ListingDefaultSeed { keyof BaseEntity | "urlSlug" | "showWaitlist" > = { ...getDefaultListing(), - amiChartOverrides: [ - { - unit: { id: newUnits[0].id }, - items: [ - { - percentOfAmi: 80, - householdSize: 1, - income: 777777, - }, - ], - }, - ], + name: "Test: Default, Two Preferences", - property: property, assets: getDefaultAssets(), listingPreferences: [ { @@ -174,6 +137,42 @@ export class ListingDefaultSeed { jurisdiction: alamedaJurisdiction, } - return await this.listingRepository.save(listingCreateDto) + let listing = await this.listingRepository.save(listingCreateDto) + + const unitsToBeCreated: Array> = getDefaultUnits().map( + (unit) => { + return { + ...unit, + listing: { + id: listing.id, + }, + amiChart, + } + } + ) + + unitsToBeCreated[0].priorityType = priorityTypeMobilityAndHearing + unitsToBeCreated[1].priorityType = priorityTypeMobilityAndHearing + unitsToBeCreated[0].unitType = unitTypeOneBdrm + unitsToBeCreated[1].unitType = unitTypeTwoBdrm + const newUnits = await this.unitsRepository.save(unitsToBeCreated) + + listing = await this.listingRepository.save({ + ...listing, + amiChartOverrides: [ + { + unit: { id: newUnits[0].id }, + items: [ + { + percentOfAmi: 80, + householdSize: 1, + income: 777777, + }, + ], + }, + ], + }) + + return listing } } diff --git a/backend/core/src/seeder/seeds/listings/listing-triton-seed.ts b/backend/core/src/seeder/seeds/listings/listing-triton-seed.ts index f0487a6a4e..66af47bc53 100644 --- a/backend/core/src/seeder/seeds/listings/listing-triton-seed.ts +++ b/backend/core/src/seeder/seeds/listings/listing-triton-seed.ts @@ -1,4 +1,4 @@ -import { ListingSeedType, PropertySeedType, UnitSeedType } from "./listings" +import { ListingSeedType, UnitSeedType } from "./listings" import { getDate, getDefaultAssets, getLiveWorkPreference } from "./shared" import { ListingDefaultSeed } from "./listing-default-seed" import { BaseEntity, DeepPartial } from "typeorm" @@ -8,8 +8,15 @@ import { ListingStatus } from "../../../listings/types/listing-status-enum" import { UnitCreateDto } from "../../../units/dto/unit-create.dto" import { Listing } from "../../../listings/entities/listing.entity" import { ListingAvailability } from "../../../listings/types/listing-availability-enum" +import { classToClass } from "class-transformer" -const tritonProperty: PropertySeedType = { +const tritonListing: ListingSeedType = { + jurisdictionName: "Alameda", + digitalApplication: false, + commonDigitalApplication: false, + paperApplication: false, + referralOpportunity: false, + countyCode: CountyCode.alameda, accessibility: "Accessibility features in common areas like lobby – wheelchair ramps, wheelchair accessible bathrooms and elevators.", amenities: "Gym, Clubhouse, Business Lounge, View Lounge, Pool, Spa", @@ -31,15 +38,6 @@ const tritonProperty: PropertySeedType = { smokingPolicy: "Non-Smoking", unitAmenities: "Washer and dryer, AC and Heater, Gas Stove", yearBuilt: 2021, -} - -const tritonListing: ListingSeedType = { - jurisdictionName: "Alameda", - digitalApplication: false, - commonDigitalApplication: false, - paperApplication: false, - referralOpportunity: false, - countyCode: CountyCode.alameda, applicationDropOffAddress: null, applicationDropOffAddressOfficeHours: null, applicationMailingAddress: null, @@ -203,17 +201,32 @@ export class ListingTritonSeed extends ListingDefaultSeed { }, ] - const property = await this.propertyRepository.save({ - ...tritonProperty, - unitsAvailable: tritonUnits.length, - }) + const listingCreateDto: Omit< + DeepPartial, + keyof BaseEntity | "urlSlug" | "showWaitlist" + > = { + ...classToClass(tritonListing), + name: "Test: Triton 2", + assets: getDefaultAssets(), + listingPreferences: [ + { + preference: await this.preferencesRepository.findOneOrFail({ + title: getLiveWorkPreference(alamedaJurisdiction.name).title, + }), + ordinal: 2, + }, + ], + events: [], + } + + const listing = await this.listingRepository.save(listingCreateDto) const unitsToBeCreated: Array> = tritonUnits.map( (unit) => { return { ...unit, - property: { - id: property.id, + listing: { + id: listing.id, }, amiChart, } @@ -228,26 +241,7 @@ export class ListingTritonSeed extends ListingDefaultSeed { await this.unitsRepository.save(unitsToBeCreated) - const listingCreateDto: Omit< - DeepPartial, - keyof BaseEntity | "urlSlug" | "showWaitlist" - > = { - ...tritonListing, - name: "Test: Triton 2", - property: property, - assets: getDefaultAssets(), - listingPreferences: [ - { - preference: await this.preferencesRepository.findOneOrFail({ - title: getLiveWorkPreference(alamedaJurisdiction.name).title, - }), - ordinal: 2, - }, - ], - events: [], - } - - return await this.listingRepository.save(listingCreateDto) + return listing } } @@ -264,12 +258,6 @@ export class ListingTritonSeedDetroit extends ListingDefaultSeed { jurisdiction: detroitJurisdiction, }) - const property = await this.propertyRepository.findOneOrFail({ - developer: "Thompson Dorfman, LLC", - neighborhood: "Foster City", - smokingPolicy: "Non-Smoking", - }) - const tritonUnits: Array = [ { amiChart: amiChart, @@ -358,12 +346,25 @@ export class ListingTritonSeedDetroit extends ListingDefaultSeed { }, ] + const listingCreateDto: Omit< + DeepPartial, + keyof BaseEntity | "urlSlug" | "showWaitlist" + > = { + ...classToClass(tritonListing), + name: "Test: Triton 1", + applicationOpenDate: getDate(-5), + assets: getDefaultAssets(), + events: [], + } + + const listing = await this.listingRepository.save(listingCreateDto) + const unitsToBeCreated: Array> = tritonUnits.map( (unit) => { return { ...unit, - property: { - id: property.id, + listing: { + id: listing.id, }, amiChart, } @@ -378,18 +379,6 @@ export class ListingTritonSeedDetroit extends ListingDefaultSeed { await this.unitsRepository.save(unitsToBeCreated) - const listingCreateDto: Omit< - DeepPartial, - keyof BaseEntity | "urlSlug" | "showWaitlist" - > = { - ...tritonListing, - name: "Test: Triton 1", - property: property, - applicationOpenDate: getDate(-5), - assets: getDefaultAssets(), - events: [], - } - - return await this.listingRepository.save(listingCreateDto) + return listing } } diff --git a/backend/core/src/seeder/seeds/listings/listings.ts b/backend/core/src/seeder/seeds/listings/listings.ts index a120526299..e49d93cf96 100644 --- a/backend/core/src/seeder/seeds/listings/listings.ts +++ b/backend/core/src/seeder/seeds/listings/listings.ts @@ -1,5 +1,4 @@ import { BaseEntity } from "typeorm" -import { PropertyCreateDto } from "../../../property/dto/property.dto" import { UnitCreateDto } from "../../../units/dto/unit-create.dto" import { ApplicationMethodCreateDto } from "../../../application-methods/dto/application-method.dto" import { ListingPublishedCreateDto } from "../../../listings/dto/listing-published-create.dto" @@ -10,24 +9,13 @@ import { AmiChartCreateDto } from "../../../ami-charts/dto/ami-chart.dto" import { ListingEventCreateDto } from "../../../listings/dto/listing-event.dto" import { UserCreateDto } from "../../../auth/dto/user-create.dto" -export type PropertySeedType = Omit< - PropertyCreateDto, - | "propertyGroups" - | "listings" - | "units" - | "unitsSummarized" - | "householdSizeMin" - | "householdSizeMax" -> - -export type UnitSeedType = Omit +export type UnitSeedType = Omit export type ApplicationMethodSeedType = ApplicationMethodCreateDto export type ListingSeedType = Omit< ListingPublishedCreateDto, | keyof BaseEntity - | "property" | "urlSlug" | "applicationMethods" | "events" @@ -36,21 +24,6 @@ export type ListingSeedType = Omit< | "leasingAgents" | "showWaitlist" | "units" - | "propertyGroups" - | "accessibility" - | "amenities" - | "buildingAddress" - | "buildingTotalUnits" - | "developer" - | "householdSizeMax" - | "householdSizeMin" - | "neighborhood" - | "petPolicy" - | "smokingPolicy" - | "unitsAvailable" - | "unitAmenities" - | "servicesOffered" - | "yearBuilt" | "unitsSummary" | "unitsSummarized" | "amiChartOverrides" @@ -69,7 +42,6 @@ export interface ListingSeed { amiChart: AmiChartCreateDto units: Array applicationMethods: Array - property: PropertySeedType preferences: Array listingEvents: Array assets: Array diff --git a/backend/core/src/seeder/seeds/listings/shared.ts b/backend/core/src/seeder/seeds/listings/shared.ts index c7267193e2..95f19f5471 100644 --- a/backend/core/src/seeder/seeds/listings/shared.ts +++ b/backend/core/src/seeder/seeds/listings/shared.ts @@ -4,7 +4,6 @@ import { ListingSeedType, PreferenceSeedType, ProgramSeedType, - PropertySeedType, UnitSeedType, } from "./listings" import { defaultAmiChart } from "../ami-charts/default-ami-chart" @@ -70,33 +69,6 @@ export const defaultAssets: Array = [ "https://regional-dahlia-staging.s3-us-west-1.amazonaws.com/listings/triton/thetriton.png", }, ] -// Properties -export function getDefaultProperty() { - return JSON.parse(JSON.stringify(defaultProperty)) -} - -export const defaultProperty: PropertySeedType = { - accessibility: "Custom accessibility text", - amenities: "Custom property amenities text", - buildingAddress: { - city: "San Francisco", - state: "CA", - street: "548 Market Street", - street2: "Suite #59930", - zipCode: "94104", - latitude: 37.789673, - longitude: -122.40151, - }, - buildingTotalUnits: 100, - developer: "Developer", - neighborhood: "Custom neighborhood text", - petPolicy: "Custom pet text", - servicesOffered: "Custom services offered text", - smokingPolicy: "Custom smoking text", - unitAmenities: "Custom unit amenities text", - unitsAvailable: 2, - yearBuilt: 2021, -} // Unit Sets export function getDefaultUnits() { @@ -171,6 +143,26 @@ export function getDefaultListing() { export const defaultListing: ListingSeedType = { jurisdictionName: "Alameda", countyCode: CountyCode.alameda, + accessibility: "Custom accessibility text", + amenities: "Custom property amenities text", + buildingAddress: { + city: "San Francisco", + state: "CA", + street: "548 Market Street", + street2: "Suite #59930", + zipCode: "94104", + latitude: 37.789673, + longitude: -122.40151, + }, + buildingTotalUnits: 100, + developer: "Developer", + neighborhood: "Custom neighborhood text", + petPolicy: "Custom pet text", + servicesOffered: "Custom services offered text", + smokingPolicy: "Custom smoking text", + unitAmenities: "Custom unit amenities text", + unitsAvailable: 2, + yearBuilt: 2021, applicationDropOffAddress: null, applicationDropOffAddressOfficeHours: null, applicationMailingAddress: null, diff --git a/backend/core/src/shared/url-helper.ts b/backend/core/src/shared/url-helper.ts index 521dbd2fde..23f0842aa3 100644 --- a/backend/core/src/shared/url-helper.ts +++ b/backend/core/src/shared/url-helper.ts @@ -23,8 +23,8 @@ export const formatUrlSlug = (input: string): string => { export const listingUrlSlug = (listing: Listing): string => { const { name } = listing - if (listing?.property?.buildingAddress) { - const { city, street, state } = listing?.property?.buildingAddress + if (listing?.buildingAddress) { + const { city, street, state } = listing?.buildingAddress return formatUrlSlug([name, street, city, state].join(" ")) } else { return formatUrlSlug(name) diff --git a/backend/core/src/translations/services/translations.service.spec.ts b/backend/core/src/translations/services/translations.service.spec.ts index 178f687c0a..7ea75b4d86 100644 --- a/backend/core/src/translations/services/translations.service.spec.ts +++ b/backend/core/src/translations/services/translations.service.spec.ts @@ -35,6 +35,7 @@ const getMockListing = () => { applicationMailingAddressType: undefined, applications: [], assets: [], + buildingAddress: undefined, buildingSelectionCriteria: undefined, buildingSelectionCriteriaFile: undefined, commonDigitalApplication: false, @@ -67,7 +68,6 @@ const getMockListing = () => { paperApplication: false, postmarkedApplicationsReceivedByDate: undefined, programRules: undefined, - property: undefined, referralOpportunity: false, rentalAssistance: undefined, rentalHistory: undefined, @@ -80,6 +80,7 @@ const getMockListing = () => { reviewOrderType: undefined, specialNotes: undefined, status: undefined, + units: [], unitsSummarized: undefined, unitsSummary: [], updatedAt: new Date(), diff --git a/backend/core/src/translations/services/translations.service.ts b/backend/core/src/translations/services/translations.service.ts index d194bf73bd..103361e9ee 100644 --- a/backend/core/src/translations/services/translations.service.ts +++ b/backend/core/src/translations/services/translations.service.ts @@ -81,13 +81,13 @@ export class TranslationsService extends AbstractServiceFactory< "requiredDocuments", "specialNotes", "whatToExpect", - "property.accessibility", - "property.amenities", - "property.neighborhood", - "property.petPolicy", - "property.servicesOffered", - "property.smokingPolicy", - "property.unitAmenities", + "accessibility", + "amenities", + "neighborhood", + "petPolicy", + "servicesOffered", + "smokingPolicy", + "unitAmenities", ] for (let i = 0; i < listing.events.length; i++) { diff --git a/backend/core/src/units/dto/unit.dto.ts b/backend/core/src/units/dto/unit.dto.ts index accb53aef2..6c7ff1cab3 100644 --- a/backend/core/src/units/dto/unit.dto.ts +++ b/backend/core/src/units/dto/unit.dto.ts @@ -10,7 +10,7 @@ import { UnitAccessibilityPriorityTypeDto } from "../../unit-accessbility-priori import { UnitAmiChartOverrideDto } from "./unit-ami-chart-override.dto" export class UnitDto extends OmitType(Unit, [ - "property", + "listing", "amiChart", "amiChartId", "unitType", @@ -20,7 +20,7 @@ export class UnitDto extends OmitType(Unit, [ ] as const) { @Exclude() @ApiHideProperty() - property + listing @Expose() @IsOptional({ groups: [ValidationsGroupsEnum.default] }) diff --git a/backend/core/src/units/entities/unit.entity.ts b/backend/core/src/units/entities/unit.entity.ts index b4e6a61956..4fd335395e 100644 --- a/backend/core/src/units/entities/unit.entity.ts +++ b/backend/core/src/units/entities/unit.entity.ts @@ -20,13 +20,13 @@ import { ValidateNested, } from "class-validator" import { Expose, Type } from "class-transformer" -import { Property } from "../../property/entities/property.entity" import { AmiChart } from "../../ami-charts/entities/ami-chart.entity" import { ValidationsGroupsEnum } from "../../shared/types/validations-groups-enum" import { UnitType } from "../../unit-types/entities/unit-type.entity" import { UnitRentType } from "../../unit-rent-types/entities/unit-rent-type.entity" import { UnitAccessibilityPriorityType } from "../../unit-accessbility-priority-types/entities/unit-accessibility-priority-type.entity" import { UnitAmiChartOverride } from "../../units/entities/unit-ami-chart-override.entity" +import Listing from "../../listings/entities/listing.entity" @Entity({ name: "units" }) class Unit { @@ -133,11 +133,11 @@ class Unit { @IsString({ groups: [ValidationsGroupsEnum.default] }) monthlyRentAsPercentOfIncome?: string | null - @ManyToOne(() => Property, (property) => property.units, { + @ManyToOne(() => Listing, (listing) => listing.units, { onDelete: "CASCADE", onUpdate: "CASCADE", }) - property: Property + listing: Listing @Column({ type: "boolean", nullable: true }) @Expose() diff --git a/backend/core/test/properties/properties.e2e-spec.ts b/backend/core/test/properties/properties.e2e-spec.ts deleted file mode 100644 index 16890621a2..0000000000 --- a/backend/core/test/properties/properties.e2e-spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Test } from "@nestjs/testing" -import { INestApplication } from "@nestjs/common" -import { TypeOrmModule } from "@nestjs/typeorm" -// Use require because of the CommonJS/AMD style export. -// See https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require -import dbOptions from "../../ormconfig.test" -import supertest from "supertest" -import { applicationSetup } from "../../src/app.module" -import { AuthModule } from "../../src/auth/auth.module" -import { getUserAccessToken } from "../utils/get-user-access-token" -import { setAuthorization } from "../utils/set-authorization-helper" -import { PropertiesModule } from "../../src/property/properties.module" -import { EmailService } from "../../src/email/email.service" - -// Cypress brings in Chai types for the global expect, but we want to use jest -// expect here so we need to re-declare it. -// see: https://github.com/cypress-io/cypress/issues/1319#issuecomment-593500345 -declare const expect: jest.Expect -jest.setTimeout(30000) - -describe("Properties", () => { - let app: INestApplication - let adminAccesstoken: string - beforeAll(async () => { - /* eslint-disable @typescript-eslint/no-empty-function */ - const testEmailService = { confirmation: async () => {} } - /* eslint-enable @typescript-eslint/no-empty-function */ - const moduleRef = await Test.createTestingModule({ - imports: [TypeOrmModule.forRoot(dbOptions), AuthModule, PropertiesModule], - }) - .overrideProvider(EmailService) - .useValue(testEmailService) - .compile() - app = moduleRef.createNestApplication() - app = applicationSetup(app) - await app.init() - adminAccesstoken = await getUserAccessToken(app, "admin@example.com", "abcdef") - }) - - it(`/GET `, async () => { - const res = await supertest(app.getHttpServer()) - .get(`/properties`) - .set(...setAuthorization(adminAccesstoken)) - .expect(200) - expect(Array.isArray(res.body)).toBe(true) - }) - - afterEach(() => { - jest.clearAllMocks() - }) - - afterAll(async () => { - await app.close() - }) -}) diff --git a/backend/core/types/src/backend-swagger.ts b/backend/core/types/src/backend-swagger.ts index cbe22fdb6c..125ab99237 100644 --- a/backend/core/types/src/backend-swagger.ts +++ b/backend/core/types/src/backend-swagger.ts @@ -1658,214 +1658,6 @@ export class ProgramsService { } } -export class PropertiesService { - /** - * List properties - */ - list(options: IRequestOptions = {}): Promise { - return new Promise((resolve, reject) => { - let url = basePath + "/properties" - - const configs: IRequestConfig = getConfigs("get", "application/json", url, options) - - let data = null - - configs.data = data - axios(configs, resolve, reject) - }) - } - /** - * Create property - */ - create( - params: { - /** requestBody */ - body?: PropertyCreate - } = {} as any, - options: IRequestOptions = {} - ): Promise { - return new Promise((resolve, reject) => { - let url = basePath + "/properties" - - const configs: IRequestConfig = getConfigs("post", "application/json", url, options) - - let data = params.body - - configs.data = data - axios(configs, resolve, reject) - }) - } - /** - * Update property - */ - update( - params: { - /** requestBody */ - body?: PropertyUpdate - } = {} as any, - options: IRequestOptions = {} - ): Promise { - return new Promise((resolve, reject) => { - let url = basePath + "/properties/{propertyId}" - - const configs: IRequestConfig = getConfigs("put", "application/json", url, options) - - let data = params.body - - configs.data = data - axios(configs, resolve, reject) - }) - } - /** - * Get property by id - */ - retrieve( - params: { - /** */ - propertyId: string - } = {} as any, - options: IRequestOptions = {} - ): Promise { - return new Promise((resolve, reject) => { - let url = basePath + "/properties/{propertyId}" - url = url.replace("{propertyId}", params["propertyId"] + "") - - const configs: IRequestConfig = getConfigs("get", "application/json", url, options) - - let data = null - - configs.data = data - axios(configs, resolve, reject) - }) - } - /** - * Delete property by id - */ - delete( - params: { - /** */ - propertyId: string - } = {} as any, - options: IRequestOptions = {} - ): Promise { - return new Promise((resolve, reject) => { - let url = basePath + "/properties/{propertyId}" - url = url.replace("{propertyId}", params["propertyId"] + "") - - const configs: IRequestConfig = getConfigs("delete", "application/json", url, options) - - let data = null - - configs.data = data - axios(configs, resolve, reject) - }) - } -} - -export class PropertyGroupsService { - /** - * List propertyGroups - */ - list(options: IRequestOptions = {}): Promise { - return new Promise((resolve, reject) => { - let url = basePath + "/propertyGroups" - - const configs: IRequestConfig = getConfigs("get", "application/json", url, options) - - let data = null - - configs.data = data - axios(configs, resolve, reject) - }) - } - /** - * Create propertyGroup - */ - create( - params: { - /** requestBody */ - body?: PropertyGroupCreate - } = {} as any, - options: IRequestOptions = {} - ): Promise { - return new Promise((resolve, reject) => { - let url = basePath + "/propertyGroups" - - const configs: IRequestConfig = getConfigs("post", "application/json", url, options) - - let data = params.body - - configs.data = data - axios(configs, resolve, reject) - }) - } - /** - * Update propertyGroup - */ - update( - params: { - /** requestBody */ - body?: PropertyGroupUpdate - } = {} as any, - options: IRequestOptions = {} - ): Promise { - return new Promise((resolve, reject) => { - let url = basePath + "/propertyGroups/{propertyGroupId}" - - const configs: IRequestConfig = getConfigs("put", "application/json", url, options) - - let data = params.body - - configs.data = data - axios(configs, resolve, reject) - }) - } - /** - * Get propertyGroup by id - */ - retrieve( - params: { - /** */ - propertyGroupId: string - } = {} as any, - options: IRequestOptions = {} - ): Promise { - return new Promise((resolve, reject) => { - let url = basePath + "/propertyGroups/{propertyGroupId}" - url = url.replace("{propertyGroupId}", params["propertyGroupId"] + "") - - const configs: IRequestConfig = getConfigs("get", "application/json", url, options) - - let data = null - - configs.data = data - axios(configs, resolve, reject) - }) - } - /** - * Delete propertyGroup by id - */ - delete( - params: { - /** */ - propertyGroupId: string - } = {} as any, - options: IRequestOptions = {} - ): Promise { - return new Promise((resolve, reject) => { - let url = basePath + "/propertyGroups/{propertyGroupId}" - url = url.replace("{propertyGroupId}", params["propertyGroupId"] + "") - - const configs: IRequestConfig = getConfigs("delete", "application/json", url, options) - - let data = null - - configs.data = data - axios(configs, resolve, reject) - }) - } -} - export class ReservedCommunityTypesService { /** * List reservedCommunityTypes @@ -5566,82 +5358,82 @@ export interface ListingCreate { units: UnitCreate[] /** */ - accessibility?: string + buildingAddress?: CombinedBuildingAddressTypes /** */ - amenities?: string + jurisdiction: Id /** */ - buildingAddress?: CombinedBuildingAddressTypes + reservedCommunityType?: Id /** */ - buildingTotalUnits?: number + result?: CombinedResultTypes /** */ - developer?: string + unitsSummary?: UnitsSummaryCreate[] /** */ - householdSizeMax?: number + listingPreferences: ListingPreferenceUpdate[] /** */ - householdSizeMin?: number + listingPrograms?: ListingProgramUpdate[] /** */ - neighborhood?: string + additionalApplicationSubmissionNotes?: string /** */ - petPolicy?: string + digitalApplication?: boolean /** */ - smokingPolicy?: string + commonDigitalApplication?: boolean /** */ - unitsAvailable?: number + paperApplication?: boolean /** */ - unitAmenities?: string + referralOpportunity?: boolean /** */ - servicesOffered?: string + assets: AssetCreate[] /** */ - yearBuilt?: number + accessibility?: string /** */ - jurisdiction: Id + amenities?: string /** */ - reservedCommunityType?: Id + buildingTotalUnits?: number /** */ - result?: CombinedResultTypes + developer?: string /** */ - unitsSummary?: UnitsSummaryCreate[] + householdSizeMax?: number /** */ - listingPreferences: ListingPreferenceUpdate[] + householdSizeMin?: number /** */ - listingPrograms?: ListingProgramUpdate[] + neighborhood?: string /** */ - additionalApplicationSubmissionNotes?: string + petPolicy?: string /** */ - digitalApplication?: boolean + smokingPolicy?: string /** */ - commonDigitalApplication?: boolean + unitsAvailable?: number /** */ - paperApplication?: boolean + unitAmenities?: string /** */ - referralOpportunity?: boolean + servicesOffered?: string /** */ - assets: AssetCreate[] + yearBuilt?: number /** */ applicationDueDate?: Date @@ -5990,82 +5782,82 @@ export interface ListingUpdate { units: UnitUpdate[] /** */ - accessibility?: string + buildingAddress?: CombinedBuildingAddressTypes /** */ - amenities?: string + jurisdiction: Id /** */ - buildingAddress?: CombinedBuildingAddressTypes + reservedCommunityType?: Id /** */ - buildingTotalUnits?: number + result?: AssetUpdate /** */ - developer?: string + unitsSummary?: UnitsSummaryUpdate[] /** */ - householdSizeMax?: number + listingPreferences: ListingPreferenceUpdate[] /** */ - householdSizeMin?: number + listingPrograms?: ListingProgramUpdate[] /** */ - neighborhood?: string + additionalApplicationSubmissionNotes?: string /** */ - petPolicy?: string + digitalApplication?: boolean /** */ - smokingPolicy?: string + commonDigitalApplication?: boolean /** */ - unitsAvailable?: number + paperApplication?: boolean /** */ - unitAmenities?: string + referralOpportunity?: boolean /** */ - servicesOffered?: string + assets: AssetCreate[] /** */ - yearBuilt?: number + accessibility?: string /** */ - jurisdiction: Id + amenities?: string /** */ - reservedCommunityType?: Id + buildingTotalUnits?: number /** */ - result?: AssetUpdate + developer?: string /** */ - unitsSummary?: UnitsSummaryUpdate[] + householdSizeMax?: number /** */ - listingPreferences: ListingPreferenceUpdate[] + householdSizeMin?: number /** */ - listingPrograms?: ListingProgramUpdate[] + neighborhood?: string /** */ - additionalApplicationSubmissionNotes?: string + petPolicy?: string /** */ - digitalApplication?: boolean + smokingPolicy?: string /** */ - commonDigitalApplication?: boolean + unitsAvailable?: number /** */ - paperApplication?: boolean + unitAmenities?: string /** */ - referralOpportunity?: boolean + servicesOffered?: string /** */ - assets: AssetCreate[] + yearBuilt?: number /** */ applicationDueDate?: Date @@ -6269,201 +6061,6 @@ export interface ProgramUpdate { id: string } -export interface Property { - /** */ - units: Unit[] - - /** */ - buildingAddress: Address - - /** */ - id: string - - /** */ - createdAt: Date - - /** */ - updatedAt: Date - - /** */ - accessibility?: string - - /** */ - amenities?: string - - /** */ - buildingTotalUnits?: number - - /** */ - developer?: string - - /** */ - householdSizeMax?: number - - /** */ - householdSizeMin?: number - - /** */ - neighborhood?: string - - /** */ - petPolicy?: string - - /** */ - smokingPolicy?: string - - /** */ - unitsAvailable?: number - - /** */ - unitAmenities?: string - - /** */ - servicesOffered?: string - - /** */ - yearBuilt?: number -} - -export interface PropertyCreate { - /** */ - buildingAddress: AddressUpdate - - /** */ - units: UnitCreate[] - - /** */ - accessibility?: string - - /** */ - amenities?: string - - /** */ - buildingTotalUnits?: number - - /** */ - developer?: string - - /** */ - householdSizeMax?: number - - /** */ - householdSizeMin?: number - - /** */ - neighborhood?: string - - /** */ - petPolicy?: string - - /** */ - smokingPolicy?: string - - /** */ - unitsAvailable?: number - - /** */ - unitAmenities?: string - - /** */ - servicesOffered?: string - - /** */ - yearBuilt?: number -} - -export interface PropertyUpdate { - /** */ - id?: string - - /** */ - createdAt?: Date - - /** */ - updatedAt?: Date - - /** */ - buildingAddress: AddressUpdate - - /** */ - units: UnitUpdate[] - - /** */ - accessibility?: string - - /** */ - amenities?: string - - /** */ - buildingTotalUnits?: number - - /** */ - developer?: string - - /** */ - householdSizeMax?: number - - /** */ - householdSizeMin?: number - - /** */ - neighborhood?: string - - /** */ - petPolicy?: string - - /** */ - smokingPolicy?: string - - /** */ - unitsAvailable?: number - - /** */ - unitAmenities?: string - - /** */ - servicesOffered?: string - - /** */ - yearBuilt?: number -} - -export interface PropertyGroup { - /** */ - properties: Id[] - - /** */ - id: string - - /** */ - createdAt: Date - - /** */ - updatedAt: Date - - /** */ - name: string -} - -export interface PropertyGroupCreate { - /** */ - name: string - - /** */ - properties: Id[] -} - -export interface PropertyGroupUpdate { - /** */ - name: string - - /** */ - properties: Id[] - - /** */ - id: string -} - export interface ReservedCommunityTypeCreate { /** */ jurisdiction: Id From 5bf20ee1ec9491e4fdf676d93ed260c2f17b736f Mon Sep 17 00:00:00 2001 From: "github.context.workflow" Date: Wed, 29 Jun 2022 00:03:11 +0000 Subject: [PATCH 023/598] chore(release): version - @bloom-housing/backend-core@5.0.1-alpha.3 - @bloom-housing/shared-helpers@5.0.1-alpha.9 - @bloom-housing/partners@5.0.1-alpha.9 - @bloom-housing/public@5.0.1-alpha.9 --- backend/core/CHANGELOG.md | 8 ++++++++ backend/core/package.json | 2 +- shared-helpers/CHANGELOG.md | 8 ++++++++ shared-helpers/package.json | 4 ++-- sites/partners/CHANGELOG.md | 8 ++++++++ sites/partners/package.json | 6 +++--- sites/public/CHANGELOG.md | 8 ++++++++ sites/public/package.json | 6 +++--- 8 files changed, 41 insertions(+), 9 deletions(-) diff --git a/backend/core/CHANGELOG.md b/backend/core/CHANGELOG.md index 1c38c1f690..78133f4c3b 100644 --- a/backend/core/CHANGELOG.md +++ b/backend/core/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.3](https://github.com/bloom-housing/bloom/compare/@bloom-housing/backend-core@5.0.1-alpha.2...@bloom-housing/backend-core@5.0.1-alpha.3) (2022-06-29) + +**Note:** Version bump only for package @bloom-housing/backend-core + + + + + ## [5.0.1-alpha.2](https://github.com/bloom-housing/bloom/compare/@bloom-housing/backend-core@5.0.1-alpha.1...@bloom-housing/backend-core@5.0.1-alpha.2) (2022-06-22) diff --git a/backend/core/package.json b/backend/core/package.json index 0714a5d9d8..205187842f 100644 --- a/backend/core/package.json +++ b/backend/core/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/backend-core", - "version": "5.0.1-alpha.2", + "version": "5.0.1-alpha.3", "description": "Listings service reference implementation for the Bloom affordable housing system", "author": "Sean Albert ", "private": false, diff --git a/shared-helpers/CHANGELOG.md b/shared-helpers/CHANGELOG.md index e4b188d2fd..cde1d83225 100644 --- a/shared-helpers/CHANGELOG.md +++ b/shared-helpers/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.9](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.8...@bloom-housing/shared-helpers@5.0.1-alpha.9) (2022-06-29) + +**Note:** Version bump only for package @bloom-housing/shared-helpers + + + + + ## [5.0.1-alpha.8](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.7...@bloom-housing/shared-helpers@5.0.1-alpha.8) (2022-06-28) diff --git a/shared-helpers/package.json b/shared-helpers/package.json index 9a42321d0f..17919bf2c5 100644 --- a/shared-helpers/package.json +++ b/shared-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/shared-helpers", - "version": "5.0.1-alpha.8", + "version": "5.0.1-alpha.9", "description": "Shared helpers for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared-helpers", "main": "index.js", @@ -16,7 +16,7 @@ "prettier": "prettier --write \"**/*.ts\"" }, "dependencies": { - "@bloom-housing/backend-core": "^5.0.1-alpha.2", + "@bloom-housing/backend-core": "^5.0.1-alpha.3", "@bloom-housing/ui-components": "^5.0.1-alpha.5" }, "devDependencies": { diff --git a/sites/partners/CHANGELOG.md b/sites/partners/CHANGELOG.md index febf508ca9..a15b4d647d 100644 --- a/sites/partners/CHANGELOG.md +++ b/sites/partners/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.9](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.8...@bloom-housing/partners@5.0.1-alpha.9) (2022-06-29) + +**Note:** Version bump only for package @bloom-housing/partners + + + + + ## [5.0.1-alpha.8](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.7...@bloom-housing/partners@5.0.1-alpha.8) (2022-06-28) diff --git a/sites/partners/package.json b/sites/partners/package.json index c74fe2d9ec..a5f4d6d769 100644 --- a/sites/partners/package.json +++ b/sites/partners/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/partners", - "version": "5.0.1-alpha.8", + "version": "5.0.1-alpha.9", "author": "Sean Albert ", "description": "Partners app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -26,8 +26,8 @@ "dev:server-wait-cypress": "wait-on \"http-get://localhost:${PORT:-3100}/listings\" --httpTimeout 60000 --tcpTimeout 1500 -v --interval 15000 && yarn build && yarn start" }, "dependencies": { - "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.8", + "@bloom-housing/backend-core": "^5.0.1-alpha.3", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.9", "@bloom-housing/ui-components": "^5.0.1-alpha.5", "@mapbox/mapbox-sdk": "^0.13.0", "ag-grid-community": "^26.0.0", diff --git a/sites/public/CHANGELOG.md b/sites/public/CHANGELOG.md index 53e9460848..13b397b4eb 100644 --- a/sites/public/CHANGELOG.md +++ b/sites/public/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.9](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.8...@bloom-housing/public@5.0.1-alpha.9) (2022-06-29) + +**Note:** Version bump only for package @bloom-housing/public + + + + + ## [5.0.1-alpha.8](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.7...@bloom-housing/public@5.0.1-alpha.8) (2022-06-28) diff --git a/sites/public/package.json b/sites/public/package.json index d8542f5d70..66a8491beb 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/public", - "version": "5.0.1-alpha.8", + "version": "5.0.1-alpha.9", "author": "Sean Albert ", "description": "Public web app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -24,8 +24,8 @@ "dev:all": "concurrently \"yarn dev:listings\" \"yarn dev:server-wait\"" }, "dependencies": { - "@bloom-housing/backend-core": "^5.0.1-alpha.2", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.8", + "@bloom-housing/backend-core": "^5.0.1-alpha.3", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.9", "@bloom-housing/ui-components": "^5.0.1-alpha.5", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", From 5fcaf58acc8cc083c738f53a23beba4e567b2994 Mon Sep 17 00:00:00 2001 From: dominikx96 Date: Wed, 29 Jun 2022 02:22:20 +0200 Subject: [PATCH 024/598] fix: hide apply section if there is no data (#2846) * fix: hide apply section if there is no data * refactor: add the GetApplication component test * Fix code style issues with Prettier Co-authored-by: Lint Action --- .../page_components/GetApplication.test.tsx | 5 +++++ .../GetApplication.stories.tsx | 21 +++++++++++++++++++ .../listing_sidebar/GetApplication.tsx | 10 +++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/ui-components/__tests__/page_components/GetApplication.test.tsx b/ui-components/__tests__/page_components/GetApplication.test.tsx index 91d02b69e2..1d2bd17458 100644 --- a/ui-components/__tests__/page_components/GetApplication.test.tsx +++ b/ui-components/__tests__/page_components/GetApplication.test.tsx @@ -4,6 +4,7 @@ import { AllFields, Preview, OpenInFuture, + WithoutPaperAppFiles, } from "../../src/page_components/listing/listing_sidebar/GetApplication.stories" afterEach(cleanup) @@ -21,6 +22,10 @@ describe("", () => { expect(getByText("Pick Up Address Street", { exact: false })).toBeTruthy() expect(getByText("Office Hours")).toBeTruthy() }) + it("do not render section when there is no paper application files and paper method is true", () => { + const { queryByTestId } = render() + expect(queryByTestId("get-application-section")).toBeNull() + }) it("disables buttons in preview state", () => { const { getByText } = render() expect(getByText("Apply Online").closest("button")?.disabled).toBe(true) diff --git a/ui-components/src/page_components/listing/listing_sidebar/GetApplication.stories.tsx b/ui-components/src/page_components/listing/listing_sidebar/GetApplication.stories.tsx index c9b367ff02..9ed48fdcad 100644 --- a/ui-components/src/page_components/listing/listing_sidebar/GetApplication.stories.tsx +++ b/ui-components/src/page_components/listing/listing_sidebar/GetApplication.stories.tsx @@ -31,6 +31,27 @@ export const AllFields = () => { ) } +export const WithoutPaperAppFiles = () => { + return ( + + ) +} + export const Preview = () => { return ( { + const showSection = + props.onlineApplicationURL || + (props.applicationsOpen && props.paperMethod && !!props.paperApplications?.length) const [showDownload, setShowDownload] = useState(false) const toggleDownload = () => setShowDownload(!showDownload) + if (!showSection) return null + return ( -
+

{t("listings.apply.howToApply")}

{!props.applicationsOpen && (

@@ -67,7 +72,8 @@ const GetApplication = (props: ApplicationsProps) => { )} )} - {props.applicationsOpen && props.paperMethod && ( + + {props.applicationsOpen && props.paperMethod && !!props.paperApplications?.length && ( <> {props.onlineApplicationURL && }

{t("listings.apply.getAPaperApplication")}
From 9afb94b8b12488154279d57078b7bf3875c479be Mon Sep 17 00:00:00 2001 From: "github.context.workflow" Date: Wed, 29 Jun 2022 00:22:49 +0000 Subject: [PATCH 025/598] chore(release): version - @bloom-housing/shared-helpers@5.0.1-alpha.10 - @bloom-housing/partners@5.0.1-alpha.10 - @bloom-housing/public@5.0.1-alpha.10 - @bloom-housing/ui-components@5.0.1-alpha.6 --- shared-helpers/CHANGELOG.md | 8 ++++++++ shared-helpers/package.json | 4 ++-- sites/partners/CHANGELOG.md | 8 ++++++++ sites/partners/package.json | 6 +++--- sites/public/CHANGELOG.md | 8 ++++++++ sites/public/package.json | 6 +++--- ui-components/CHANGELOG.md | 11 +++++++++++ ui-components/package.json | 2 +- 8 files changed, 44 insertions(+), 9 deletions(-) diff --git a/shared-helpers/CHANGELOG.md b/shared-helpers/CHANGELOG.md index cde1d83225..fa3d4b92a4 100644 --- a/shared-helpers/CHANGELOG.md +++ b/shared-helpers/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.10](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.9...@bloom-housing/shared-helpers@5.0.1-alpha.10) (2022-06-29) + +**Note:** Version bump only for package @bloom-housing/shared-helpers + + + + + ## [5.0.1-alpha.9](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.8...@bloom-housing/shared-helpers@5.0.1-alpha.9) (2022-06-29) **Note:** Version bump only for package @bloom-housing/shared-helpers diff --git a/shared-helpers/package.json b/shared-helpers/package.json index 17919bf2c5..bd61c4f977 100644 --- a/shared-helpers/package.json +++ b/shared-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/shared-helpers", - "version": "5.0.1-alpha.9", + "version": "5.0.1-alpha.10", "description": "Shared helpers for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared-helpers", "main": "index.js", @@ -17,7 +17,7 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.3", - "@bloom-housing/ui-components": "^5.0.1-alpha.5" + "@bloom-housing/ui-components": "^5.0.1-alpha.6" }, "devDependencies": { "@bloom-housing/ui-components": "^3.0.1-alpha.15", diff --git a/sites/partners/CHANGELOG.md b/sites/partners/CHANGELOG.md index a15b4d647d..7dd3dacfec 100644 --- a/sites/partners/CHANGELOG.md +++ b/sites/partners/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.10](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.9...@bloom-housing/partners@5.0.1-alpha.10) (2022-06-29) + +**Note:** Version bump only for package @bloom-housing/partners + + + + + ## [5.0.1-alpha.9](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.8...@bloom-housing/partners@5.0.1-alpha.9) (2022-06-29) **Note:** Version bump only for package @bloom-housing/partners diff --git a/sites/partners/package.json b/sites/partners/package.json index a5f4d6d769..6455d80ac6 100644 --- a/sites/partners/package.json +++ b/sites/partners/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/partners", - "version": "5.0.1-alpha.9", + "version": "5.0.1-alpha.10", "author": "Sean Albert ", "description": "Partners app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -27,8 +27,8 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.3", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.9", - "@bloom-housing/ui-components": "^5.0.1-alpha.5", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.10", + "@bloom-housing/ui-components": "^5.0.1-alpha.6", "@mapbox/mapbox-sdk": "^0.13.0", "ag-grid-community": "^26.0.0", "ag-grid-react": "^26.0.0", diff --git a/sites/public/CHANGELOG.md b/sites/public/CHANGELOG.md index 13b397b4eb..96c4608de9 100644 --- a/sites/public/CHANGELOG.md +++ b/sites/public/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.10](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.9...@bloom-housing/public@5.0.1-alpha.10) (2022-06-29) + +**Note:** Version bump only for package @bloom-housing/public + + + + + ## [5.0.1-alpha.9](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.8...@bloom-housing/public@5.0.1-alpha.9) (2022-06-29) **Note:** Version bump only for package @bloom-housing/public diff --git a/sites/public/package.json b/sites/public/package.json index 66a8491beb..d37090174e 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/public", - "version": "5.0.1-alpha.9", + "version": "5.0.1-alpha.10", "author": "Sean Albert ", "description": "Public web app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -25,8 +25,8 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.3", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.9", - "@bloom-housing/ui-components": "^5.0.1-alpha.5", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.10", + "@bloom-housing/ui-components": "^5.0.1-alpha.6", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1", diff --git a/ui-components/CHANGELOG.md b/ui-components/CHANGELOG.md index 6595ab99ee..89ff138bbf 100644 --- a/ui-components/CHANGELOG.md +++ b/ui-components/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.6](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.5...@bloom-housing/ui-components@5.0.1-alpha.6) (2022-06-29) + + +### Bug Fixes + +* hide apply section if there is no data ([#2846](https://github.com/bloom-housing/bloom/issues/2846)) ([5fcaf58](https://github.com/bloom-housing/bloom/commit/5fcaf58acc8cc083c738f53a23beba4e567b2994)) + + + + + ## [5.0.1-alpha.5](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.4...@bloom-housing/ui-components@5.0.1-alpha.5) (2022-06-27) diff --git a/ui-components/package.json b/ui-components/package.json index 5b4a10e8b8..de02e68559 100644 --- a/ui-components/package.json +++ b/ui-components/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/ui-components", - "version": "5.0.1-alpha.5", + "version": "5.0.1-alpha.6", "author": "Sean Albert ", "description": "Shared user interface components for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared/ui-components", From c3fde4adbff2bd116c4acaf39f609d3c0ed0638f Mon Sep 17 00:00:00 2001 From: Jared White Date: Fri, 1 Jul 2022 17:25:16 -0700 Subject: [PATCH 026/598] fix: switch back to padding for image card leader (#2851) --- ui-components/src/blocks/ImageCard.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-components/src/blocks/ImageCard.scss b/ui-components/src/blocks/ImageCard.scss index 3e19801fb7..0770fdee93 100644 --- a/ui-components/src/blocks/ImageCard.scss +++ b/ui-components/src/blocks/ImageCard.scss @@ -61,7 +61,7 @@ @media (min-width: $screen-sm) { width: var(--bloom-width-2-3rd); - margin-block-start: var(--bloom-s8); - margin-inline-end: var(--bloom-s8); + padding-block-start: var(--bloom-s8); + padding-inline-end: var(--bloom-s8); } } From 357c9cc18fb3d563b0f10bb83621351e92fb6a23 Mon Sep 17 00:00:00 2001 From: "github.context.workflow" Date: Sat, 2 Jul 2022 00:25:52 +0000 Subject: [PATCH 027/598] chore(release): version - @bloom-housing/shared-helpers@5.0.1-alpha.11 - @bloom-housing/partners@5.0.1-alpha.11 - @bloom-housing/public@5.0.1-alpha.11 - @bloom-housing/ui-components@5.0.1-alpha.7 --- shared-helpers/CHANGELOG.md | 8 ++++++++ shared-helpers/package.json | 4 ++-- sites/partners/CHANGELOG.md | 8 ++++++++ sites/partners/package.json | 6 +++--- sites/public/CHANGELOG.md | 8 ++++++++ sites/public/package.json | 6 +++--- ui-components/CHANGELOG.md | 11 +++++++++++ ui-components/package.json | 2 +- 8 files changed, 44 insertions(+), 9 deletions(-) diff --git a/shared-helpers/CHANGELOG.md b/shared-helpers/CHANGELOG.md index fa3d4b92a4..bbbbe5e441 100644 --- a/shared-helpers/CHANGELOG.md +++ b/shared-helpers/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.11](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.10...@bloom-housing/shared-helpers@5.0.1-alpha.11) (2022-07-02) + +**Note:** Version bump only for package @bloom-housing/shared-helpers + + + + + ## [5.0.1-alpha.10](https://github.com/bloom-housing/bloom/compare/@bloom-housing/shared-helpers@5.0.1-alpha.9...@bloom-housing/shared-helpers@5.0.1-alpha.10) (2022-06-29) **Note:** Version bump only for package @bloom-housing/shared-helpers diff --git a/shared-helpers/package.json b/shared-helpers/package.json index bd61c4f977..aa2d467694 100644 --- a/shared-helpers/package.json +++ b/shared-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/shared-helpers", - "version": "5.0.1-alpha.10", + "version": "5.0.1-alpha.11", "description": "Shared helpers for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared-helpers", "main": "index.js", @@ -17,7 +17,7 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.3", - "@bloom-housing/ui-components": "^5.0.1-alpha.6" + "@bloom-housing/ui-components": "^5.0.1-alpha.7" }, "devDependencies": { "@bloom-housing/ui-components": "^3.0.1-alpha.15", diff --git a/sites/partners/CHANGELOG.md b/sites/partners/CHANGELOG.md index 7dd3dacfec..ab42554c55 100644 --- a/sites/partners/CHANGELOG.md +++ b/sites/partners/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.11](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.10...@bloom-housing/partners@5.0.1-alpha.11) (2022-07-02) + +**Note:** Version bump only for package @bloom-housing/partners + + + + + ## [5.0.1-alpha.10](https://github.com/bloom-housing/bloom/compare/@bloom-housing/partners@5.0.1-alpha.9...@bloom-housing/partners@5.0.1-alpha.10) (2022-06-29) **Note:** Version bump only for package @bloom-housing/partners diff --git a/sites/partners/package.json b/sites/partners/package.json index 6455d80ac6..7a2ef1c864 100644 --- a/sites/partners/package.json +++ b/sites/partners/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/partners", - "version": "5.0.1-alpha.10", + "version": "5.0.1-alpha.11", "author": "Sean Albert ", "description": "Partners app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -27,8 +27,8 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.3", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.10", - "@bloom-housing/ui-components": "^5.0.1-alpha.6", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.11", + "@bloom-housing/ui-components": "^5.0.1-alpha.7", "@mapbox/mapbox-sdk": "^0.13.0", "ag-grid-community": "^26.0.0", "ag-grid-react": "^26.0.0", diff --git a/sites/public/CHANGELOG.md b/sites/public/CHANGELOG.md index 96c4608de9..fe99affb7d 100644 --- a/sites/public/CHANGELOG.md +++ b/sites/public/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.11](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.10...@bloom-housing/public@5.0.1-alpha.11) (2022-07-02) + +**Note:** Version bump only for package @bloom-housing/public + + + + + ## [5.0.1-alpha.10](https://github.com/bloom-housing/bloom/compare/@bloom-housing/public@5.0.1-alpha.9...@bloom-housing/public@5.0.1-alpha.10) (2022-06-29) **Note:** Version bump only for package @bloom-housing/public diff --git a/sites/public/package.json b/sites/public/package.json index d37090174e..1fa69232cc 100644 --- a/sites/public/package.json +++ b/sites/public/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/public", - "version": "5.0.1-alpha.10", + "version": "5.0.1-alpha.11", "author": "Sean Albert ", "description": "Public web app reference implementation for the Bloom affordable housing system", "main": "index.js", @@ -25,8 +25,8 @@ }, "dependencies": { "@bloom-housing/backend-core": "^5.0.1-alpha.3", - "@bloom-housing/shared-helpers": "^5.0.1-alpha.10", - "@bloom-housing/ui-components": "^5.0.1-alpha.6", + "@bloom-housing/shared-helpers": "^5.0.1-alpha.11", + "@bloom-housing/ui-components": "^5.0.1-alpha.7", "@fortawesome/fontawesome-svg-core": "^6.1.1", "@fortawesome/free-regular-svg-icons": "^6.1.1", "@fortawesome/free-solid-svg-icons": "^6.1.1", diff --git a/ui-components/CHANGELOG.md b/ui-components/CHANGELOG.md index 89ff138bbf..b82e1f701a 100644 --- a/ui-components/CHANGELOG.md +++ b/ui-components/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [5.0.1-alpha.7](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.6...@bloom-housing/ui-components@5.0.1-alpha.7) (2022-07-02) + + +### Bug Fixes + +* switch back to padding for image card leader ([#2851](https://github.com/bloom-housing/bloom/issues/2851)) ([c3fde4a](https://github.com/bloom-housing/bloom/commit/c3fde4adbff2bd116c4acaf39f609d3c0ed0638f)) + + + + + ## [5.0.1-alpha.6](https://github.com/bloom-housing/bloom/compare/@bloom-housing/ui-components@5.0.1-alpha.5...@bloom-housing/ui-components@5.0.1-alpha.6) (2022-06-29) diff --git a/ui-components/package.json b/ui-components/package.json index de02e68559..ebe9878692 100644 --- a/ui-components/package.json +++ b/ui-components/package.json @@ -1,6 +1,6 @@ { "name": "@bloom-housing/ui-components", - "version": "5.0.1-alpha.6", + "version": "5.0.1-alpha.7", "author": "Sean Albert ", "description": "Shared user interface components for Bloom affordable housing system", "homepage": "https://github.com/bloom-housing/bloom/tree/master/shared/ui-components", From 5b1be3e5dc3f9830c231b9e5b363db6b9f2f9c2f Mon Sep 17 00:00:00 2001 From: Jared White Date: Tue, 5 Jul 2022 17:44:09 -0700 Subject: [PATCH 028/598] 2576/new email templates (#2594) * feat: add base HTML email layout (WIP) * chore: flesh out translated and injected text in email * Fix code style issues with Prettier * fix: additional improvements to confirmation email * chore: migration and refinements for confirmation email * chore: set up email logos for jurisdictions * chore: add cloudinary image URLs for email * fix: more default translations for confirmation email * 2730/new partner email (#2812) * feat: initial invite template * feat: update invite email template * feat: update translations * fix: fix broken typeorm command * feat: update migration * feat: add translations * fix: update translations * Fix code style issues with Prettier Co-authored-by: Lint Action Co-authored-by: dominikx96 Co-authored-by: Sean Albert --- backend/core/src/email/email.service.spec.ts | 117 ++++- backend/core/src/email/email.service.ts | 89 +++- ...660403-addConfirmationEmailTranslations.ts | 122 +++++ .../1655122535654-update-invite-email-text.ts | 25 ++ ...99904609-add-default-email-translations.ts | 28 ++ .../core/src/shared/views/change-email.hbs | 2 +- .../core/src/shared/views/confirmation.hbs | 90 +++- .../core/src/shared/views/forgot-password.hbs | 2 +- backend/core/src/shared/views/invite.hbs | 55 ++- .../core/src/shared/views/layouts/default.hbs | 9 + backend/core/src/shared/views/mfa-code.hbs | 2 +- .../core/src/shared/views/partials/footer.hbs | 33 +- .../core/src/shared/views/partials/head.hbs | 418 ++++++++++++++++++ .../core/src/shared/views/partials/header.hbs | 23 + .../shared/views/partials/simple-footer.hbs | 4 + .../core/src/shared/views/register-email.hbs | 2 +- 16 files changed, 954 insertions(+), 67 deletions(-) create mode 100644 backend/core/src/migration/1652135660403-addConfirmationEmailTranslations.ts create mode 100644 backend/core/src/migration/1655122535654-update-invite-email-text.ts create mode 100644 backend/core/src/migration/1655299904609-add-default-email-translations.ts create mode 100644 backend/core/src/shared/views/layouts/default.hbs create mode 100644 backend/core/src/shared/views/partials/head.hbs create mode 100644 backend/core/src/shared/views/partials/header.hbs create mode 100644 backend/core/src/shared/views/partials/simple-footer.hbs diff --git a/backend/core/src/email/email.service.spec.ts b/backend/core/src/email/email.service.spec.ts index 747e257ec6..23210df6c4 100644 --- a/backend/core/src/email/email.service.spec.ts +++ b/backend/core/src/email/email.service.spec.ts @@ -16,6 +16,7 @@ import { JurisdictionsService } from "../jurisdictions/services/jurisdictions.se import { Jurisdiction } from "../jurisdictions/entities/jurisdiction.entity" import { GeneratedListingTranslation } from "../translations/entities/generated-listing-translation.entity" import { GoogleTranslateService } from "../translations/services/google-translate.service" +import { ListingReviewOrder } from "../listings/types/listing-review-order-enum" declare const expect: jest.Expect jest.setTimeout(30000) @@ -80,7 +81,13 @@ describe("EmailService", () => { }, language: Language.en, translations: { + confirmation: { + whatHappensNext: "What happens next?", + applicationPeriodCloses: + "Once the application period closes, the property manager will begin processing applications.", + }, footer: { + line1: "Generic Portal is a project of the", footer: "Generic footer", thankYou: "Thank you!", }, @@ -94,20 +101,40 @@ describe("EmailService", () => { language: Language.en, translations: { confirmation: { - yourConfirmationNumber: "Here is your confirmation number:", + gotYourConfirmationNumber: "We got your application for", + yourConfirmationNumber: "Your Confirmation Number", // UPDATED + applicationReceived: 'Application
received completed', + applicationsClosed: 'Application
closed not completed', + applicationsRanked: 'Application
ranked not completed', + whatHappensNext: "What happens next?", + applicationPeriodCloses: + "JURISDICTION: Once the application period closes, the property manager will begin processing applications.", + eligibleApplicants: { + FCFS: + "Eligible applicants will be placed in order based on first come first serve basis.", + lotteryDate: "The lottery will be held on %{lotteryDate}.", + lottery: + "Eligible applicants will be placed in order based on preference and lottery rank.", + }, + contactedForAnInterview: + "If you are contacted for an interview, you will need to fill out a more detailed application and provide supporting documents.", + prepareForNextSteps: "Prepare for next steps", + whileYouWait: + "While you wait, there are things you can do to prepare for potential next steps and future opportunities.", + readHowYouCanPrepare: "Read about how you can prepare for next steps", + needToMakeUpdates: "Need to make updates?", + ifYouNeedToUpdateInformation: "", + shouldBeChosen: "Should your application be chosen, be prepared to fill out a more detailed application and provide required supporting documents.", subject: "Your Application Confirmation", thankYouForApplying: "Thanks for applying. We have received your application for", whatToExpectNext: "What to expect next:", - whatToExpect: { - FCFS: - "Applicants will be contacted by the property agent on a first come first serve basis until vacancies are filled.", - lottery: - "The lottery will be held on %{lotteryDate}. Applicants will be contacted by the agent in lottery rank order until vacancies are filled.", - noLottery: - "Applicants will be contacted by the agent in waitlist order until vacancies are filled.", - }, + }, + header: { + logoTitle: "Alameda County Housing Portal", + logoUrl: + "https://res.cloudinary.com/mariposta/image/upload/v1652326298/testing/alameda-portal.png", }, footer: { callToAction: "How are we doing? We'd like to get your ", @@ -115,6 +142,8 @@ describe("EmailService", () => { "https://docs.google.com/forms/d/e/1FAIpQLScr7JuVwiNW8q-ifFUWTFSWqEyV5ndA08jAhJQSlQ4ETrnl9w/viewform", feedback: "feedback", footer: "Alameda County - Housing and Community Development (HCD) Department", + line1: "Alameda County Housing Portal is a project of the", + line2: "Alameda County - Housing and Community Development (HCD) Department", thankYou: "Thank you", }, forgotPassword: { @@ -130,7 +159,8 @@ describe("EmailService", () => { }, leasingAgent: { contactAgentToUpdateInfo: - "If you need to update information on your application, do not apply again. Contact the agent. See below for contact information for the Agent for this listing.", + "If you need to update information on your application, do not apply again. Instead, contact the agent for this listing.", // UPDATED" + propertyManager: "Property Manager", officeHours: "Office Hours:", }, register: { @@ -143,6 +173,22 @@ describe("EmailService", () => { }, t: { hello: "Hello", + seeListing: "See Listing", + }, + }, + }) + + await translationsService.create({ + jurisdiction: { + id: jurisdiction.id, + }, + language: Language.es, + translations: { + confirmation: { + yourConfirmationNumber: "SPANISH NUMBER", // UPDATED + }, + footer: { + line1: "SPANISH Alameda County Housing Portal is a project of the", }, }, }) @@ -182,17 +228,54 @@ describe("EmailService", () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore await service.confirmation(listing, application, "http://localhost:3000") + expect(sendMock).toHaveBeenCalled() - expect(sendMock.mock.calls[0][0].to).toEqual(user.email) - expect(sendMock.mock.calls[0][0].subject).toEqual("Your Application Confirmation") - expect(sendMock.mock.calls[0][0].html).toMatch( - /Applicants will be contacted by the agent in waitlist order until vacancies are filled/ + const emailMock = sendMock.mock.calls[0][0] + expect(emailMock.to).toEqual(user.email) + expect(emailMock.subject).toEqual("Your Application Confirmation") + expect(emailMock.html).toMatch( + `Alameda County Housing Portal` ) - expect(sendMock.mock.calls[0][0].html).toMatch( - /http:\/\/localhost:3000\/listing\/Uvbk5qurpB2WI9V6WnNdH/ + expect(emailMock.html).toMatch("Your Confirmation Number") + expect(emailMock.html).toMatch("Marisela Baca") + expect(emailMock.html).toMatch( + /Eligible applicants will be placed in order based on first come first serve<\/strong> basis./ ) + expect(emailMock.html).toMatch(/http:\/\/localhost:3000\/listing\/Uvbk5qurpB2WI9V6WnNdH/) // contains application id - expect(sendMock.mock.calls[0][0].html).toMatch(/abc123/) + expect(emailMock.html).toMatch(/abc123/) + }) + + it("should support lottery order", async () => { + const service = await module.resolve(EmailService) + // TODO Remove BaseEntity from inheritance from all entities + await service.confirmation( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + { ...listing, reviewOrderType: ListingReviewOrder.lottery }, + application, + "http://localhost:3000" + ) + + const emailMock = sendMock.mock.calls[0][0] + expect(emailMock.html).toMatch( + /The lottery will be held on December 31, 2019. Eligible applicants will be placed in order based on preference and lottery rank<\/strong>./ + ) + }) + + it("should support translations", async () => { + const service = await module.resolve(EmailService) + await service.confirmation( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + listing, + { ...application, language: "es" }, + "http://localhost:3000" + ) + + const emailMock = sendMock.mock.calls[0][0] + expect(emailMock.html).toMatch("SPANISH NUMBER") + expect(emailMock.html).toMatch("SPANISH Alameda County Housing Portal is a project of the") }) }) diff --git a/backend/core/src/email/email.service.ts b/backend/core/src/email/email.service.ts index 63289ceb13..da26b93981 100644 --- a/backend/core/src/email/email.service.ts +++ b/backend/core/src/email/email.service.ts @@ -6,6 +6,7 @@ import Handlebars from "handlebars" import path from "path" import Polyglot from "node-polyglot" import fs from "fs" +import dayjs from "dayjs" import { ConfigService } from "@nestjs/config" import { TranslationsService } from "../translations/services/translations.service" import { JurisdictionResolverService } from "../jurisdictions/services/jurisdiction-resolver.service" @@ -16,6 +17,7 @@ import { ListingReviewOrder } from "../listings/types/listing-review-order-enum" import { Jurisdiction } from "../jurisdictions/entities/jurisdiction.entity" import { Language } from "../shared/types/language-enum" import { JurisdictionsService } from "../jurisdictions/services/jurisdictions.service" +import { Translation } from "../translations/entities/translation.entity" @Injectable({ scope: Scope.REQUEST }) export class EmailService { @@ -119,7 +121,7 @@ export class EmailService { public async confirmation(listing: Listing, application: Application, appUrl: string) { const jurisdiction = await this.getListingJurisdiction(listing) void (await this.loadTranslations(jurisdiction, application.language || Language.en)) - let whatToExpectText + let eligibleApplicantsText const listingUrl = `${appUrl}/listing/${listing.id}` const compiledTemplate = this.template("confirmation") @@ -129,34 +131,45 @@ export class EmailService { ) } - if (listing.applicationDueDate) { - if (listing.reviewOrderType === ListingReviewOrder.lottery) { - whatToExpectText = this.polyglot.t("confirmation.whatToExpect.lottery", { - lotteryDate: listing.applicationDueDate, - }) - } else { - whatToExpectText = this.polyglot.t("confirmation.whatToExpect.noLottery", { - lotteryDate: listing.applicationDueDate, - }) + if (listing.reviewOrderType === ListingReviewOrder.lottery) { + const lotteryText = [] + if (listing.applicationDueDate) { + lotteryText.push( + this.polyglot.t("confirmation.eligibleApplicants.lotteryDate", { + lotteryDate: dayjs(listing.applicationDueDate).format("MMMM D, YYYY"), + }) + ) } + lotteryText.push(this.polyglot.t("confirmation.eligibleApplicants.lottery")) + eligibleApplicantsText = new Handlebars.SafeString(lotteryText.join(" ")) } else { - whatToExpectText = this.polyglot.t("confirmation.whatToExpect.FCFS") + // for when listing.reviewOrderType === ListingReviewOrder.firstComeFirstServe + eligibleApplicantsText = new Handlebars.SafeString( + this.polyglot.t("confirmation.eligibleApplicants.FCFS") + ) } const user = { firstName: application.applicant.firstName, middleName: application.applicant.middleName, lastName: application.applicant.lastName, } + const nextStepsUrl = this.polyglot.t("confirmation.nextStepsUrl") await this.send( application.applicant.emailAddress, jurisdiction.emailFromAddress, this.polyglot.t("confirmation.subject"), compiledTemplate({ - listing: listing, - listingUrl: listingUrl, - application: application, - whatToExpectText: whatToExpectText, - user: user, + subject: this.polyglot.t("confirmation.subject"), + header: { + logoTitle: this.polyglot.t("header.logoTitle"), + logoUrl: this.polyglot.t("header.logoUrl"), + }, + listing, + listingUrl, + application, + eligibleApplicantsText, + nextStepsUrl: nextStepsUrl != "confirmation.nextStepsUrl" ? nextStepsUrl : null, + user, }) ) } @@ -186,19 +199,41 @@ export class EmailService { } private async loadTranslations(jurisdiction: Jurisdiction | null, language: Language) { - const jurisdictionalTranslations = await this.translationService.getTranslationByLanguageAndJurisdictionOrDefaultEn( - language, - jurisdiction ? jurisdiction.id : null - ) - const genericTranslations = await this.translationService.getTranslationByLanguageAndJurisdictionOrDefaultEn( - language, + let jurisdictionalTranslations: Translation | null, + genericTranslations: Translation | null, + jurisdictionalDefaultTranslations: Translation | null + + if (language != Language.en) { + if (jurisdiction) { + jurisdictionalTranslations = await this.translationService.getTranslationByLanguageAndJurisdictionOrDefaultEn( + language, + jurisdiction.id + ) + } + genericTranslations = await this.translationService.getTranslationByLanguageAndJurisdictionOrDefaultEn( + language, + null + ) + } + + if (jurisdiction) { + jurisdictionalDefaultTranslations = await this.translationService.getTranslationByLanguageAndJurisdictionOrDefaultEn( + Language.en, + jurisdiction.id + ) + } + + const genericDefaultTranslations = await this.translationService.getTranslationByLanguageAndJurisdictionOrDefaultEn( + Language.en, null ) // Deep merge const translations = merge( - genericTranslations.translations, - jurisdictionalTranslations.translations + genericDefaultTranslations.translations, + genericTranslations?.translations, + jurisdictionalDefaultTranslations?.translations, + jurisdictionalTranslations?.translations ) this.polyglot.replace(translations) @@ -228,6 +263,12 @@ export class EmailService { partials[filename.slice(0, -4)] = this.partial("partials/" + filename) }) + const layoutsDirName = path.resolve(__dirname, "..", "shared", "views/layouts") + + fs.readdirSync(layoutsDirName).forEach((filename) => { + partials[`layout_${filename.slice(0, -4)}`] = this.partial("layouts/" + filename) + }) + return partials } diff --git a/backend/core/src/migration/1652135660403-addConfirmationEmailTranslations.ts b/backend/core/src/migration/1652135660403-addConfirmationEmailTranslations.ts new file mode 100644 index 0000000000..87e49acbad --- /dev/null +++ b/backend/core/src/migration/1652135660403-addConfirmationEmailTranslations.ts @@ -0,0 +1,122 @@ +import { MigrationInterface, QueryRunner } from "typeorm" +import { Language } from "../shared/types/language-enum" + +export class addConfirmationEmailTranslations1652135660403 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + let generalTranslation = await queryRunner.query( + `SELECT translations FROM translations WHERE jurisdiction_id IS NULL AND language = ($1)`, + [Language.en] + ) + + generalTranslation = generalTranslation["0"]["translations"] + + generalTranslation.confirmation = { + ...generalTranslation.confirmation, + gotYourConfirmationNumber: "We got your application for", + yourConfirmationNumber: "Your Confirmation Number", + applicationReceived: 'Application
received completed', + applicationsClosed: 'Application
closed not completed', + applicationsRanked: 'Application
ranked not completed', + whatHappensNext: "What happens next?", + applicationPeriodCloses: + "Once the application period closes, the property manager will begin processing applications.", + eligibleApplicants: { + FCFS: + "Eligible applicants will be placed in order based on first come first serve basis.", + lotteryDate: "The lottery will be held on %{lotteryDate}.", + lottery: + "Eligible applicants will be placed in order based on preference and lottery rank.", + }, + contactedForAnInterview: + "If you are contacted for an interview, you will need to fill out a more detailed application and provide supporting documents.", + prepareForNextSteps: "Prepare for next steps", + whileYouWait: + "While you wait, there are things you can do to prepare for potential next steps and future opportunities.", + readHowYouCanPrepare: "Read about how you can prepare for next steps", + needToMakeUpdates: "Need to make updates?", + } + + generalTranslation.leasingAgent.contactAgentToUpdateInfo = + "If you need to update information on your application, do not apply again. Instead, contact the agent for this listing." + generalTranslation.leasingAgent.propertyManager = "Property Manager" + + generalTranslation.t.seeListing = "See Listing" + + await queryRunner.query( + `UPDATE "translations" SET translations = ($1) where jurisdiction_id IS NULL and language = ($2)`, + [generalTranslation, Language.en] + ) + + const [{ id: alamedaJurisdiction }] = await queryRunner.query( + `SELECT id FROM jurisdictions WHERE name = 'Alameda' LIMIT 1` + ) + + let alamedaTranslation = await queryRunner.query( + `SELECT translations FROM translations WHERE jurisdiction_id = ($1) AND language = ($2)`, + [alamedaJurisdiction, Language.en] + ) + alamedaTranslation = alamedaTranslation["0"]["translations"] + + alamedaTranslation.header = { + logoTitle: "Alameda County Housing Portal", + logoUrl: + "https://res.cloudinary.com/exygy/image/upload/v1652459319/housingbayarea/163838489-d5a1bc08-7d69-4c4a-8a94-8485617d8b46_dkkqvw.png", + } + alamedaTranslation.footer.line1 = "Alameda County Housing Portal is a project of the" + alamedaTranslation.footer.line2 = + "Alameda County - Housing and Community Development (HCD) Department" + + await queryRunner.query( + `UPDATE "translations" SET translations = ($1) where jurisdiction_id = ($2) and language = ($3)`, + [alamedaTranslation, alamedaJurisdiction, Language.en] + ) + + let [{ id: sanJoseJurisdiction }] = await queryRunner.query( + `SELECT id FROM jurisdictions WHERE name = 'San Jose' LIMIT 1` + ) + + let sanJoseTranslation = await queryRunner.query( + `SELECT translations FROM translations WHERE jurisdiction_id = ($1) AND language = ($2)`, + [sanJoseJurisdiction, Language.en] + ) + sanJoseTranslation = sanJoseTranslation["0"]["translations"] + + sanJoseTranslation.header = { + logoTitle: "City of San José Housing Portal", + logoUrl: + "https://res.cloudinary.com/exygy/image/upload/v1652459304/housingbayarea/163838487-7279a41f-4ec5-4da0-818b-4df3351a7971_yjnjh7.png", + } + sanJoseTranslation.footer.line1 = "City of San José Housing Portal is a project of the" + sanJoseTranslation.footer.line2 = "City of San José - Housing Department" + + await queryRunner.query( + `UPDATE "translations" SET translations = ($1) where jurisdiction_id = ($2) and language = ($3)`, + [sanJoseTranslation, sanJoseJurisdiction, Language.en] + ) + + let [{ id: sanMateoJurisdiction }] = await queryRunner.query( + `SELECT id FROM jurisdictions WHERE name = 'San Mateo' LIMIT 1` + ) + + let sanMateoTranslation = await queryRunner.query( + `SELECT translations FROM translations WHERE jurisdiction_id = ($1) AND language = ($2)`, + [sanMateoJurisdiction, Language.en] + ) + sanMateoTranslation = sanMateoTranslation["0"]["translations"] + + sanMateoTranslation.header = { + logoTitle: "San Mateo County Housing", + logoUrl: + "https://res.cloudinary.com/exygy/image/upload/v1652459282/housingbayarea/163838485-87dd8976-b816-424c-a303-93e5473e931e_qvnsml.png", + } + sanMateoTranslation.footer.line1 = "San Mateo County Housing Portal is a project of the" + sanMateoTranslation.footer.line2 = "San Mateo County - Department of Housing (DOH)" + + await queryRunner.query( + `UPDATE "translations" SET translations = ($1) where jurisdiction_id = ($2) and language = ($3)`, + [sanMateoTranslation, sanMateoJurisdiction, Language.en] + ) + } + + public async down(queryRunner: QueryRunner): Promise {} +} diff --git a/backend/core/src/migration/1655122535654-update-invite-email-text.ts b/backend/core/src/migration/1655122535654-update-invite-email-text.ts new file mode 100644 index 0000000000..01c01dc5f7 --- /dev/null +++ b/backend/core/src/migration/1655122535654-update-invite-email-text.ts @@ -0,0 +1,25 @@ +import { MigrationInterface, QueryRunner } from "typeorm" +import { Language } from "../shared/types/language-enum" +export class updateInviteEmailText1655122535654 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + const [{ language, translations }] = await queryRunner.query( + `SELECT language, translations from translations WHERE language = 'en' LIMIT 1` + ) + + translations["invite"] = { + hello: "Welcome to the Partners Portal", + confirmMyAccount: "Confirm my account", + inviteManageListings: + "You will now be able to manage listings and applications that you are a part of from one centralized location.", + inviteWelcomeMessage: "Welcome to the Partners Portal at %{appUrl}.", + toCompleteAccountCreation: "To complete your account creation, please click the link below:", + } + + await queryRunner.query( + `UPDATE "translations" SET translations = ($1) where jurisdiction_id is NULL and language = ($2)`, + [translations, Language.en] + ) + } + + public async down(queryRunner: QueryRunner): Promise {} +} diff --git a/backend/core/src/migration/1655299904609-add-default-email-translations.ts b/backend/core/src/migration/1655299904609-add-default-email-translations.ts new file mode 100644 index 0000000000..b035fb3589 --- /dev/null +++ b/backend/core/src/migration/1655299904609-add-default-email-translations.ts @@ -0,0 +1,28 @@ +import { MigrationInterface, QueryRunner } from "typeorm" +import { Language } from "../shared/types/language-enum" +export class addDefaultEmailTranslations1655299904609 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + const [{ language, translations }] = await queryRunner.query( + `SELECT language, translations from translations WHERE language = 'en' LIMIT 1` + ) + + translations["header"] = { + logoUrl: + "https://res.cloudinary.com/exygy/image/upload/v1652459319/housingbayarea/163838489-d5a1bc08-7d69-4c4a-8a94-8485617d8b46_dkkqvw.png", + logoTitle: "Alameda County Housing Portal", + } + + translations["footer"] = { + line1: + "Alameda County Housing Portal is a project of the Alameda County - Housing and Community Development (HCD) Department", + line2: "", + } + + await queryRunner.query( + `UPDATE "translations" SET translations = ($1) where jurisdiction_id is NULL and language = ($2)`, + [translations, Language.en] + ) + } + + public async down(queryRunner: QueryRunner): Promise {} +} diff --git a/backend/core/src/shared/views/change-email.hbs b/backend/core/src/shared/views/change-email.hbs index 3ea084a33f..d3e2c32919 100644 --- a/backend/core/src/shared/views/change-email.hbs +++ b/backend/core/src/shared/views/change-email.hbs @@ -8,4 +8,4 @@

{{t "changeEmail.changeMyEmail"}}

-{{> footer }} +{{> simple-footer }} diff --git a/backend/core/src/shared/views/confirmation.hbs b/backend/core/src/shared/views/confirmation.hbs index 9cc04f3cea..7b6ec0bfcf 100644 --- a/backend/core/src/shared/views/confirmation.hbs +++ b/backend/core/src/shared/views/confirmation.hbs @@ -1,7 +1,83 @@ -

{{t "t.hello"}} {{> user-name }},

-

{{t "confirmation.thankYouForApplying"}} {{listing.name}}

-

{{t "confirmation.yourConfirmationNumber"}} {{application.confirmationCode}}

-

{{t "confirmation.whatToExpectNext"}}
{{whatToExpectText}}

-

{{t "confirmation.shouldBeChosen"}}

-{{> leasing-agent }} -{{> footer }} +{{#> layout_default }} +

{{t "confirmation.gotYourConfirmationNumber"}}
{{listing.name}}

+ + + + + + + + + + + + + + + +
+ {{t "t.seeListing"}} + +
+{{/layout_default }} diff --git a/backend/core/src/shared/views/forgot-password.hbs b/backend/core/src/shared/views/forgot-password.hbs index 5b2f9fa2f9..0209837abe 100644 --- a/backend/core/src/shared/views/forgot-password.hbs +++ b/backend/core/src/shared/views/forgot-password.hbs @@ -12,4 +12,4 @@

{{t "forgotPassword.passwordInfo"}}

-{{> footer }} +{{> simple-footer }} diff --git a/backend/core/src/shared/views/invite.hbs b/backend/core/src/shared/views/invite.hbs index 00a6fce315..23a02715e5 100644 --- a/backend/core/src/shared/views/invite.hbs +++ b/backend/core/src/shared/views/invite.hbs @@ -1,11 +1,44 @@ -

{{t "invite.hello"}} {{> user-name }}

-

- {{t "invite.inviteMessage" appOptions}} -

-

- {{t "invite.toCompleteAccountCreation"}} -

-

- {{t "invite.confirmMyAccount"}} -

-{{> footer }} +{{#> layout_default }} +

+ + {{t "invite.hello"}} + +
+ + {{> user-name }} +

+ + + + + + + +
+

+ {{t "invite.inviteWelcomeMessage" appOptions}} +

+ {{t "invite.inviteManageListings"}} +

+ {{t "invite.toCompleteAccountCreation"}} +

+
+ + + + + + + +
+ + {{t "invite.confirmMyAccount"}} + +
+{{/layout_default }} diff --git a/backend/core/src/shared/views/layouts/default.hbs b/backend/core/src/shared/views/layouts/default.hbs new file mode 100644 index 0000000000..2960258da4 --- /dev/null +++ b/backend/core/src/shared/views/layouts/default.hbs @@ -0,0 +1,9 @@ + + + {{> head }} + + {{> header }} + {{> @partial-block }} + {{> footer }} + + diff --git a/backend/core/src/shared/views/mfa-code.hbs b/backend/core/src/shared/views/mfa-code.hbs index 38d0008144..ac650a65c0 100644 --- a/backend/core/src/shared/views/mfa-code.hbs +++ b/backend/core/src/shared/views/mfa-code.hbs @@ -5,4 +5,4 @@

{{t "mfaCodeEmail.mfaCode" mfaCodeOptions}}

-{{> footer }} +{{> simple-footer }} diff --git a/backend/core/src/shared/views/partials/footer.hbs b/backend/core/src/shared/views/partials/footer.hbs index fdb0898cb8..ac448bffe5 100644 --- a/backend/core/src/shared/views/partials/footer.hbs +++ b/backend/core/src/shared/views/partials/footer.hbs @@ -1,4 +1,29 @@ -

- {{t "footer.thankYou"}}
- {{t "footer.footer"}} -

\ No newline at end of file + + + + + + + + + + + + + +
+ +   + + diff --git a/backend/core/src/shared/views/partials/head.hbs b/backend/core/src/shared/views/partials/head.hbs new file mode 100644 index 0000000000..5ed30deff9 --- /dev/null +++ b/backend/core/src/shared/views/partials/head.hbs @@ -0,0 +1,418 @@ + + + + {{ subject }} + + + + + diff --git a/backend/core/src/shared/views/partials/header.hbs b/backend/core/src/shared/views/partials/header.hbs new file mode 100644 index 0000000000..419b66e25c --- /dev/null +++ b/backend/core/src/shared/views/partials/header.hbs @@ -0,0 +1,23 @@ + + +