Skip to content

Commit

Permalink
verified email is used on application as read-only
Browse files Browse the repository at this point in the history
  • Loading branch information
Joosakur committed Mar 1, 2025
1 parent 8efa616 commit b03060a
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import React from 'react'
import styled from 'styled-components'

import { useQueryResult } from 'lib-common/query'
import Checkbox from 'lib-components/atoms/form/Checkbox'
import InputField from 'lib-components/atoms/form/InputField'
import AdaptiveFlex from 'lib-components/layout/AdaptiveFlex'
import {
FixedSpaceColumn,
FixedSpaceFlexWrap,
FixedSpaceRow
} from 'lib-components/layout/flex-helpers'
import ExpandingInfo from 'lib-components/molecules/ExpandingInfo'
Expand All @@ -18,8 +20,10 @@ import { H3, Label, P } from 'lib-components/typography'
import { Gap } from 'lib-components/white-space'

import { ContactInfoSectionProps } from '../../../applications/editor/contact-info/ContactInfoSection'
import { renderResult } from '../../../async-rendering'
import { errorToInputInfo } from '../../../input-info-helper'
import { useLang, useTranslation } from '../../../localization'
import { emailVerificationStatusQuery } from '../../../personal-details/queries'

export default React.memo(function GuardianSubSection({
formData,
Expand All @@ -29,6 +33,21 @@ export default React.memo(function GuardianSubSection({
}: ContactInfoSectionProps) {
const t = useTranslation()
const [lang] = useLang()
const verifiedEmail = useQueryResult(emailVerificationStatusQuery()).map(
(res) => res.verifiedEmail
)

if (
verifiedEmail.isSuccess &&
verifiedEmail.value &&
formData.guardianEmail !== verifiedEmail.value
) {
updateFormData({
guardianEmail: verifiedEmail.value,
guardianEmailVerification: verifiedEmail.value,
noGuardianEmail: false
})
}

return (
<>
Expand Down Expand Up @@ -81,62 +100,89 @@ export default React.memo(function GuardianSubSection({
<Gap size="m" />

<EmailRow breakpoint="860px" verticalSpacing="zero">
<FixedSpaceColumn spacing="xs">
<Label htmlFor="guardian-email">
{t.applications.editor.contactInfo.email + ' *'}
</Label>
<InputField
id="guardian-email"
value={formData.guardianEmail}
data-qa="guardianEmail-input"
onChange={(value) =>
updateFormData({ guardianEmail: value, noGuardianEmail: false })
}
info={errorToInputInfo(errors.guardianEmail, t.validationErrors)}
hideErrorsBeforeTouched={!verificationRequested}
placeholder={t.applications.editor.contactInfo.email}
width="L"
required={true}
/>
<Gap size="xs" />
<Label htmlFor="verify-guardian-email">
{t.applications.editor.contactInfo.verifyEmail + ' *'}
</Label>
<InputField
id="verify-guardian-email"
value={formData.guardianEmailVerification}
data-qa="guardianEmailVerification-input"
onChange={(value) =>
updateFormData({
guardianEmailVerification: value,
noGuardianEmail: false
})
}
info={errorToInputInfo(
errors.guardianEmailVerification,
t.validationErrors
)}
hideErrorsBeforeTouched={!verificationRequested}
placeholder={t.applications.editor.contactInfo.verifyEmail}
width="L"
required={true}
/>
</FixedSpaceColumn>
<div>
<Gap size="m" />
<Checkbox
label={t.applications.editor.contactInfo.noEmail}
checked={formData.noGuardianEmail}
data-qa="noGuardianEmail-input"
onChange={(checked) =>
updateFormData({
guardianEmail: '',
guardianEmailVerification: '',
noGuardianEmail: checked
})
}
/>
</div>
{renderResult(verifiedEmail, (verifiedEmail) =>
verifiedEmail ? (
<FixedSpaceColumn spacing="xs">
<Label>{t.applications.editor.contactInfo.email + ' *'}</Label>
<FixedSpaceFlexWrap horizontalSpacing="X3L" verticalSpacing="L">
<span translate="no" data-qa="verified-email">
{verifiedEmail}
</span>
<div>
{t.applications.editor.contactInfo.emailChangeTip}
<a href="/personal-details" target="_blank" rel="noreferrer">
{t.applications.editor.contactInfo.emailChangeTipLink}
</a>
</div>
</FixedSpaceFlexWrap>
</FixedSpaceColumn>
) : (
<>
<FixedSpaceColumn spacing="xs">
<Label htmlFor="guardian-email">
{t.applications.editor.contactInfo.email + ' *'}
</Label>
<InputField
id="guardian-email"
value={formData.guardianEmail}
data-qa="guardianEmail-input"
onChange={(value) =>
updateFormData({
guardianEmail: value,
noGuardianEmail: false
})
}
info={errorToInputInfo(
errors.guardianEmail,
t.validationErrors
)}
hideErrorsBeforeTouched={!verificationRequested}
placeholder={t.applications.editor.contactInfo.email}
width="L"
required={true}
/>
<Gap size="xs" />
<Label htmlFor="verify-guardian-email">
{t.applications.editor.contactInfo.verifyEmail + ' *'}
</Label>
<InputField
id="verify-guardian-email"
value={formData.guardianEmailVerification}
data-qa="guardianEmailVerification-input"
onChange={(value) =>
updateFormData({
guardianEmailVerification: value,
noGuardianEmail: false
})
}
info={errorToInputInfo(
errors.guardianEmailVerification,
t.validationErrors
)}
hideErrorsBeforeTouched={!verificationRequested}
placeholder={t.applications.editor.contactInfo.verifyEmail}
width="L"
required={true}
/>
</FixedSpaceColumn>
<div>
<Gap size="m" />
<Checkbox
label={t.applications.editor.contactInfo.noEmail}
checked={formData.noGuardianEmail}
data-qa="noGuardianEmail-input"
onChange={(checked) =>
updateFormData({
guardianEmail: '',
guardianEmailVerification: '',
noGuardianEmail: checked
})
}
/>
</div>
</>
)
)}
</EmailRow>
<Gap size="m" />

Expand Down
5 changes: 5 additions & 0 deletions frontend/src/e2e-test/pages/citizen/citizen-applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,11 @@ class CitizenApplicationEditor {
await popup.waitForSelector('img:not([src=""])')
}

async assertVerifiedReadOnlyEmail(email: string) {
await this.page.findByDataQa('verified-email').assertTextEquals(email)
await this.page.findByDataQa('guardianEmail-input').waitUntilHidden()
}

private serviceNeedOptionDataQaPrefix(placementType: PlacementType | null) {
if (placementType === 'DAYCARE') {
return `full-time-option-`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
createDaycarePlacements,
getApplication,
resetServiceState,
runJobs,
setPersonEmail
} from '../../generated/api-clients'
import CitizenApplicationsPage from '../../pages/citizen/citizen-applications'
Expand All @@ -31,6 +32,7 @@ import {
fullDaycareForm,
minimalDaycareForm
} from '../../utils/application-forms'
import { getVerificationCodeFromEmail } from '../../utils/email'
import { Page } from '../../utils/page'
import { enduserLogin } from '../../utils/user'

Expand Down Expand Up @@ -342,4 +344,56 @@ describe('Citizen daycare applications', () => {
await editorPage.openSection('contactInfo')
await editorPage.guardianPhoneInput.assertValueEquals('040123456789')
})

test('If user has a verified email, that one is used in the application and cannot be changed', async () => {
// given user has a draft application with an email
await header.selectTab('applications')
const editorPage = await applicationsPage.createApplication(
testChild.id,
'DAYCARE'
)
const applicationId = editorPage.getNewApplicationId()
await editorPage.fillData({
...minimalDaycareForm().form,
contactInfo: {
guardianPhone: testAdult.phone,
guardianEmail: '[email protected]',
noGuardianEmail: false,
otherGuardianAgreementStatus: 'AGREED'
}
})
await editorPage.saveAsDraftButton.click()
await editorPage.modalOkBtn.click()

// and given user verifies another email
await header.selectTab('personal-details')
const section = new CitizenPersonalDetailsPage(page).personalDetailsSection
await section.editPersonalData(
{
preferredName: testAdult.firstName.split(' ')[1],
email: '[email protected]',
phone: testAdult.phone,
backupPhone: testAdult.backupPhone
},
true
)
await section.unverifiedEmailStatus.waitUntilVisible()
await section.sendVerificationCode.click()
await section.verificationCodeField.waitUntilVisible()
await runJobs({ mockedTime: mockedNow })
const verificationCode = await getVerificationCodeFromEmail()
expect(verificationCode).toBeTruthy()
await section.verificationCodeField.fill(verificationCode ?? '')
await section.verifyEmail.click()
await section.verifiedEmailStatus.waitUntilVisible()

// when user goes back to the application
await page.reload()
await header.selectTab('applications')
await applicationsPage.editApplication(applicationId)

// then the email in the application updates and cannot be edited
await editorPage.openSection('contactInfo')
await editorPage.assertVerifiedReadOnlyEmail('[email protected]')
})
})
3 changes: 3 additions & 0 deletions frontend/src/lib-customizations/defaults/citizen/i18n/en.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,9 @@ const en: Translations = {
phone: 'Phone number',
verifyEmail: 'Verify email',
email: 'Email',
emailChangeTip:
'Om du vill ändra e-postadressen, gör det på sidan för ',
emailChangeTipLink: 'personuppgifter.',
noEmail: "I don't have an email address",
secondGuardianInfoTitle: 'Second guardian information',
secondGuardianInfo:
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/lib-customizations/defaults/citizen/i18n/fi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1463,6 +1463,9 @@ export default {
phone: 'Puhelinnumero',
verifyEmail: 'Vahvista sähköpostiosoite',
email: 'Sähköpostiosoite',
emailChangeTip:
'Jos haluat muuttaa sähköpostiosoitettasi, voit tehdä sen ',
emailChangeTipLink: 'omissa tiedoissasi.',
noEmail: 'Minulla ei ole sähköpostiosoitetta',
secondGuardianInfoTitle: 'Toisen huoltajan tiedot',
secondGuardianInfo:
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/lib-customizations/defaults/citizen/i18n/sv.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,9 @@ const sv: Translations = {
phone: 'Telefonnummer',
verifyEmail: 'Verifiera e-postadress',
email: 'E-postadress',
emailChangeTip:
'If you wish to change the email address, please update it in your ',
emailChangeTipLink: 'personal information.',
noEmail: 'Jag har inte en e-postadress',
secondGuardianInfoTitle: 'Uppgifter om den andra vårdnadshavaren',
secondGuardianInfo:
Expand Down
3 changes: 2 additions & 1 deletion service/src/main/kotlin/fi/espoo/evaka/pis/PersonQueries.kt
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ RETURNING *
.exactlyOne(toPersonDTO)
}

// email is only updated if the user has no verified email
fun Database.Transaction.updatePersonBasicContactInfo(
id: PersonId,
email: String?,
Expand All @@ -403,7 +404,7 @@ fun Database.Transaction.updatePersonBasicContactInfo(
sql(
"""
UPDATE person SET
email = ${bind(email)},
email = CASE WHEN verified_email IS NULL THEN ${bind(email)} ELSE email END,
phone = ${bind(phone)}
WHERE id = ${bind(id)}
RETURNING id
Expand Down

0 comments on commit b03060a

Please sign in to comment.