Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions strr-base-web/app/components/form/ContactInfo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ onMounted(async () => {
name-divider
:prepopulate-name="!isLoggedInAsBCeid"
:prepopulate-type="$keycloak.tokenParsed?.loginSource"
:error-details="hasFormErrors(compPartyFormRef, ['phone.countryCode', 'phone.number', 'email'])"
:error-details="hasFormErrors(compPartyFormRef, ['phone.countryCode', 'phone.number', 'emailAddress'])"
/>
</UForm>
</ConnectPageSection>
Expand All @@ -126,18 +126,26 @@ onMounted(async () => {
v-model:last-name="primaryRep.lastName"
v-model:phone="primaryRep.phone"
v-model:email-address="primaryRep.emailAddress"
v-model:confirm-email-address="primaryRep.confirmEmailAddress"
v-model:fax-number="primaryRep.faxNumber"
v-model:position="primaryRep.position"
data-testid="primary-rep"
id-prefix="platform-primary-rep"
name-divider
:prepopulate-name="isCompletingPartyRep && !isLoggedInAsBCeid"
:prepopulate-type="$keycloak.tokenParsed?.loginSource"
show-confirm-email
email-warning
:section-info="isCompletingPartyRep ? undefined : $t('strr.text.primaryContact')"
:error-name="hasFormErrors(primaryRepFormRef, ['firstName', 'middleName', 'lastName'])"
:error-details="
hasFormErrors(primaryRepFormRef, ['position', 'phone.countryCode', 'phone.number', 'email'])
hasFormErrors(primaryRepFormRef, [
'position',
'phone.countryCode',
'phone.number',
'emailAddress',
'confirmEmailAddress'
])
"
/>
</UForm>
Expand Down
12 changes: 12 additions & 0 deletions strr-base-web/app/components/form/common/Contact.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const lastName = defineModel<string>('lastName', { required: false })
const position = defineModel<string>('position', { required: false })
const faxNumber = defineModel<string>('faxNumber', { required: false })
const emailAddress = defineModel<string>('emailAddress', { required: false })
const confirmEmailAddress = defineModel<string>('confirmEmailAddress', { required: false })
const phone = defineModel<ConnectPhone>('phone', { required: false })

defineProps<{
Expand All @@ -19,6 +20,7 @@ defineProps<{
sectionInfo?: string
nameDivider?: boolean
emailWarning?: boolean
showConfirmEmail?: boolean
errorName?: boolean
errorDetails?: boolean
}>()
Expand Down Expand Up @@ -130,6 +132,16 @@ defineProps<{
:is-required="true"
type="email"
/>
<ConnectFormFieldGroup
v-if="showConfirmEmail && confirmEmailAddress !== undefined"
:id="idPrefix + '-party-confirm-email'"
v-model="confirmEmailAddress"
:aria-label="t('label.confirmEmailAddress')"
name="confirmEmailAddress"
:placeholder="t('label.confirmEmailAddress')"
:is-required="true"
type="email"
/>
<EmailAlert v-if="emailWarning" />
</div>
</ConnectFormSection>
Expand Down
1 change: 1 addition & 0 deletions strr-base-web/app/interfaces/contact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface Contact {
phone: ConnectPhone
faxNumber?: string
emailAddress: string
confirmEmailAddress?: string
}

export interface StrrContact extends Contact {
Expand Down
23 changes: 16 additions & 7 deletions strr-base-web/app/stores/strrContact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,23 @@ import type { Contact, StrrContact } from '#imports'
export const useStrrContactStore = defineStore('strr/contact', () => {
const { t } = useNuxtApp().$i18n
const { kcUser } = useKeycloak()
const getContactSchema = (completingParty = false) => {
return z.object({
const getContactSchema = (completingParty = false, requireEmailConfirmation = false) => {
const schema = z.object({
firstName: optionalOrEmptyString,
middleName: optionalOrEmptyString,
lastName: getRequiredNonEmptyString(t('validation.name.last')),
position: completingParty ? optionalOrEmptyString : getRequiredNonEmptyString(t('validation.position')),
phone: getRequiredPhone(t('validation.required'), t('validation.phone.number')),
faxNumber: optionalOrEmptyString,
emailAddress: getRequiredEmail(t('validation.email'))
emailAddress: getRequiredEmail(t('validation.email')),
confirmEmailAddress: optionalOrEmptyString
})

if (!requireEmailConfirmation) {
return schema
}

return withEmailConfirmation(schema, t('validation.emailMatch'))
}

const getNewContact = (isActiveUser = false): Contact => {
Expand All @@ -29,7 +36,8 @@ export const useStrrContactStore = defineStore('strr/contact', () => {
extension: ''
},
faxNumber: '',
emailAddress: ''
emailAddress: '',
confirmEmailAddress: ''
}
}

Expand All @@ -51,6 +59,7 @@ export const useStrrContactStore = defineStore('strr/contact', () => {
watch(primaryRep, (val) => {
if (val && isCompletingPartyRep.value) {
completingParty.value.emailAddress = val?.emailAddress
completingParty.value.confirmEmailAddress = val?.confirmEmailAddress
completingParty.value.phone = val?.phone

// for BCeID users we need to copy the names over from primary rep to completing party
Expand All @@ -64,9 +73,9 @@ export const useStrrContactStore = defineStore('strr/contact', () => {

const secondaryRep = ref<StrrContact | undefined>(undefined)

const compPartySchema = getContactSchema(true)
const primaryRepSchema = getContactSchema(false)
const secondaryRepSchema = getContactSchema(false)
const compPartySchema = getContactSchema(true, false)
const primaryRepSchema = getContactSchema(false, true)
const secondaryRepSchema = getContactSchema(false, false)

const validateContact = async (returnBool = false): Promise<MultiFormValidationResult | boolean> => {
const validations = [
Expand Down
25 changes: 25 additions & 0 deletions strr-base-web/app/utils/connect-validation/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,28 @@ export const getOptionalEmail = (message: string) =>
}
return true
}, message)

/**
* Refines a Zod schema to enforce that an email and its confirmation field match exactly.
* Attaches the validation error to the confirmation field path.
*/
export const withEmailConfirmation = <T extends z.ZodTypeAny>(
schema: T,
message: string,
emailKey: string = 'emailAddress',
confirmEmailKey: string = 'confirmEmailAddress'
) => {
return schema.refine(
(data: Record<string, any>) => {
// If neither is entered, allow the required email validator to report on the primary field
if (!data[emailKey] && !data[confirmEmailKey]) {
return true
}
return data[emailKey] === data[confirmEmailKey]
},
{
message,
path: [confirmEmailKey]
}
)
}
1 change: 1 addition & 0 deletions strr-base-web/app/utils/formattingHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export function formatPartyUI <T extends ApiParty> (party: T): Contact {
lastName: party.lastName || '',
middleName: party.middleName || '',
emailAddress: party.emailAddress,
confirmEmailAddress: party.emailAddress || '',
phone: formatPhoneNumberUI(party),
...(party.preferredName ? { preferredName: party.preferredName } : {}),
...(party.faxNumber ? { faxNumber: party.faxNumber } : {})
Expand Down
2 changes: 2 additions & 0 deletions strr-base-web/i18n/locales/en-CA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ export default {
citizenshipPR: 'Citizenship/Permanent Residency',
emailAddress: 'Email Address',
emailAddressOpt: 'Email Address (Optional)',
confirmEmailAddress: 'Confirm Email Address',
firstName: 'First Name',
middleName: 'Middle Name',
middleNameOpt: 'Middle Name (Optional)',
Expand Down Expand Up @@ -491,6 +492,7 @@ export default {
cpbc: 'Please enter a valid CPBC number'
},
email: 'Please enter a valid email',
emailMatch: 'Email address does not match',
name: {
first: 'Please enter a first name',
last: 'Please enter a last name',
Expand Down
7 changes: 6 additions & 1 deletion strr-base-web/i18n/locales/fr-CA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ export default {
label: {
expiryDaysLeft: '{count} jours restants',
expiryDaysAgo: 'il y a {count} jours',
expiryToday: "aujourd'hui"
expiryToday: "aujourd'hui",
confirmEmailAddress: "Confirmer l'adresse e-mail"
},
labels: {
note: 'Note',
Expand All @@ -94,6 +95,7 @@ export default {
citizenship: 'Citoyenneté',
citizenshipPR: 'Citoyenneté/Résidence Permanente',
emailAddress: 'Adresse e-mail',
confirmEmailAddress: "Confirmer l'adresse e-mail",
fullName: 'Nom Légal Complet',
preferredName: 'Nom Préféré',
address: 'Adresse',
Expand Down Expand Up @@ -191,6 +193,9 @@ export default {
},
table: {},
toast: {},
validation: {
emailMatch: "L'adresse e-mail ne correspond pas"
},
widgets: {
feeSummary: {
title: 'Résumé des Frais',
Expand Down
2 changes: 1 addition & 1 deletion strr-base-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "strr-base-web",
"private": true,
"type": "module",
"version": "0.0.53",
"version": "0.0.54",
"engines": {
"node": ">=24"
},
Expand Down
Loading
Loading