Skip to content

Commit

Permalink
Merge pull request #186 from dimak1/fea/business-lic-exp-date
Browse files Browse the repository at this point in the history
Add Business Licence Expiry Date
  • Loading branch information
dimak1 authored Oct 17, 2024
2 parents 6169d46 + 4948964 commit 7c965e8
Show file tree
Hide file tree
Showing 18 changed files with 184 additions and 12 deletions.
9 changes: 6 additions & 3 deletions strr-web/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,19 @@ export default defineAppConfig({
help: 'text-gray-700 text-xs'
},
input: {
base: 'bg-gray-100 hover:bg-gray-200 h-[56px] border-b-[1px] focus:border-b-2 focus:ring-0',
base: 'bg-gray-100 hover:bg-gray-100 h-[56px] border-b-[1px] focus:border-b-2 focus:ring-0',
rounded: 'rounded-none rounded-t-md',
placeholder: 'placeholder-gray-600',
variant: {
bcGov: 'border-gray-700 placeholder-gray-700 focus:border-primary-500 focus:placeholder-primary-500',
error: 'border-red-500 focus:border-red-500 placeholder-red-500 focus:placeholder-red-500',
primary: 'border-primary-500 placeholder-primary-500 border-b-2'
}
},
select: {
base: 'bg-gray-100 hover:bg-gray-200 h-[56px] border-b-[1px] focus:border-b-2 focus:ring-0',
base: 'bg-gray-100 hover:bg-gray-100 h-[56px] border-b-[1px] focus:border-b-2 focus:ring-0',
rounded: 'rounded-none rounded-t-md',
placeholder: 'placeholder-gray-600',
variant: {
bcGov: 'border-gray-700',
error: 'border-red-500'
Expand All @@ -52,8 +54,9 @@ export default defineAppConfig({
}
},
textarea: {
base: 'bg-gray-100 hover:bg-gray-200 border-b-[1px] focus:border-b-2 h-20 focus:ring-0 text-gray-900',
base: 'bg-gray-100 hover:bg-gray-100 border-b-[1px] focus:border-b-2 h-20 focus:ring-0 text-gray-900',
rounded: 'rounded-none rounded-t-md',
placeholder: 'placeholder-gray-600',
variant: {
bcGov: 'border-gray-700 placeholder-gray-700 focus:border-primary-500 focus:placeholder-primary-500',
error: 'border-red-500 focus:border-red-500 placeholder-red-500 focus:placeholder-red-500'
Expand Down
30 changes: 29 additions & 1 deletion strr-web/components/bcros/form-section/property/Details.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,26 @@
</template>
</UFormGroup>
</div>
<div
v-if="businessLicense"
class="flex flex-row justify-between w-full mb-[40px] mobile:mb-[16px]"
>
<UFormGroup name="businessLicenseExpiryDate" class="d:pr-[16px] flex-grow">
<UInput
v-model="businessLicenseExpiryDate"
:placeholder="t('createAccount.propertyForm.businessLicenseExpiryDate')"
type="date"
:min="new Date().toISOString().split('T')[0]"
:max="new Date('2999-12-31').toISOString().split('T')[0]"
:ui="{ base: 'uppercase' }"
@blur="emit('validateBusinessLicenseExpiryDate')"
@change="emit('validateBusinessLicenseExpiryDate')"
/>
<template #help>
{{ t('createAccount.propertyForm.businessLicenseExpiryDateHelp') }}
</template>
</UFormGroup>
</div>
<div class="flex flex-row justify-between w-full mb-[40px] mobile:mb-[16px]">
<UFormGroup name="propertyType" class="d:pr-[16px] flex-grow" :error="propertyTypeError">
<USelect
Expand Down Expand Up @@ -83,8 +103,16 @@ const propertyType = defineModel<string>('propertyType')
const ownershipType = defineModel<string>('ownershipType')
const businessLicense = defineModel<string>('businessLicense')
const parcelIdentifier = defineModel<string>('parcelIdentifier')
const businessLicenseExpiryDate = defineModel<string>('businessLicenseExpiryDate')
watch(businessLicense, (): void => {
if (!businessLicense.value) {
// clear exp date when business lic is empty
formState.propertyDetails.businessLicenseExpiryDate = ''
}
})
const emit = defineEmits(['validateOwnership', 'validateProperty'])
const emit = defineEmits(['validateOwnership', 'validateProperty', 'validateBusinessLicenseExpiryDate'])
const {
propertyTypes,
Expand Down
9 changes: 9 additions & 0 deletions strr-web/components/bcros/form-section/property/Form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@
v-model:ownership-type="formState.propertyDetails.ownershipType"
v-model:business-license="formState.propertyDetails.businessLicense"
v-model:parcel-identifier="formState.propertyDetails.parcelIdentifier"
v-model:business-license-expiry-date="formState.propertyDetails.businessLicenseExpiryDate"
:property-types="propertyTypes"
:ownership-types="ownershipTypes"
:ownership-type-error="ownershipTypeError"
:property-type-error="propertyTypeError"
@validate-ownership="validateOwnershipType"
@validate-property="validatePropertyType"
@validate-business-license-expiry-date="validateBusinessLicenseExpiryDate"
/>
<BcrosFormSectionPropertyListingDetails
v-model:listing-details="formState.propertyDetails.listingDetails"
Expand Down Expand Up @@ -166,6 +168,7 @@ const ownershipTypes: string[] = [
const propertyTypeError = ref('')
const ownershipTypeError = ref('')
const businessLicenseExpiryDate = ref('')
const validatePropertyType = () => {
const parsed = propertyDetailsSchema.safeParse(formState.propertyDetails).error?.errors
Expand All @@ -179,6 +182,12 @@ const validateOwnershipType = () => {
ownershipTypeError.value = error ? error.message : ''
}
const validateBusinessLicenseExpiryDate = () => {
const parsed = propertyDetailsSchema.safeParse(formState.propertyDetails).error?.errors
const error = parsed?.find(error => error.path.includes('businessLicenseExpiryDate'))
businessLicenseExpiryDate.value = error ? error.message : ''
}
const form = ref()
watch(form, () => {
Expand Down
15 changes: 10 additions & 5 deletions strr-web/components/bcros/form-section/review/Form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@
:primary="false"
/>
</div>
<div class="mt-[48px]">
<div class="mt-[48px]" data-test-id="rental-unit-review">
<p class="font-bold mb-[24px] mobile:mx-[8px]">
{{ tReview('rentalUnitInfo') }}
</p>
<div class="bg-white py-[22px] px-[30px] mobile:px-[8px]">
<div class="flex flex-row justify-between w-full desktop:mb-[24px] mobile:flex-col">
<BcrosFormSectionReviewItem
:title="tReview('nickname')"
:content="formState.propertyDetails.nickname ?? '-'"
:content="formState.propertyDetails.nickname || '-'"
/>
<BcrosFormSectionReviewItem
:title="tReview('businessLicense')"
:content="formState.propertyDetails.businessLicense ?? '-'"
:content="formState.propertyDetails.businessLicense || '-'"
/>
<BcrosFormSectionReviewItem
:title="tReview('ownershipType')"
Expand All @@ -62,11 +62,16 @@
}}
</p>
</BcrosFormSectionReviewItem>
<BcrosFormSectionReviewItem
v-if="formState.propertyDetails.businessLicenseExpiryDate"
:title="tReview('businessLicenseExpiryDate')"
:content="convertDateToLongFormat(formState.propertyDetails.businessLicenseExpiryDate)"
/>
<BcrosFormSectionReviewItem
:title="tReview('propertyType')"
:content="formState.propertyDetails.propertyType ?? '-'"
/>
<div class="flex-1" />
<div v-if="!formState.propertyDetails.businessLicenseExpiryDate" class="flex-1" />
</div>
<div
v-if="
Expand Down Expand Up @@ -154,7 +159,7 @@
<UCheckbox
v-model="formState.principal.agreeToSubmit"
:label="tReview('confirm')"
:class="`${isComplete && !formState.principal.agreeToSubmit ? 'outline outline-bcGovColor-error' : ''}`"
:ui="{ label: isComplete && !formState.principal.agreeToSubmit ? 'text-bcGovColor-error' : '' }"
/>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions strr-web/components/bcros/stepper/Stepper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
pb-5 flex flex-col cursor-pointer
mobile:border-b-0
`"
:data-test-id="`step-index-${index}`"
@click="() => emit('changeStep', index)"
>
<div class="flex justify-center pt-[7px] ">
Expand Down
2 changes: 2 additions & 0 deletions strr-web/interfaces/account-i.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ export interface CreateAccountFormStateI {
whichPlatform: string | undefined
parcelIdentifier: string | undefined
businessLicense: string | undefined
businessLicenseExpiryDate: string | undefined
propertyType: string | undefined
ownershipType: string | undefined
nickname: string | undefined
Expand Down Expand Up @@ -227,6 +228,7 @@ export interface CreateAccountFormAPII {
unitDetails: {
parcelIdentifier?: string
businessLicense?: string
businessLicenseExpiryDate?: string
propertyType: string
ownershipType: string
}
Expand Down
1 change: 1 addition & 0 deletions strr-web/interfaces/application-i.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export interface ApplicationDetailsI {
unitDetails: {
parcelIdentifier?: string
businessLicense?: string
businessLicenseExpiryDate?: string
propertyType: string
ownershipType: string
}
Expand Down
1 change: 1 addition & 0 deletions strr-web/interfaces/registration-i.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface RegistrationI {
unitDetails: {
parcelIdentifier?: string,
businessLicense?: string,
businessLicenseExpiryDate?: string,
propertyType: string,
ownershipType: string
},
Expand Down
3 changes: 3 additions & 0 deletions strr-web/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,8 @@
"parcelIdentifierTooltip": "You can find your Parcel Identifier (PID) on your Property Assessment Notice from BC Assessment. Alternatively, visit the BC Assessment website, search for your civic address, and look for the PID under 'Legal Description and Parcel ID'.",
"businessLicense": "Local Government Business Licence (Optional)",
"businessLicenseHelp": "This is the business licence to operate a short-term rental as provided by your local government.",
"businessLicenseExpiryDate": "Business Licence Expiry Date",
"businessLicenseExpiryDateHelp": "Enter the Business Licence Expiry Date (YYYY-MM-DD)",
"propertyType": "Type of Property",
"ownershipType": "Ownership Type",
"rentalUnitDetails": "Rental Unit Details",
Expand Down Expand Up @@ -353,6 +355,7 @@
"mailingAddress": "Mailing Address",
"nickname": "Nickname",
"businessLicense": "Business License",
"businessLicenseExpiryDate": "Business License Expiry Date",
"address": "Address",
"ownershipType": "Ownership Type",
"propertyType": "Type Of Property",
Expand Down
8 changes: 8 additions & 0 deletions strr-web/stores/strr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ export const propertyDetailsSchema = z.object({
address: requiredNonEmptyString,
addressLineTwo: optionalOrEmptyString,
businessLicense: optionalOrEmptyString,
businessLicenseExpiryDate: optionalOrEmptyString,
city: requiredNonEmptyString,
country: requiredNonEmptyString,
listingDetails: listingDetailsSchema,
Expand All @@ -207,6 +208,12 @@ export const propertyDetailsSchema = z.object({
postalCode: requiredNonEmptyString,
propertyType: requiredNonEmptyString,
province: requiredNonEmptyString.refine(province => province === 'BC', { message: 'Province must be set to BC' })
}).refine((data) => {
// additional validation: businessLicenseExpiryDate is required if businessLicense present
return !data.businessLicense || (data.businessLicense && data.businessLicenseExpiryDate)
}, {
message: 'Business License Expiry Date is required',
path: ['businessLicenseExpiryDate']
})

export const formState: CreateAccountFormStateI = reactive({
Expand All @@ -215,6 +222,7 @@ export const formState: CreateAccountFormStateI = reactive({
propertyDetails: {
parcelIdentifier: undefined,
businessLicense: undefined,
businessLicenseExpiryDate: undefined,
propertyType: undefined,
ownershipType: undefined,
primaryResidence: undefined,
Expand Down
4 changes: 4 additions & 0 deletions strr-web/tests/setupTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { config } from '@vue/test-utils'
import { dataTestId } from './unit/plugins/data-test-id'

config.global.plugins.push(dataTestId)
72 changes: 72 additions & 0 deletions strr-web/tests/unit/pages/rental-application.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { mountSuspended } from '@nuxt/test-utils/runtime'
import ApplicationDetails from '@/pages/create-account.vue'
import Stepper from '~/components/bcros/stepper/Stepper.vue'
import InfoModal from '~/components/common/InfoModal.vue'
import FeeWidget from '~/components/FeeWidget.vue'
import H1 from '~/components/bcros/typography/H1.vue'
import H2 from '~/components/bcros/typography/H2.vue'
import {
BcrosFormSectionContactInformationForm,
BcrosFormSectionReviewForm,
BcrosFormSectionReviewSubsection
} from '#components'

const { t } = useTranslation()

describe('Rental Application', () => {
let wrapper: any

// go to specific step in Stepper
const goToStep = async (stepNumber: number) => {
const stepId = `[data-test-id=step-index-${stepNumber - 1}]`
wrapper.findComponent(Stepper).find(stepId).trigger('click')
await nextTick()
}

it('should render Rental Application page and form correctly', async () => {
wrapper = await mountSuspended(ApplicationDetails)

expect(wrapper.findTestId('create-account-page').exists()).toBe(true)

expect(wrapper.findAllComponents(InfoModal).length).toBe(2)
expect(wrapper.findComponent(FeeWidget).exists()).toBeTruthy()

expect(wrapper.findComponent(H1).text()).toBe(t('createAccount.title'))

const stepper = wrapper.findComponent(Stepper)
expect(stepper.exists()).toBeTruthy()

expect(wrapper.findComponent(BcrosFormSectionContactInformationForm).exists()).toBeTruthy()
expect(wrapper.findComponent(H2).text()).toContain('Step 1')

await goToStep(2)
expect(wrapper.findComponent(H2).text()).toContain('Step 2')

await goToStep(3)
expect(wrapper.findComponent(H2).text()).toContain('Step 3')

await goToStep(4)
expect(wrapper.findComponent(H2).text()).toContain('Step 4')

const reviewForm = wrapper.findComponent(BcrosFormSectionReviewForm)
expect(reviewForm.exists()).toBeTruthy()

// only one Primary Contact section is showing, Secondary Contact does not exist
expect(reviewForm.findAllComponents(BcrosFormSectionReviewSubsection).length).toBe(1)

const primaryContactReview = reviewForm.findComponent(BcrosFormSectionReviewSubsection)
expect(primaryContactReview.exists()).toBeTruthy()

// check number of fields displayed in primary contact section
const primaryContactFields = primaryContactReview.findAll('[data-test-id=form-item]')
expect(primaryContactFields.length).toBe(9)

// const rentalUnitReview = reviewForm.find('[data-test-id=rental-unit-review]')
const rentalUnitReview = reviewForm.findTestId('rental-unit-review')
expect(rentalUnitReview.exists()).toBeTruthy()

// check number of fields displayed in rental unit section
const rentalUnitReviewFields = rentalUnitReview.findAll("[data-test-id='form-item']")
expect(rentalUnitReviewFields.length).toBe(5)
})
})
20 changes: 20 additions & 0 deletions strr-web/tests/unit/plugins/data-test-id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { config } from '@vue/test-utils'

/**
* A plugin to extend VueWrapper with a utility function
* to find elements by their data-test-id attribute.
*
* Usage: wrapper.findTestId('create-account-page')
*/
export const dataTestId = (wrapper: any) => {
function findTestId (selector: string) {
const dataSelector = `[data-test-id="${selector}"]`
return wrapper.find(dataSelector)
}

return {
findTestId
}
}

config.plugins.VueWrapper.install(dataTestId)
1 change: 1 addition & 0 deletions strr-web/tests/unit/utils/formStateToApi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ it('begins with empty address', () => {
whichPlatform: 'whichPlatform',
parcelIdentifier: 'parcelIdentifier',
businessLicense: 'businessLicense',
businessLicenseExpiryDate: 'businessLicenseExpiryDate',
propertyType: 'propertyType',
ownershipType: 'ownershipType',
nickname: 'nickname',
Expand Down
2 changes: 1 addition & 1 deletion strr-web/utils/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const getOwnershipTypeDisplay = (ownershipType: string | null, t: (key: string) => string) => {
export const getOwnershipTypeDisplay = (ownershipType: string | undefined, t: (key: string) => string) => {
switch (ownershipType) {
case 'CO_OWN':
return t('coOwner')
Expand Down
3 changes: 2 additions & 1 deletion strr-web/utils/formStateToApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ export const formStateToApi = (
parcelIdentifier: formState.propertyDetails.parcelIdentifier,
propertyType,
ownershipType,
businessLicense: formState.propertyDetails.businessLicense
businessLicense: formState.propertyDetails.businessLicense,
businessLicenseExpiryDate: formState.propertyDetails.businessLicenseExpiryDate
}
}

Expand Down
13 changes: 13 additions & 0 deletions strr-web/utils/format-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,16 @@ export function formatLongDate (date: Date) {
export function formatTimeString (date: Date): string {
return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
}

/**
* Converts a date string in 'YYYY-MM-DD' format to a long date format, ignoring the timezone.
*
* @example "2024-05-20" -> "May 20, 2024"
* @param dateString - A string representing a date in 'YYYY-MM-DD' format.
* @returns A string representing the date in long format.
*/
export function convertDateToLongFormat (dateString: string): string {
const [year, month, day] = dateString.split('-').map(Number)
const date = new Date(year, month - 1, day)
return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })
}
2 changes: 1 addition & 1 deletion strr-web/vitest.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default defineVitestConfig({
domEnvironment: (process.env.VITEST_DOM_ENV as 'happy-dom' | 'jsdom') ?? 'happy-dom'
}
},
// setupFiles: ['./tests/setup/mocks.ts'],
setupFiles: ['./tests/setupTests.ts'],
globals: true
}
})

0 comments on commit 7c965e8

Please sign in to comment.