diff --git a/README.md b/README.md index df54cb3be5..f5c640abe9 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Bloom consists of a client/server architecture using [Next.js](https://nextjs.or The frontend apps can easily be deployed to any Jamstack-friendly web host such as [Netlify](https://www.netlify.com/) or Vercel. The frontend build process performs a static rendering of as much of the React page component trees as possible based on API data available at the time of the build. Additional real-time interactivity is made possible by React components at run-time. -The backend can be simultaenously deployed to PaaS-style hosts such as Heroku. Its primary architectural dependency is a PostgreSQL database. +The backend can be simultaneously deployed to PaaS-style hosts such as Heroku. Its primary architectural dependency is a PostgreSQL database. ### Structure diff --git a/backend/core/package.json b/backend/core/package.json index 356b86802d..6926526f04 100644 --- a/backend/core/package.json +++ b/backend/core/package.json @@ -47,7 +47,7 @@ "@anchan828/nest-sendgrid": "^0.3.25", "@aws-sdk/client-s3": "^3.326.0", "@aws-sdk/s3-request-presigner": "^3.327.0", - "@google-cloud/translate": "^6.2.6", + "@google-cloud/translate": "^7.2.1", "@nestjs/axios": "^2.0.0", "@nestjs/cli": "^9.5.0", "@nestjs/common": "^9.4.2", @@ -60,9 +60,12 @@ "@nestjs/swagger": "^6.3.0", "@nestjs/throttler": "^4.0.0", "@nestjs/typeorm": "^9.0.1", + "@turf/buffer": "6.5.0", + "@turf/helpers": "6.5.0", + "@turf/boolean-point-in-polygon": "6.5.0", "@types/cache-manager": "^3.4.0", "async-retry": "^1.3.1", - "axios": "0.21.2", + "axios": "0.21.3", "cache-manager": "^3.4.0", "casbin": "5.13.0", "class-transformer": "0.3.1", @@ -82,17 +85,17 @@ "nanoid": "^3.1.12", "nestjs-twilio": "^4.1.1", "nestjs-typeorm-paginate": "^4.0.3", - "newrelic": "7.5.1", + "newrelic": "11.4.0", "node-polyglot": "^2.4.0", "passport": "^0.6.0", "passport-custom": "^1.1.1", - "passport-jwt": "^4.0.0", + "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", - "pg": "^8.4.1", + "pg": "^8.11.3", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.5.4", - "swagger-ui-express": "^4.1.4", + "swagger-ui-express": "^4.2.0", "ts-node": "10.8.0", "twilio": "^3.71.3", "typeorm": "0.3.12", diff --git a/backend/core/src/applications/applications.module.ts b/backend/core/src/applications/applications.module.ts index 30b996348b..16bbc1dce7 100644 --- a/backend/core/src/applications/applications.module.ts +++ b/backend/core/src/applications/applications.module.ts @@ -16,6 +16,7 @@ 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 { GeocodingService } from "./services/geocoding.service" @Module({ imports: [ @@ -28,7 +29,7 @@ import { ActivityLogModule } from "../activity-log/activity-log.module" EmailModule, ScheduleModule.forRoot(), ], - providers: [ApplicationsService, CsvBuilder, ApplicationCsvExporterService], + providers: [ApplicationsService, CsvBuilder, ApplicationCsvExporterService, GeocodingService], exports: [ApplicationsService], controllers: [ApplicationsController, ApplicationsSubmissionController], }) diff --git a/backend/core/src/applications/services/applications.service.ts b/backend/core/src/applications/services/applications.service.ts index 503c561473..45060cad09 100644 --- a/backend/core/src/applications/services/applications.service.ts +++ b/backend/core/src/applications/services/applications.service.ts @@ -29,6 +29,7 @@ import { Listing } from "../../listings/entities/listing.entity" import { ApplicationCsvExporterService } from "./application-csv-exporter.service" import { User } from "../../auth/entities/user.entity" import { StatusDto } from "../../shared/dto/status.dto" +import { GeocodingService } from "./geocoding.service" @Injectable({ scope: Scope.REQUEST }) export class ApplicationsService { @@ -38,6 +39,7 @@ export class ApplicationsService { private readonly listingsService: ListingsService, private readonly emailService: EmailService, private readonly applicationCsvExporter: ApplicationCsvExporterService, + private readonly geocodingService: GeocodingService, @InjectRepository(Application) private readonly repository: Repository, @InjectRepository(Listing) private readonly listingsRepository: Repository ) {} @@ -421,6 +423,15 @@ export class ApplicationsService { if (application.applicant.emailAddress && shouldSendConfirmation) { await this.emailService.confirmation(listing, application, applicationCreateDto.appUrl) } + + // Calculate geocoding preferences after save and email sent + if (listing.jurisdiction?.enableGeocodingPreferences) { + try { + void this.geocodingService.validateGeocodingPreferences(application, listing) + } catch (e) { + console.warn("error while validating geocoding preferences") + } + } return application } diff --git a/backend/core/src/applications/services/geocoding.service.spec.ts b/backend/core/src/applications/services/geocoding.service.spec.ts new file mode 100644 index 0000000000..682749d7fc --- /dev/null +++ b/backend/core/src/applications/services/geocoding.service.spec.ts @@ -0,0 +1,167 @@ +import { Test, TestingModule } from "@nestjs/testing" +import { GeocodingService } from "./geocoding.service" +import { Address } from "../../shared/entities/address.entity" +import { getRepositoryToken } from "@nestjs/typeorm" +import { Application } from "../entities/application.entity" +import { ValidationMethod } from "../../multiselect-question/types/validation-method-enum" +import { Listing } from "../../listings/entities/listing.entity" +import { InputType } from "../../shared/types/input-type" + +describe("GeocodingService", () => { + let service: GeocodingService + const applicationRepoUpdate = jest.fn() + const mockApplicationRepo = { + createQueryBuilder: jest.fn(), + update: applicationRepoUpdate, + } + const date = new Date() + const listingAddress: Address = { + id: "id", + createdAt: date, + updatedAt: date, + city: "Washington", + county: null, + state: "DC", + street: "1600 Pennsylvania Avenue", + street2: null, + zipCode: "20500", + latitude: 38.8977, + longitude: -77.0365, + } + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + GeocodingService, + { + provide: getRepositoryToken(Application), + useValue: mockApplicationRepo, + }, + ], + }).compile() + + service = await module.resolve(GeocodingService) + }) + + describe("verifyRadius", () => { + it("should return 'unknown' if lat and long not there", () => { + expect( + service.verifyRadius( + { + ...listingAddress, + latitude: null, + longitude: null, + }, + 5, + listingAddress + ) + ).toBe("unknown") + }) + it("should return 'true' if within radius", () => { + expect( + service.verifyRadius( + { + ...listingAddress, + latitude: 38.89485, + longitude: -77.04251, + }, + 5, + listingAddress + ) + ).toBe("true") + }) + it("should return 'false' if not within radius", () => { + expect( + service.verifyRadius( + { + ...listingAddress, + latitude: 39.284205, + longitude: -76.621698, + }, + 5, + listingAddress + ) + ).toBe("false") + }) + it("should return 'true' if same lat long", () => { + expect( + service.verifyRadius( + { + ...listingAddress, + }, + 5, + listingAddress + ) + ).toBe("true") + }) + }) + describe("validateRadiusPreferences", () => { + const listing = { + buildingAddress: listingAddress, + listingMultiselectQuestions: [ + { + multiselectQuestion: { + options: [ + { + text: "Geocoding option by radius", + collectAddress: true, + radiusSize: 5, + validationMethod: ValidationMethod.radius, + }, + ], + }, + }, + ], + } + const preferenceAddress = { ...listingAddress, latitude: 38.89485, longitude: -77.04251 } + const application = { + id: "applicationId", + preferences: [ + { + key: "Geocoding preference", + options: [ + { + key: "Geocoding option by radius", + checked: true, + extraData: [ + { + type: InputType.address, + value: preferenceAddress, + }, + ], + }, + ], + }, + ], + } + it("should save the validated value as extraData", async () => { + await service.validateRadiusPreferences( + (application as unknown) as Application, + listing as Listing + ) + expect(applicationRepoUpdate).toBeCalledWith( + { id: "applicationId" }, + { + preferences: expect.arrayContaining([ + expect.objectContaining({ + key: "Geocoding preference", + options: [ + { + checked: true, + extraData: [ + { + type: "address", + value: preferenceAddress, + }, + { key: "geocodingVerified", type: "text", value: "true" }, + ], + key: "Geocoding option by radius", + }, + ], + }), + ]), + } + ) + }) + }) +}) diff --git a/backend/core/src/applications/services/geocoding.service.ts b/backend/core/src/applications/services/geocoding.service.ts new file mode 100644 index 0000000000..bc20daedc6 --- /dev/null +++ b/backend/core/src/applications/services/geocoding.service.ts @@ -0,0 +1,101 @@ +import { point } from "@turf/helpers" +import buffer from "@turf/buffer" +import booleanPointInPolygon from "@turf/boolean-point-in-polygon" +import { InjectRepository } from "@nestjs/typeorm" +import { Repository } from "typeorm" +import { Address } from "../../shared/entities/address.entity" +import { Application } from "../entities/application.entity" +import { Listing } from "../../listings/entities/listing.entity" +import { ValidationMethod } from "../../multiselect-question/types/validation-method-enum" +import { MultiselectOption } from "../../multiselect-question/types/multiselect-option" +import { ApplicationMultiselectQuestion } from "../entities/application-multiselect-question.entity" +import { ApplicationMultiselectQuestionOption } from "../types/application-multiselect-question-option" +import { InputType } from "../../shared/types/input-type" +import { GeocodingValues } from "../../shared/types/geocoding-values" + +export class GeocodingService { + constructor( + @InjectRepository(Application) private readonly repository: Repository + ) {} + + public async validateGeocodingPreferences(application: Application, listing: Listing) { + await this.validateRadiusPreferences(application, listing) + } + + verifyRadius( + preferenceAddress: Address, + radius: number, + listingAddress: Address + ): GeocodingValues { + try { + if (preferenceAddress.latitude && preferenceAddress.longitude) { + const preferencePoint = point([ + Number.parseFloat(preferenceAddress.longitude.toString()), + Number.parseFloat(preferenceAddress.latitude.toString()), + ]) + const listingPoint = point([ + Number.parseFloat(listingAddress.longitude.toString()), + Number.parseFloat(listingAddress.latitude.toString()), + ]) + const calculatedBuffer = buffer(listingPoint.geometry, radius, { units: "miles" }) + return booleanPointInPolygon(preferencePoint, calculatedBuffer) + ? GeocodingValues.true + : GeocodingValues.false + } + } catch (e) { + console.log("error happened while calculating radius") + } + return GeocodingValues.unknown + } + + public async validateRadiusPreferences(application: Application, listing: Listing) { + // Get all radius preferences from the listing + const radiusPreferenceOptions: MultiselectOption[] = listing.listingMultiselectQuestions.reduce( + (options, multiselectQuestion) => { + const newOptions = multiselectQuestion.multiselectQuestion.options?.filter( + (option) => option.validationMethod === ValidationMethod.radius + ) + return [...options, ...newOptions] + }, + [] + ) + // If there are any radius preferences do the calculation and save the new preferences + if (radiusPreferenceOptions.length) { + const preferences: ApplicationMultiselectQuestion[] = application.preferences.map( + (preference) => { + const newPreferenceOptions: ApplicationMultiselectQuestionOption[] = preference.options.map( + (option) => { + const addressData = option.extraData.find((data) => data.type === InputType.address) + if (option.checked && addressData) { + const foundOption = radiusPreferenceOptions.find( + (preferenceOption) => preferenceOption.text === option.key + ) + if (foundOption) { + const geocodingVerified = this.verifyRadius( + addressData.value as Address, + foundOption.radiusSize, + listing.buildingAddress + ) + return { + ...option, + extraData: [ + ...option.extraData, + { + key: "geocodingVerified", + type: InputType.text, + value: geocodingVerified, + }, + ], + } + } + } + return option + } + ) + return { ...preference, options: newPreferenceOptions } + } + ) + await this.repository.update({ id: application.id }, { preferences: preferences }) + } + } +} diff --git a/backend/core/src/shared/types/geocoding-values.ts b/backend/core/src/shared/types/geocoding-values.ts new file mode 100644 index 0000000000..fa3f90d5f5 --- /dev/null +++ b/backend/core/src/shared/types/geocoding-values.ts @@ -0,0 +1,5 @@ +export enum GeocodingValues { + true = "true", + false = "false", + unknown = "unknown", +} diff --git a/doorway-ui-components/__tests__/navigation/ProgressNav.test.tsx b/doorway-ui-components/__tests__/navigation/ProgressNav.test.tsx deleted file mode 100644 index d4f3080d1e..0000000000 --- a/doorway-ui-components/__tests__/navigation/ProgressNav.test.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react" -import { render, cleanup } from "@testing-library/react" -import { ProgressNav } from "../../src/navigation/ProgressNav" - -afterEach(cleanup) - -describe("", () => { - it("renders without error", () => { - const { getByText } = render( - - ) - expect(getByText("You")).toBeTruthy() - expect(getByText("Household")).toBeTruthy() - expect(getByText("Income")).toBeTruthy() - expect(getByText("Preferences")).toBeTruthy() - expect(getByText("Review")).toBeTruthy() - }) -}) diff --git a/doorway-ui-components/index.ts b/doorway-ui-components/index.ts index 90f20d1c1f..9921a2fc0d 100644 --- a/doorway-ui-components/index.ts +++ b/doorway-ui-components/index.ts @@ -84,7 +84,7 @@ export * from "./src/lists/PreferencesList" /* Navigation */ export * from "./src/navigation/FooterNav" export * from "./src/navigation/LanguageNav" -export * from "./src/navigation/ProgressNav" +// DELETED: export * from "./src/navigation/ProgressNav" export * from "./src/navigation/TabNav" export * from "./src/navigation/Tabs" export * from "./src/navigation/Breadcrumbs" diff --git a/doorway-ui-components/package.json b/doorway-ui-components/package.json index 826fbf7fba..576ed96dfb 100644 --- a/doorway-ui-components/package.json +++ b/doorway-ui-components/package.json @@ -110,7 +110,7 @@ "typescript": "^4.9.4" }, "dependencies": { - "@bloom-housing/ui-components": "12.0.26", + "@bloom-housing/ui-components": "12.1.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/doorway-ui-components/src/actions/Button.tsx b/doorway-ui-components/src/actions/Button.tsx index 54373b8e1e..0f22813670 100644 --- a/doorway-ui-components/src/actions/Button.tsx +++ b/doorway-ui-components/src/actions/Button.tsx @@ -27,7 +27,6 @@ export interface ButtonProps extends AppearanceProps { transition?: boolean type?: "button" | "submit" | "reset" unstyled?: boolean - isActive?: boolean index?: number label?: string diff --git a/doorway-ui-components/src/global/css-imports.scss b/doorway-ui-components/src/global/css-imports.scss index 0af788fd1d..215aed28d2 100644 --- a/doorway-ui-components/src/global/css-imports.scss +++ b/doorway-ui-components/src/global/css-imports.scss @@ -2,6 +2,6 @@ @import "tailwindcss/components"; @import "fonts/metropolis/metropolis.css"; -@import url("https://fonts.googleapis.com/css?family=Noto+Serif:400,700,400italic"); +@import url("https://fonts.googleapis.com/css?family=Noto+Serif:400,600,700,400italic"); @import "tailwindcss/utilities"; diff --git a/doorway-ui-components/src/global/fonts/metropolis/metropolis.css b/doorway-ui-components/src/global/fonts/metropolis/metropolis.css index 4342e441e1..73496087aa 100644 --- a/doorway-ui-components/src/global/fonts/metropolis/metropolis.css +++ b/doorway-ui-components/src/global/fonts/metropolis/metropolis.css @@ -71,7 +71,7 @@ } @font-face { - font-family: 'Metropolis Extra'; + font-family: 'Metropolis'; src: url('Metropolis-ExtraLightItalic.woff2') format('woff2'), url('Metropolis-ExtraLightItalic.woff') format('woff'); font-weight: 200; @@ -107,7 +107,7 @@ } @font-face { - font-family: 'Metropolis Semi'; + font-family: 'Metropolis'; src: url('Metropolis-SemiBold.woff2') format('woff2'), url('Metropolis-SemiBold.woff') format('woff'); font-weight: 600; @@ -116,7 +116,7 @@ } @font-face { - font-family: 'Metropolis Extra'; + font-family: 'Metropolis'; src: url('Metropolis-ExtraLight.woff2') format('woff2'), url('Metropolis-ExtraLight.woff') format('woff'); font-weight: 200; @@ -134,7 +134,7 @@ } @font-face { - font-family: 'Metropolis Semi'; + font-family: 'Metropolis'; src: url('Metropolis-SemiBoldItalic.woff2') format('woff2'), url('Metropolis-SemiBoldItalic.woff') format('woff'); font-weight: 600; diff --git a/doorway-ui-components/src/global/forms.scss b/doorway-ui-components/src/global/forms.scss index 92b9d0993a..e422faffb4 100644 --- a/doorway-ui-components/src/global/forms.scss +++ b/doorway-ui-components/src/global/forms.scss @@ -86,10 +86,13 @@ } input.input { - @apply px-3; + padding-inline: var(--bloom-s3); + padding-block: var(--bloom-s4); border-width: 2px; - border-color: var(--bloom-color-gray-450); - font-size: 0.75rem; + background-color: var(--bloom-color-gray-200); + border-color: var(--bloom-color-gray-500); + border-radius: var(--bloom-rounded-lg); + font-size: 0.9rem; } .prepend { @@ -124,7 +127,7 @@ text-decoration: none; // Appears to be for the case of no label outline: none; - box-shadow: 0 0 0 2px #ffffff, 0 0 3px 4px $tailwind-primary; + box-shadow: 0 0 0 2px #ffffff, 0 0 3px 4px var(--bloom-color-primary); } } @@ -140,7 +143,7 @@ input[type="radio"] + label::before { background: white; border-radius: 4px; - box-shadow: 0 0 0 1px white, 0 0 0 2px map-get($tailwind-gray, 550); + box-shadow: 0 0 0 1px white, 0 0 0 2px var(--bloom-color-gray-500); content: "\a0"; display: inline-block; height: 1.25rem; @@ -159,17 +162,17 @@ input[type="checkbox"]:checked + label::before, input[type="radio"]:checked + label::before { - background-color: var(--bloom-color-accent-cool-light); - box-shadow: 0 0 0 1px var(--bloom-color-accent-cool-light); + background-color: var(--bloom-color-primary); + box-shadow: 0 0 0 2px var(--bloom-color-primary); } input[type="radio"]:checked + label::before { - box-shadow: 0 0 0 1px white, 0 0 0 2px $tailwind-primary; + box-shadow: 0 0 0 2px white, 0 0 0 3px var(--bloom-color-gray-500); } input[type="checkbox"]:checked + label::before { background-image: url("/images/check.png"); - background-image: url("/images/check-black.svg"); + background-image: url("/images/check.svg"); background-position: 50%; background-repeat: no-repeat; } @@ -192,17 +195,16 @@ select { @apply text-gray-950; - @apply rounded; @apply border; @apply border-gray-500; @apply bg-gray-200; @apply text-gray-950; @apply leading-tight; - @apply py-2; + @apply py-4; @apply px-3; - height: 3em; + height: 3.5em; font-family: inherit; - font-size: 1rem; + font-size: 0.9rem !important; line-height: normal; -moz-appearance: none; -webkit-appearance: none; @@ -213,6 +215,8 @@ background-repeat: no-repeat; background-size: 0.75rem; padding-right: 2.25rem; + border-radius: var(--bloom-rounded-lg); + border-width: 2px !important; } input:disabled, diff --git a/doorway-ui-components/src/navigation/ProgressNav.docs.mdx b/doorway-ui-components/src/navigation/ProgressNav.docs.mdx deleted file mode 100644 index 3f9999384e..0000000000 --- a/doorway-ui-components/src/navigation/ProgressNav.docs.mdx +++ /dev/null @@ -1,47 +0,0 @@ -import { Canvas, Story, ArgsTable } from "@storybook/addon-docs" -import { ProgressNav } from "./ProgressNav" - -# Progress Nav - -The progress nav component is used to illustrate progress in a multi-step process. It displays the step labels, and uses color and layout to indicate which steps are completed, in-progress, and unstarted. - -## Dot Style - - - - - -## Bar Style - - - - - -You can apply CSS variables to the `.progrss-nav` selector to customize the appearance of the component. - -| Name | Type | Description | Default | -| ----------------------------- | ----- | ------------------------------------------- | ------------------------------ | -| `--completed-step-color` | Color | The color of completed step | `--bloom-color-primary-darker` | -| `--completed-step-font-color` | Color | The color of completed step label | `--bloom-color-primary-darker` | -| `--active-step-color` | Color | The color of active step | `--bloom-color-primary` | -| `--active-step-font-color` | Color | The color of active step label | `--bloom-color-primary-darker` | -| `--future-step-color` | Color | The color of future step | `--bloom-color-gray-450` | -| `--future-step-font-color` | Color | The color of future step label | `--bloom-color-gray-750` | -| | | | | -| `--dot-size` | Size | The diameter of each dot step | `--bloom-s3` | -| `--dot-padding-left-mobile` | Size | The padding-left of each dot step on mobile | `--bloom-s2` | -| `--dot-label-padding-top` | Size | The padding-top of each dot step label | `--bloom-s4` | -| `--dot-label-padding-left` | Size | The padding-left of each dot step label | `--bloom-s1` | -| `--dot-font-size-desktop` | Size | The font size of dot step labels on desktop | `--bloom-font-size-base` | -| `--dot-font-size-mobile` | Size | The font size of dot step labels on mobile | `--bloom-font-size-3xs` | -| `--dot-line-color` | Color | The color of the dot connecting line | `--bloom-color-gray-450` | -| `--dot-active-font-weight` | Size | The font weight of active dot step label | `600` | -| `--dot-text-transform` | Size | The capitalization of dot step label | `capitalize` | -| | | | | -| `--bar-height` | Size | The height of bar step | `--bloom-s4` | -| `--bar-spacing` | Size | The spacing between each bar step | `--bloom-s0_5` | -| `--bar-label-padding-top` | Size | The padding-top of each bar step label | `--bloom-s2` | -| `--bar-label-padding-left` | Size | The padding-left of each bar step label | `--bloom-s0_5` | -| `--bar-font-size` | Size | The font size of bar step labels on desktop | `--bloom-font-size-base` | -| `--bar-active-font-weight` | Size | The font weight of active bar step label | `600` | -| `--bar-text-transform` | Size | The capitalization of dot step label | `capitalize` | diff --git a/doorway-ui-components/src/navigation/ProgressNav.scss b/doorway-ui-components/src/navigation/ProgressNav.scss deleted file mode 100644 index 14789a8603..0000000000 --- a/doorway-ui-components/src/navigation/ProgressNav.scss +++ /dev/null @@ -1,148 +0,0 @@ -.progress-nav { - --completed-step-color: var(--bloom-color-gray-850); - --completed-step-font-color: var(--bloom-color-gray-850); - --active-step-color: var(--bloom-color-primary); - --active-step-font-color: var(--bloom-color-gray-950); - --future-step-color: var(--bloom-color-gray-450); - --future-step-font-color: var(--bloom-text-color); - - --dot-size: var(--bloom-s3); - --dot-padding-left-mobile: var(--bloom-s2); - --dot-label-padding-top: var(--bloom-s4); - --dot-label-padding-left: var(--bloom-s1); - --dot-font-size-desktop: var(--bloom-font-size-base); - --dot-font-size-mobile: var(--bloom-font-size-3xs); - --dot-line-color: var(--bloom-color-gray-450); - --dot-active-font-weight: bold; - --dot-text-transform: capitalize; - - --bar-height: var(--bloom-s4); - --bar-spacing: var(--bloom-s0_5); - --bar-label-padding-top: var(--bloom-s2); - --bar-label-padding-left: var(--bloom-s0_5); - --bar-font-size: var(--bloom-font-size-base); - --bar-active-font-weight: bold; - --bar-text-transform: capitalize; - - display: table; - width: 100%; -} - -.progress-nav__dot-item { - font-size: var(--dot-font-size-mobile); - padding: var(--dot-size) var(--dot-padding-left-mobile); - position: relative; - text-align: center; - text-transform: var(--dot-text-transform); - display: table-cell; - white-space: normal; - float: none; - @media (min-width: $screen-md) { - font-size: var(--dot-font-size-desktop); - padding: 0rem; - } - - &:before { - position: absolute; - height: var(--dot-size); - width: var(--dot-size); - background-color: var(--completed-step-color); - border-radius: 50%; - top: 0; - left: 50%; - content: ""; - transform: translateX(-50%); - z-index: 2; - } - - &:after { - position: absolute; - background-color: var(--dot-line-color); - width: 100%; - left: 0; - top: 0.4rem; - content: ""; - height: 1px; - } - - &:first-of-type { - &:after { - left: 50%; - } - } - - &:last-of-type { - &:after { - left: auto; - right: 50%; - } - } - .progress-nav__item-container { - padding-top: var(--dot-label-padding-top); - padding-left: var(--dot-label-padding-left); - color: var(--completed-step-font-color); - display: block; - position: relative; - pointer-events: none; - cursor: default; - z-index: 3; - } - - &.is-active { - &:before { - background-color: var(--active-step-color); - } - .progress-nav__item-container { - color: var(--active-step-font-color); - font-weight: var(--dot-active-font-weight); - } - } - - &.is-disabled { - &:before { - background-color: var(--future-step-color); - } - .progress-nav__item-container { - color: var(--future-step-font-color); - } - } -} - -.progress-nav__bar { - border-spacing: var(--bar-spacing); - table-layout: fixed; -} - -.progress-nav__bar-item { - // drop the labels on mobile view - font-size: 0; - padding-top: var(--bar-label-padding-top); - padding-left: var(--bar-label-padding-left); - position: relative; - display: table-cell; - white-space: normal; - float: none; - max-width: 20%; - border-top-width: var(--bar-height); - border-top-color: var(--completed-step-color); - text-transform: var(--bar-text-transform); - @media (min-width: $screen-md) { - font-size: var(--bar-font-size); - } - &.is-active { - border-top-color: var(--active-step-color); - .progress-nav__item-container { - font-weight: var(--bar-active-font-weight); - color: var(--active-step-font-color); - } - } - &.is-disabled { - border-top-color: var(--future-step-color); - .progress-nav__item-container { - color: var(--future-step-font-color); - } - } - .progress-nav__item-container { - color: var(--completed-step-font-color); - } -} diff --git a/doorway-ui-components/src/navigation/ProgressNav.stories.tsx b/doorway-ui-components/src/navigation/ProgressNav.stories.tsx deleted file mode 100644 index 956050e76b..0000000000 --- a/doorway-ui-components/src/navigation/ProgressNav.stories.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from "react" -import { BADGES } from "../../.storybook/constants" -import { ProgressNav } from "./ProgressNav" -import ProgressNavDocs from "./ProgressNav.docs.mdx" - -export default { - title: "Navigation/Progress Nav 🚩", - decorators: [(storyFn: any) =>
{storyFn()}
], - parameters: { - docs: { - page: ProgressNavDocs, - }, - badges: [BADGES.GEN2], - }, -} - -export const Default = () => ( - -) - -export const barStyle = () => ( - -) diff --git a/shared-helpers/package.json b/shared-helpers/package.json index 5a73973583..5ed23160ac 100644 --- a/shared-helpers/package.json +++ b/shared-helpers/package.json @@ -18,7 +18,8 @@ "dependencies": { "@bloom-housing/backend-core": "^7.13.0", "@bloom-housing/doorway-ui-components": "^1.0.0", - "@bloom-housing/ui-components": "12.0.26", + "@bloom-housing/ui-components": "12.1.0", + "@bloom-housing/ui-seeds": "1.12.1", "axios-cookiejar-support": "4.0.6", "tough-cookie": "4.1.3" }, diff --git a/shared-helpers/src/auth/Timeout.tsx b/shared-helpers/src/auth/Timeout.tsx index 6b13d563d2..0fc0194ad5 100644 --- a/shared-helpers/src/auth/Timeout.tsx +++ b/shared-helpers/src/auth/Timeout.tsx @@ -1,15 +1,13 @@ import React, { createElement, FunctionComponent, useContext, useEffect, useState } from "react" import { AuthContext } from "./AuthContext" import { ConfigContext } from "./ConfigContext" +import { Button } from "@bloom-housing/ui-seeds" import { NavigationContext, - Button, Modal, setSiteAlertMessage, AlertTypes, t, - AppearanceSizeType, - AppearanceStyleType, } from "@bloom-housing/ui-components" const PROMPT_TIMEOUT = 60000 @@ -85,12 +83,12 @@ export const IdleTimeout: FunctionComponent = ({ const modalActions = [ , diff --git a/shared-helpers/src/locales/es.json b/shared-helpers/src/locales/es.json index b62d7dd3ee..5be721ca9e 100644 --- a/shared-helpers/src/locales/es.json +++ b/shared-helpers/src/locales/es.json @@ -39,7 +39,7 @@ "application.alternateContact.contact.description": "Solo utilizaremos esta información para ponernos en contacto con él o ella en relación con su solicitud.", "application.alternateContact.contact.emailAddressFormLabel": "Dirección de email del contacto", "application.alternateContact.contact.phoneNumberFormLabel": "Número telefónico del contacto", - "application.alternateContact.contact.title": "Díganos cómo comunicarnos con su contacto alternativo", + "application.alternateContact.contact.title": "Díganos cómo comunicarnos con su contacto alternativo.", "application.alternateContact.name.alternateContactFormLabel": "Nombre del contacto alternativo", "application.alternateContact.name.caseManagerAgencyFormLabel": "¿En dónde trabaja su administrador(a) de casos o asesor(a) sobre vivienda?", "application.alternateContact.name.caseManagerAgencyFormPlaceHolder": "Agencia", @@ -79,8 +79,11 @@ "application.contact.couldntLocateAddress": "No hemos podido ubicar la dirección que ingresó. Por favor_ confirme que sea la dirección correcta.", "application.contact.doYouWorkInDescription": "Por decidirse", "application.contact.doYouWorkIn": "¿Trabaja usted en ?", + "application.contact.familyName": "Apellido", + "application.contact.givenName": "Nombre de pila", "application.contact.mailingAddress": "Dirección postal", "application.contact.noPhoneNumber": "No tengo un número telefónico", + "application.contact.number.subNote": "10 dígitos, por ejemplo 999-999-9999", "application.contact.phoneNumberTypes.cell": "Celular", "application.contact.phoneNumberTypes.home": "Casa", "application.contact.phoneNumberTypes.prompt": "¿Qué tipo de número es este?", @@ -91,11 +94,12 @@ "application.contact.state": "Estado", "application.contact.streetAddress": "Domicilio", "application.contact.suggestedAddress": "Dirección sugerida:", - "application.contact.title": "Gracias %{firstName}. Ahora, necesitamos saber cómo comunicarnos con usted.", + "application.contact.title": "Gracias, %{firstName}. Ahora necesitamos saber cómo comunicarnos con usted acerca de su solicitud.", "application.contact.verifyAddressTitle": "Hemos localizado la siguiente dirección. Por favor_ confirme que sea la dirección correcta.", "application.contact.workAddress": "Dirección del trabajo", "application.contact.youEntered": "Ha ingresado los siguientes datos:", "application.contact.yourAdditionalPhoneNumber": "Su segundo número de teléfono", + "application.contact.yourAddress": "Su dirección", "application.contact.yourPhoneNumber": "Su número telefónico", "application.contact.zipCode": "Código Postal", "application.contact.zip": "Código Postal", @@ -151,7 +155,7 @@ "application.household.householdStudent.question": "¿Alguien de su grupo familiar es estudiante a tiempo completo o cumplirá 18 años en los próximos 60 días?", "application.household.householdStudent.title": "El grupo familiar incluye un estudiante o miembro que está por cumplir 18 años", "application.household.liveAlone.liveWithOtherPeople": "Otras personas vivirán conmigo", - "application.household.liveAlone.title": "Ahora nos gustaría obtener información acerca de las otras personas que residirán con usted en la vivienda", + "application.household.liveAlone.title": "Ahora nos gustaría obtener información acerca de las otras personas que residirán con usted en la vivienda.", "application.household.liveAlone.willLiveAlone": "Viviré solo(a)", "application.household.member.cancelAddingThisPerson": "Cancelar añadir a esta persona", "application.household.member.dateOfBirth": "Fecha de nacimiento", @@ -159,8 +163,8 @@ "application.household.member.haveSameAddress": "¿Tiene la misma dirección que usted?", "application.household.member.name": "Nombre del miembro del hogar", "application.household.member.saveHouseholdMember": "Guardar el miembro del hogar", - "application.household.member.subTitle": "Tendrá la oportunidad de añadir más miembros del hogar en la próxima pantalla", - "application.household.member.title": "Háblenos acerca de esta persona", + "application.household.member.subTitle": "Tendrá la oportunidad de añadir más miembros del hogar en la próxima pantalla.", + "application.household.member.title": "Háblenos acerca de esta persona.", "application.household.member.updateHouseholdMember": "Actualizar al miembro del hogar", "application.household.member.whatIsTheirRelationship": "¿Cuál es su parentesco o relación con usted?", "application.household.member.whatReletionship": "¿Cuál es su parentesco o relación con usted?", @@ -175,11 +179,12 @@ "application.household.preferredUnit.options.studio": "Estudio", "application.household.preferredUnit.options.threeBdrm": "3 dormitorios", "application.household.preferredUnit.options.twoBdrm": "2 dormitorios", - "application.household.preferredUnit.optionsLabel": "Marque todas las opciones que correspondan:", + "application.household.preferredUnit.optionsLabel": "Marque todas las opciones que correspondan", "application.household.preferredUnit.preferredUnitType": "Tipo de vivienda preferida", "application.household.preferredUnit.subTitle": "Aunque los tamaños de las unidades en general se basen en la ocupación, indique el tamaño de unidad que desee para determinar su preferencia en esta oportunidad o establecer una lista de espera (solo por esta oportunidad).", "application.household.preferredUnit.title": "¿Cuáles son los tamaños de vivienda que le interesan?", "application.household.primaryApplicant": "Solicitante primario", + "application.name.dobHelper": "Por ejemplo: 01 19 2000", "application.name.emailPrivacy": "Solo utilizaremos su dirección de email para comunicarnos con usted en relación con su solicitud.", "application.name.firstName": "Nombre", "application.name.lastName": "Apellido", @@ -444,7 +449,9 @@ "errors.dateOfBirthError": "Por favor ingrese una fecha de nacimiento válida", "errors.emailAddressError": "Por favor ingrese una dirección de email", "errors.errorsToResolve": "Hay errores que tendrá que corregir antes de poder seguir adelante.", + "errors.familyNameError": "Por favor ingrese un apellido", "errors.firstNameError": "Por favor ingrese un nombre", + "errors.givenNameError": "Por favor ingrese un nombre de pila", "errors.householdTooBig": "El número de miembros de su hogar es demasiado alto.", "errors.householdTooSmall": "El número de miembros de su hogar es demasiado bajo.", "errors.lastNameError": "Por favor ingrese un apellido", @@ -1061,7 +1068,7 @@ "t.petsPolicy": "Política de mascotas", "t.phoneNumberPlaceholder": "(555) 555-5555", "t.phone": "Teléfono", - "t.pleaseSelectOne": "Por favor seleccione una opción.", + "t.pleaseSelectOne": "Por favor seleccione una opción", "t.pleaseSelectYesNo": "Elija “sí” o “no”.", "t.pm": "PM", "t.preferNotToSay": "Prefiero no decirlo", diff --git a/shared-helpers/src/locales/general.json b/shared-helpers/src/locales/general.json index acfb94b898..debb7aa29f 100644 --- a/shared-helpers/src/locales/general.json +++ b/shared-helpers/src/locales/general.json @@ -39,7 +39,7 @@ "application.alternateContact.contact.description": "We'll only use this information to contact them about your application.", "application.alternateContact.contact.emailAddressFormLabel": "Contact email address", "application.alternateContact.contact.phoneNumberFormLabel": "Contact phone number", - "application.alternateContact.contact.title": "Let us know how to reach your alternate contact", + "application.alternateContact.contact.title": "Let us know how to reach your alternate contact.", "application.alternateContact.name.alternateContactFormLabel": "Name of alternate contact", "application.alternateContact.name.caseManagerAgencyFormLabel": "Where does your case manager or housing counselor work?", "application.alternateContact.name.caseManagerAgencyFormPlaceHolder": "Agency", @@ -79,25 +79,32 @@ "application.contact.couldntLocateAddress": "We couldn't locate the address you entered. Please confirm it's correct.", "application.contact.doYouWorkIn": "Do you work in %{county} County?", "application.contact.doYouWorkInDescription": "TBD", + "application.contact.familyName": "Family Name", + "application.contact.givenName": "Given Name", "application.contact.mailingAddress": "Mailing Address", "application.contact.noPhoneNumber": "I don't have a telephone number", + "application.contact.number": "Number", + "application.contact.number.subNote": "10-digit, for example 999-999-9999", "application.contact.phoneNumberTypes.cell": "Cell", "application.contact.phoneNumberTypes.home": "Home", "application.contact.phoneNumberTypes.prompt": "What type of number is this?", "application.contact.phoneNumberTypes.work": "Work", "application.contact.preferredContactType": "Preferred Contact Type", "application.contact.provideAMailingAddress": "Provide an address where you can receive updates and materials about your application.", + "application.contact.secondNumber": "Second Number", "application.contact.sendMailToMailingAddress": "Send my mail to a different address", "application.contact.state": "State", "application.contact.streetAddress": "Street Address", "application.contact.suggestedAddress": "Suggested Address:", - "application.contact.title": "Thanks %{firstName}. Now we need to know how to contact you.", + "application.contact.title": "Thanks, %{firstName}. Now we need to know how to contact you about your application.", "application.contact.verifyAddressTitle": "We have located the following address. Please confirm it's correct.", + "application.contact.verifyMultipleAddresses": "Since there are multiple options for this preference, you’ll need to verify multiple addresses.", "application.contact.workAddress": "Work Address", "application.contact.youEntered": "You Entered:", "application.contact.yourAdditionalPhoneNumber": "Your Second Phone Number", + "application.contact.yourAddress": "Your Address", "application.contact.yourPhoneNumber": "Your Phone Number", - "application.contact.zip": "Zip", + "application.contact.zip": "Zip Code", "application.contact.zipCode": "Zipcode", "application.details.adaPriorities": "ADA Priorities Selected", "application.edited": "Edited", @@ -151,7 +158,7 @@ "application.household.householdStudent.question": "Is someone in your household a full time student or going to turn 18 years old within 60 days?", "application.household.householdStudent.title": "Household Includes Student or Member Nearing 18", "application.household.liveAlone.liveWithOtherPeople": "Other people will live with me", - "application.household.liveAlone.title": "Next we would like to know about the others who will live with you in the unit", + "application.household.liveAlone.title": "Next we would like to know about the others who will live with you in the unit.", "application.household.liveAlone.willLiveAlone": "I will live alone", "application.household.member.cancelAddingThisPerson": "Cancel adding this person", "application.household.member.dateOfBirth": "Date of Birth", @@ -159,8 +166,8 @@ "application.household.member.haveSameAddress": "Do they have same address as you?", "application.household.member.name": "Household member's name", "application.household.member.saveHouseholdMember": "Save household member", - "application.household.member.subTitle": "You will have an opportunity to add more household members on the next screen", - "application.household.member.title": "Tell us about this person", + "application.household.member.subTitle": "You will have an opportunity to add more household members on the next screen.", + "application.household.member.title": "Tell us about this person.", "application.household.member.updateHouseholdMember": "Update Household Member", "application.household.member.whatIsTheirRelationship": "What is their relationship to you?", "application.household.member.whatReletionship": "What is their relationship to you", @@ -175,11 +182,12 @@ "application.household.preferredUnit.options.studio": "Studio", "application.household.preferredUnit.options.threeBdrm": "3 Bedroom", "application.household.preferredUnit.options.twoBdrm": "2 Bedroom", - "application.household.preferredUnit.optionsLabel": "Check all that apply:", + "application.household.preferredUnit.optionsLabel": "Check all that apply", "application.household.preferredUnit.preferredUnitType": "Preferred Unit Type", - "application.household.preferredUnit.subTitle": "Although unit sizes will typically be based on occupancy, please provide your preferred unit size for determining your preference in this opportunity or establishing a waitlist (for this opportunity only)", + "application.household.preferredUnit.subTitle": "Although unit sizes will typically be based on occupancy, please provide your preferred unit size for determining your preference in this opportunity or establishing a waitlist (for this opportunity only).", "application.household.preferredUnit.title": "What unit sizes are you interested in?", "application.household.primaryApplicant": "Primary Applicant", + "application.name.dobHelper": "For example: 01 19 2000", "application.name.emailPrivacy": "We will only use your email address to contact you about your application.", "application.name.firstName": "First Name", "application.name.lastName": "Last Name", @@ -449,6 +457,7 @@ "application.review.terms.textSubmissionDate": "This application must be submitted by %{applicationDueDate}.", "application.review.terms.fcfs.text": "* Applicants are applying to currently vacant apartments on a first come, first serve basis.\n\n* Eligible applicants will be contacted on a first come first serve basis until vacancies are filled.\n\n* If you are contacted for an interview, you will be asked to fill out a more detailed application and provide supporting documents.\n\n* All of the information that you have provided will be verified and your eligibility confirmed.\n\n* Your application may be removed if you have made any fraudulent statements.\n\n* For properties with housing preferences, if we cannot verify a housing preference that you have claimed, you will not receive the preference but will not be otherwise penalized.\n\nFor more information, please contact the housing developer or property manager posted in the listing.\n\nCompleting this 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.\n\nYou cannot change your online application after you submit.\n\nI 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 application process.", "application.review.terms.lottery.text": "* Applicants are applying to enter a lottery for currently vacant apartments.\n\n* Once the application period closes, eligible applicants will be placed in lottery rank order.\n\n* If you are contacted for an interview, you will be asked to fill out a more detailed application and provide supporting documents.\n\n* All of the information that you have provided will be verified and your eligibility confirmed.\n\n* Your application may be removed if you have made any fraudulent statements.\n\n* For properties with housing preferences, if we cannot verify a housing preference that you have claimed, you will not receive the preference but will not be otherwise penalized.\n\nFor more information, please contact the housing developer or property manager posted in the listing.\n\nCompleting this 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.\n\nYou cannot change your online application after you submit.\n\nI 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 application process.", + "application.review.terms.submittingApplication": "Submitting application", "application.review.terms.waitlist.text": "* Applicants are applying for an open waitlist and not a currently vacant apartment.\n\n* When vacancies become available, eligible applicants will be contacted by the property manager on a first come, first serve basis.\n\n* If you are contacted for an interview, you will be asked to fill out a more detailed application and provide supporting documents.\n\n* All of the information that you have provided will be verified and your eligibility confirmed.\n\n* Your application may be removed if you have made any fraudulent statements.\n\n* For properties with housing preferences, if we cannot verify a housing preference that you have claimed, you will not receive the preference but will not be otherwise penalized.\n\n* You may be contacted while on the waitlist to confirm that you wish to remain on the waitlist.\n\nFor more information, please contact the housing developer or property manager posted in the listing.\n\nCompleting this 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.\n\nYou cannot change your online application after you submit.\n\nI 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 application process.", "application.review.terms.title": "Terms", "application.review.voucherOrSubsidy": "Housing Voucher or Rental Subsidy", @@ -585,7 +594,9 @@ "errors.dateOfBirthErrorAge": "Please enter a valid Date of Birth, must be 18 or older", "errors.emailAddressError": "Please enter an email address", "errors.errorsToResolve": "There are errors you'll need to resolve before moving on.", + "errors.familyNameError": "Please enter a Family Name", "errors.firstNameError": "Please enter a First Name", + "errors.givenNameError": "Please enter a Given Name", "errors.householdTooBig": "Your household size is too big.", "errors.householdTooSmall": "Your household size is too small.", "errors.lastNameError": "Please enter a Last Name", @@ -607,7 +618,7 @@ "errors.stateError": "Please enter a state", "errors.streetError": "Please enter an address", "errors.timeError": "Please enter a valid time", - "errors.zipCodeError": "Please enter a zipcode", + "errors.zipCodeError": "Please enter a zip code", "filter.goToDahlia": "For San Francisco listings, go to ", "filter.goToDahliaLink": "DAHLIA", "footer.accessibilityStatement": "Accessibility Statement", @@ -1315,7 +1326,7 @@ "t.petsPolicy": "Pets Policy", "t.phone": "Phone", "t.phoneNumberPlaceholder": "(555) 555-5555", - "t.pleaseSelectOne": "Please select one.", + "t.pleaseSelectOne": "Please select one", "t.pleaseSelectYesNo": "Please select yes or no.", "t.pm": "PM", "t.preferNotToSay": "Prefer not to say", diff --git a/shared-helpers/src/locales/tl.json b/shared-helpers/src/locales/tl.json index 43c7b370ea..232817adbd 100644 --- a/shared-helpers/src/locales/tl.json +++ b/shared-helpers/src/locales/tl.json @@ -39,7 +39,7 @@ "application.alternateContact.contact.description": "Gagamitin lamang namin ang impormasyong ito para kontakin sila tungkol sa iyong application.", "application.alternateContact.contact.emailAddressFormLabel": "Email address ng Contact", "application.alternateContact.contact.phoneNumberFormLabel": "Numero ng telepono ng contact", - "application.alternateContact.contact.title": "Ipaalam sa ami kung paano matatawagan ang iyong kahaliling contact", + "application.alternateContact.contact.title": "Ipaalam sa ami kung paano matatawagan ang iyong kahaliling contact.", "application.alternateContact.name.alternateContactFormLabel": "Pangalan ng kahaliling contact", "application.alternateContact.name.caseManagerAgencyFormLabel": "Saan nagtatrabaho ang iyong case manager o tagapayo sa pabahay?", "application.alternateContact.name.caseManagerAgencyFormPlaceHolder": "Ahensiya", @@ -79,8 +79,11 @@ "application.contact.couldntLocateAddress": "Hindi namin mahanap ang lugar na iyong inilagay. Pakikumpirmahin kung tama ito.", "application.contact.doYouWorkInDescription": "TBD", "application.contact.doYouWorkIn": "Nagtatrabaho ka ba sa %{county} County?", + "application.contact.familyName": "Apelyido", + "application.contact.givenName": "Ibinigay na pangalan", "application.contact.mailingAddress": "Mailing Address", "application.contact.noPhoneNumber": "Wala akong numero ng telepono", + "application.contact.number.subNote": "10-digit, halimbawa 999-999-9999", "application.contact.phoneNumberTypes.cell": "Cell", "application.contact.phoneNumberTypes.home": "Home", "application.contact.phoneNumberTypes.prompt": "Anong uri ng numero ito?", @@ -91,14 +94,15 @@ "application.contact.state": "State", "application.contact.streetAddress": "Street Address", "application.contact.suggestedAddress": "Iminungkahing Address:", - "application.contact.title": "Salamat %{firstName}. Ngayon ay kailangang malaman namin kung paano ka makokontak.", + "application.contact.title": "Salamat, %{firstName}. Ngayon ay kailangan naming malaman kung paano makipag-ugnayan sa iyo tungkol sa iyong aplikasyon.", "application.contact.verifyAddressTitle": "Nahanap namin ang sumusunod na address. Pakikumpirma kung tama ito.", "application.contact.workAddress": "Address ng Trabaho", "application.contact.youEntered": "Inilagay Mo ay:", "application.contact.yourAdditionalPhoneNumber": "Iyong Pangalawang Numero ng Telepono", + "application.contact.yourAddress": "Ang iyong Address", "application.contact.yourPhoneNumber": "Ang Iyong Numero ng Telepono", "application.contact.zipCode": "Zipcode", - "application.contact.zip": "Zip", + "application.contact.zip": "Zip Code", "application.details.adaPriorities": "Napiling ADA Priorities", "application.edited": "Binago", "application.financial.income.instruction1": "Idagdag ang iyong kabuuang (bago ang buwis) na kita ng sambahayan mula sa sahod_ benepisyo at iba pang pinagkukunan mula sa lahat ng miyembro ng sambahayan.", @@ -151,7 +155,7 @@ "application.household.householdStudent.question": "Mayroon ba sa iyong sambahayan na isang full time na estudyante o magiging 18 taong gulang sa loob ng 60 araw?", "application.household.householdStudent.title": "Kasama sa Sambahayan ang Mag-aaral o Miyembro na Malapit nang Mag-18", "application.household.liveAlone.liveWithOtherPeople": "Iba pang mga tao na kasama kong nakatira", - "application.household.liveAlone.title": "Susunod ay gusto naming malaman ang tungkol sa iba pang titira na kasama mo sa unit", + "application.household.liveAlone.title": "Susunod ay gusto naming malaman ang tungkol sa iba pang titira na kasama mo sa unit.", "application.household.liveAlone.willLiveAlone": "Mag-isa akong titira", "application.household.member.cancelAddingThisPerson": "Kanselahin ang pagdagdag sa taong ito", "application.household.member.dateOfBirth": "Petsa ng Kapanganakan", @@ -159,8 +163,8 @@ "application.household.member.haveSameAddress": "Kapareho ba nila ang address mo?", "application.household.member.name": "Pangalan ng miyembro ng sambahayan", "application.household.member.saveHouseholdMember": "I-save ang miyembro ng sambahayan", - "application.household.member.subTitle": "Magkakaroon ka ng oportunidad na magdagdag ng mga miyembro ng sambahayan sa susunod na screen", - "application.household.member.title": "Sabihin sa amin ang tungkol sa taong ito", + "application.household.member.subTitle": "Magkakaroon ka ng oportunidad na magdagdag ng mga miyembro ng sambahayan sa susunod na screen.", + "application.household.member.title": "Sabihin sa amin ang tungkol sa taong ito.", "application.household.member.updateHouseholdMember": "I-update ang Miyembro ng Household", "application.household.member.whatIsTheirRelationship": "Ano ang kanilang relasyon sa iyo?", "application.household.member.whatReletionship": "Ano ang kanilang relasyon sa iyo", @@ -175,11 +179,12 @@ "application.household.preferredUnit.options.studio": "Studio", "application.household.preferredUnit.options.threeBdrm": "3 Kwarto", "application.household.preferredUnit.options.twoBdrm": "2 Kwarto", - "application.household.preferredUnit.optionsLabel": "Tingnan ang lahat ng naaangkop:", + "application.household.preferredUnit.optionsLabel": "Tingnan ang lahat ng naaangkop", "application.household.preferredUnit.preferredUnitType": "Napiling Uri ng Unit", - "application.household.preferredUnit.subTitle": "Bagama't ang mga sukat ng unit ay karaniwang ibabatay sa nakatira_ mangyaring ibigay ang iyong gustong laki ng unit para sa pagtukoy ng iyong kagustuhan sa pagkakataong ito o pagbuo ng waitlist (para sa pagkakataong ito lamang)", + "application.household.preferredUnit.subTitle": "Bagama't ang mga sukat ng unit ay karaniwang ibabatay sa nakatira_ mangyaring ibigay ang iyong gustong laki ng unit para sa pagtukoy ng iyong kagustuhan sa pagkakataong ito o pagbuo ng waitlist (para sa pagkakataong ito lamang).", "application.household.preferredUnit.title": "Sa anong mga sukat ng unit ka interesado?", "application.household.primaryApplicant": "Pangunahing Aplikante", + "application.name.dobHelper": "Halimbawa: 01 19 2000", "application.name.emailPrivacy": "Gagamitin lang namin ang iyong email address para makipag-ugnayan sa iyo tungkol sa iyong application.", "application.name.firstName": "Pangalan", "application.name.lastName": "Apelyido", @@ -379,7 +384,9 @@ "errors.dateOfBirthError": "Pakilagay ang tamang Petsa ng Kapanganakan", "errors.emailAddressError": "Pakilagay ang email address", "errors.errorsToResolve": "May mga problema na gusto mong resolbahin bago magpatuloy.", + "errors.familyNameError": "Mangyaring maglagay ng Pangalan ng Pamilya", "errors.firstNameError": "Pakilagay ang pangalan", + "errors.givenNameError": "Mangyaring maglagay ng Ibinigay na Pangalan", "errors.householdTooBig": "Napakalaki ng iyong sambahayan.", "errors.householdTooSmall": "Napakaliit ng iyong sambahayan.", "errors.lastNameError": "Pakilagay ang Apelyido", @@ -982,7 +989,7 @@ "t.petsPolicy": "Patakaran sa mga Alagang Hayop", "t.phoneNumberPlaceholder": "(555) 555-5555", "t.phone": "Telepono", - "t.pleaseSelectOne": "Pumili ng isa.", + "t.pleaseSelectOne": "Pumili ng isa", "t.pleaseSelectYesNo": "Pakipiliin ang oo o hindi.", "t.pm": "PM", "t.preferNotToSay": "Piniling hindi sabihin", diff --git a/shared-helpers/src/locales/vi.json b/shared-helpers/src/locales/vi.json index 7d598fa270..610487c5a2 100644 --- a/shared-helpers/src/locales/vi.json +++ b/shared-helpers/src/locales/vi.json @@ -39,7 +39,7 @@ "application.alternateContact.contact.description": "Chúng tôi sẽ chỉ sử dụng thông tin này để liên lạc với họ về đơn ghi danh của quý vị.", "application.alternateContact.contact.emailAddressFormLabel": "Địa chỉ email của người liên lạc", "application.alternateContact.contact.phoneNumberFormLabel": "Số điện thoại của người liên lạc", - "application.alternateContact.contact.title": "Hãy cho chúng tôi biết cách để liên lạc với người liên lạc thay thế của quý vị", + "application.alternateContact.contact.title": "Hãy cho chúng tôi biết cách để liên lạc với người liên lạc thay thế của quý vị.", "application.alternateContact.name.alternateContactFormLabel": "Tên người liên lạc thay thế", "application.alternateContact.name.caseManagerAgencyFormLabel": "Quản lý hồ sơ hoặc cố vấn nhà ở của quý vị công tác tại đâu?", "application.alternateContact.name.caseManagerAgencyFormPlaceHolder": "Cơ quan", @@ -79,8 +79,11 @@ "application.contact.couldntLocateAddress": "Chúng tôi không thể tìm thấy địa chỉ quý vị đã nhập. Vui lòng xác nhận địa chỉ chính xác.", "application.contact.doYouWorkInDescription": "Chưa xác định", "application.contact.doYouWorkIn": "Quý vị có làm việc tại không?", + "application.contact.familyName": "Tên gia đình", + "application.contact.givenName": "Tên", "application.contact.mailingAddress": "Địa chỉ Nhận thư", "application.contact.noPhoneNumber": "Tôi không có số điện thoạii", + "application.contact.number.subNote": "10 chữ số, ví dụ 999-999-9999", "application.contact.phoneNumberTypes.cell": "Di động", "application.contact.phoneNumberTypes.home": "Nhà riêng", "application.contact.phoneNumberTypes.prompt": "Kiểu số điện thoại này là gì?", @@ -91,14 +94,15 @@ "application.contact.state": "Tiểu bang", "application.contact.streetAddress": "Địa chỉ Đường phố", "application.contact.suggestedAddress": "Địa chỉ đề xuất:", - "application.contact.title": "Cảm ơn %{firstName}. Bây giờ chúng tôi cần biết cách để liên lạc với quý vị.", + "application.contact.title": "Cảm ơn, %{firstName}. Bây giờ chúng tôi cần biết cách liên hệ với bạn về đơn đăng ký của bạn.", "application.contact.verifyAddressTitle": "Chúng tôi đã xác định được địa chỉ sau. Vui lòng xác nhận địa chỉ chính xác.", "application.contact.workAddress": "Địa chỉ Sở Làm", "application.contact.youEntered": "Quý vị đã nhập:", "application.contact.yourAdditionalPhoneNumber": "Số điện thoại thứ hai của quý vị", + "application.contact.yourAddress": "Địa chỉ của bạn", "application.contact.yourPhoneNumber": "Số Điện thoại của Quý vị", "application.contact.zipCode": "Số zipcode", - "application.contact.zip": "Mã Zip", + "application.contact.zip": "Mã Bưu Chính", "application.details.adaPriorities": "Ưu tiên ADA đã chọn", "application.edited": "Đã chỉnh sửa", "application.financial.income.instruction1": "Tính tổng thu nhập hộ gia đình (trước thuế) từ tiền lương, tiền trợ cấp và các nguồn khác từ tất cả các thành viên trong hộ gia đình.", @@ -151,7 +155,7 @@ "application.household.householdStudent.question": "Có ai đó trong hộ gia đình quý vị là sinh viên chính quy hoặc sắp đủ 18 tuổi trong vòng 60 ngày không?", "application.household.householdStudent.title": "Hộ Gia Đình Có Sinh Viên hoặc Thành Viên Sắp 18 Tuổi", "application.household.liveAlone.liveWithOtherPeople": "Những người khác sẽ sống cùng tôi", - "application.household.liveAlone.title": "Tiếp theo, chúng tôi muốn biết về những người khác sẽ sống trong căn nhà cùng quý vị", + "application.household.liveAlone.title": "Tiếp theo, chúng tôi muốn biết về những người khác sẽ sống trong căn nhà cùng quý vị.", "application.household.liveAlone.willLiveAlone": "Tôi sẽ sống một mình", "application.household.member.cancelAddingThisPerson": "Hủy bỏ việc thêm người này", "application.household.member.dateOfBirth": "Ngày sinh", @@ -159,8 +163,8 @@ "application.household.member.haveSameAddress": "Họ có cùng địa chỉ với quý vị không?", "application.household.member.name": "Tên của thành viên hộ gia đình", "application.household.member.saveHouseholdMember": "Lưu thành viên hộ gia đình", - "application.household.member.subTitle": "Quý vị sẽ có cơ hội thêm nhiều thành viên hộ gia đình hơn trong màn hình tiếp theo", - "application.household.member.title": "Hãy cho chúng tôi biết về người này", + "application.household.member.subTitle": "Quý vị sẽ có cơ hội thêm nhiều thành viên hộ gia đình hơn trong màn hình tiếp theo.", + "application.household.member.title": "Hãy cho chúng tôi biết về người này.", "application.household.member.updateHouseholdMember": "Cập nhật Thành viên Hộ Gia đình", "application.household.member.whatIsTheirRelationship": "Quan hệ của người đó với quý vị là gì?", "application.household.member.whatReletionship": "Quan hệ của người đó với quý vị là gì", @@ -175,11 +179,12 @@ "application.household.preferredUnit.options.studio": "Một phòng kiểu studio", "application.household.preferredUnit.options.threeBdrm": "3 Phòng ngủ", "application.household.preferredUnit.options.twoBdrm": "2 Phòng ngủ", - "application.household.preferredUnit.optionsLabel": "Đánh dấu tất cả các lựa chọn phù hợp:", + "application.household.preferredUnit.optionsLabel": "Đánh dấu tất cả các lựa chọn phù hợp", "application.household.preferredUnit.preferredUnitType": "Loại Căn nhà Ưa thích", - "application.household.preferredUnit.subTitle": "Mặc dù các kích thước nhà ở thường dựa vào mức độ sử dụng nhưng vui lòng cho biết kích thước nhà ở mà quý vị yêu thích để xác định ưu tiên của quý vị trong cơ hội này hoặc lập danh sách chờ (chỉ dành cho cơ hội này)", + "application.household.preferredUnit.subTitle": "Mặc dù các kích thước nhà ở thường dựa vào mức độ sử dụng nhưng vui lòng cho biết kích thước nhà ở mà quý vị yêu thích để xác định ưu tiên của quý vị trong cơ hội này hoặc lập danh sách chờ (chỉ dành cho cơ hội này).", "application.household.preferredUnit.title": "Kích thước căn nhà mà quý vị quan tâm là gì?", "application.household.primaryApplicant": "Người nộp đơn Chính", + "application.name.dobHelper": "Ví dụ: 01 19 2000", "application.name.emailPrivacy": "Chúng tôi sẽ chỉ sử dụng địa chỉ email của quý vị để liên lạc với quý vị về việc ghi danh của quý vị.", "application.name.firstName": "Tên", "application.name.lastName": "Họ", @@ -426,7 +431,9 @@ "errors.dateOfBirthError": "Vui lòng nhập Ngày sinh hợp lệ", "errors.emailAddressError": "Vui lòng nhập địa chỉ email", "errors.errorsToResolve": "Quý vị cần giải quyết những lỗi này trước khi chuyển sang bước tiếp.", + "errors.familyNameError": "Vui lòng nhập Họ Tên", "errors.firstNameError": "Vui lòng nhập Tên", + "errors.givenNameError": "Vui lòng nhập Tên riêng", "errors.householdTooBig": "Quy mô hộ gia đình của quý vị quá lớn.", "errors.householdTooSmall": "Quy mô hộ gia đình của quý vị quá nhỏ.", "errors.lastNameError": "Vui lòng nhập Họ", @@ -1038,7 +1045,7 @@ "t.petsPolicy": "Chính sách đối với Vật nuôi", "t.phoneNumberPlaceholder": "(555) 555-5555", "t.phone": "Điện thoại", - "t.pleaseSelectOne": "Vui lòng chọn một.", + "t.pleaseSelectOne": "Vui lòng chọn một", "t.pleaseSelectYesNo": "Vui lòng chọn có hoặc không.", "t.pm": "Chiều", "t.preferNotToSay": "Không muốn nói", diff --git a/shared-helpers/src/locales/zh.json b/shared-helpers/src/locales/zh.json index e3d0c4379c..7b3461e52e 100644 --- a/shared-helpers/src/locales/zh.json +++ b/shared-helpers/src/locales/zh.json @@ -39,7 +39,7 @@ "application.alternateContact.contact.description": "我們只會為您的申請事宜使用此資料聯絡他們。", "application.alternateContact.contact.emailAddressFormLabel": "聯絡人電郵地址", "application.alternateContact.contact.phoneNumberFormLabel": "聯絡人電話號碼", - "application.alternateContact.contact.title": "請告知我們如何接觸您的其他聯絡人", + "application.alternateContact.contact.title": "請告知我們如何接觸您的其他聯絡人。", "application.alternateContact.name.alternateContactFormLabel": "其他聯絡人姓名", "application.alternateContact.name.caseManagerAgencyFormLabel": "您的個案經理或房屋顧問在哪裡工作?", "application.alternateContact.name.caseManagerAgencyFormPlaceHolder": "機構", @@ -79,8 +79,11 @@ "application.contact.couldntLocateAddress": "我們找不到您輸入的地址。 請確認這是正確地址。", "application.contact.doYouWorkInDescription": "待定", "application.contact.doYouWorkIn": "您是否在 工作?", + "application.contact.familyName": "姓", + "application.contact.givenName": "給定的名稱", "application.contact.mailingAddress": "郵寄地址", "application.contact.noPhoneNumber": "我沒有電話號碼", + "application.contact.number.subNote": "10 位數字,例如 999-999-9999", "application.contact.phoneNumberTypes.cell": "手機", "application.contact.phoneNumberTypes.home": "住家", "application.contact.phoneNumberTypes.prompt": "這是哪類電話號碼?", @@ -91,11 +94,12 @@ "application.contact.state": "州", "application.contact.streetAddress": "街道地址", "application.contact.suggestedAddress": "建議的地址:", - "application.contact.title": "%{firstName},謝謝您。現在我們需要知道如何聯絡您。", + "application.contact.title": "謝謝,%{firstName}。現在我們需要知道如何就您的申請與您聯繫。", "application.contact.verifyAddressTitle": "我們已找到下列地址。請確認這是正確地址。", "application.contact.workAddress": "工作地址", "application.contact.youEntered": "您已輸入:", "application.contact.yourAdditionalPhoneNumber": "您的第二電話號碼", + "application.contact.yourAddress": "你的地址", "application.contact.yourPhoneNumber": "您的電話號碼", "application.contact.zipCode": "郵遞區號", "application.contact.zip": "郵遞區號", @@ -145,13 +149,13 @@ "application.household.dontQualifyInfo": "如果您認為自己可能填寫錯誤,請更改資料。請注意,如果您偽造任何申請資料,您將會被取消資格。如果您填寫的資料正確無誤,我們建議您日後再回來查看,因為會有更多可供申請的物業。", "application.household.expectingChanges.question": "在未來12個月中,您預期您的住家會發生什麼變化嗎,例如家庭人數?", "application.household.expectingChanges.title": "預計家庭變化", - "application.household.genericSubtitle": "如果您的申請被選中,請準備提供必要文件。 ", + "application.household.genericSubtitle": "如果您的申請被選中,請準備提供必要文件。", "application.household.householdMember": "家庭成員(一人)", "application.household.householdMembers": "家庭成員(多人)", "application.household.householdStudent.question": "您的家人是否有全日制學生或在 60 天內年滿 18 歲?", "application.household.householdStudent.title": "家人包括接近 18 歲的學生或成員", "application.household.liveAlone.liveWithOtherPeople": "其他將會與我同住的人", - "application.household.liveAlone.title": "接着,我們想知道將會與您同住一個單位的其他人", + "application.household.liveAlone.title": "接着,我們想知道將會與您同住一個單位的其他人。", "application.household.liveAlone.willLiveAlone": "我將會獨居", "application.household.member.cancelAddingThisPerson": "取消加入此人", "application.household.member.dateOfBirth": "出生日期", @@ -159,8 +163,8 @@ "application.household.member.haveSameAddress": "他們的地址是否和您一樣?", "application.household.member.name": "家庭成員姓名", "application.household.member.saveHouseholdMember": "儲存家庭成員資料", - "application.household.member.subTitle": "在下一個頁面,您將有機會加入更多家庭成員", - "application.household.member.title": "請提供此人的資料", + "application.household.member.subTitle": "在下一個頁面,您將有機會加入更多家庭成員。", + "application.household.member.title": "請提供此人的資料。", "application.household.member.updateHouseholdMember": "更新家庭成員資料", "application.household.member.whatIsTheirRelationship": "他們與您是什麼關係?", "application.household.member.whatReletionship": "他們與您是什麼關係?", @@ -175,11 +179,12 @@ "application.household.preferredUnit.options.studio": "套房", "application.household.preferredUnit.options.threeBdrm": "3 臥室", "application.household.preferredUnit.options.twoBdrm": "2 間臥室", - "application.household.preferredUnit.optionsLabel": "請勾選所有適用的單位:", + "application.household.preferredUnit.optionsLabel": "請勾選所有適用的單位", "application.household.preferredUnit.preferredUnitType": "首選單位類型", - "application.household.preferredUnit.subTitle": "雖然單位尺寸通常根據入住率分配,但請提供您的首選單位尺寸,以確定您在此機會的偏好或建立候補名單(僅針對此機會)", + "application.household.preferredUnit.subTitle": "雖然單位尺寸通常根據入住率分配,但請提供您的首選單位尺寸,以確定您在此機會的偏好或建立候補名單(僅針對此機會)。", "application.household.preferredUnit.title": "您對哪類單位面積感興趣?", "application.household.primaryApplicant": "主要申請人", + "application.name.dobHelper": "例如:2000 年 1 月 19 日", "application.name.emailPrivacy": "我們只會為申請事宜使用您的電郵地址與您聯絡。", "application.name.firstName": "名字", "application.name.lastName": "姓氏", @@ -426,7 +431,9 @@ "errors.dateOfBirthError": "請輸入有效的出生日期", "errors.emailAddressError": "請輸入電郵地址", "errors.errorsToResolve": "出現一些錯誤;您需要解決問題才能繼續操作。", + "errors.familyNameError": "請輸入姓氏", "errors.firstNameError": "請輸入名字", + "errors.givenNameError": "請輸入名字", "errors.householdTooBig": "您的家庭人數過多。", "errors.householdTooSmall": "您的家庭人數過少。", "errors.lastNameError": "請輸入姓氏", @@ -1042,7 +1049,7 @@ "t.petsPolicy": "寵物政策", "t.phoneNumberPlaceholder": "(555) 555-5555", "t.phone": "電話號碼", - "t.pleaseSelectOne": "請選取一項。", + "t.pleaseSelectOne": "請選取一項", "t.pleaseSelectYesNo": "請選擇是或否。", "t.pm": "下午", "t.preferNotToSay": "保密", diff --git a/shared-helpers/src/views/address/FormAddressAlternate.tsx b/shared-helpers/src/views/address/FormAddressAlternate.tsx new file mode 100644 index 0000000000..73367403f9 --- /dev/null +++ b/shared-helpers/src/views/address/FormAddressAlternate.tsx @@ -0,0 +1,102 @@ +import { UseFormMethods } from "react-hook-form" +import { Field, resolveObject, Select, t } from "@bloom-housing/ui-components" +import React from "react" + +type FormAddressProps = { + subtitle?: string + dataKey: string + register: UseFormMethods["register"] + errors?: UseFormMethods["errors"] + required?: boolean + stateKeys: string[] +} + +export const FormAddressAlternate = ({ + subtitle, + dataKey, + register, + errors, + required, + stateKeys, +}: FormAddressProps) => { + return ( + <> + + {!subtitle ? t("application.preferences.options.address") : subtitle} + + + + + +
+ + +