From 508c31002335523bb87a2a906f7db8fbb5e1b0f5 Mon Sep 17 00:00:00 2001 From: Niko Pitkonen Date: Wed, 13 Sep 2023 15:50:51 +0300 Subject: [PATCH] HAI-1586 Change hanke contact data model --- public/mockServiceWorker.js | 6 +- src/domain/hanke/edit/HankeForm.test.tsx | 55 ++++--- .../hanke/edit/HankeFormYhteystiedot.tsx | 139 +++++++----------- .../hanke/edit/components/ContactsSummary.tsx | 17 +-- src/domain/hanke/edit/hankeSchema.ts | 33 ++--- src/domain/hanke/edit/types.ts | 11 +- src/domain/hanke/hankeView/HankeView.test.tsx | 4 +- src/domain/mocks/data/hankkeet-data.ts | 43 +++--- src/domain/types/hanke.ts | 11 +- src/locales/en.json | 2 + src/locales/fi.json | 2 + src/locales/sv.json | 2 + 12 files changed, 143 insertions(+), 182 deletions(-) diff --git a/public/mockServiceWorker.js b/public/mockServiceWorker.js index 8b1525b56..799b96754 100644 --- a/public/mockServiceWorker.js +++ b/public/mockServiceWorker.js @@ -120,7 +120,7 @@ self.addEventListener('fetch', function (event) { console.warn( '[MSW] Successfully emulated a network error for the "%s %s" request.', request.method, - request.url + request.url, ); return; } @@ -131,9 +131,9 @@ self.addEventListener('fetch', function (event) { [MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, request.method, request.url, - `${error.name}: ${error.message}` + `${error.name}: ${error.message}`, ); - }) + }), ); }); diff --git a/src/domain/hanke/edit/HankeForm.test.tsx b/src/domain/hanke/edit/HankeForm.test.tsx index 043c6b758..7ac378ded 100644 --- a/src/domain/hanke/edit/HankeForm.test.tsx +++ b/src/domain/hanke/edit/HankeForm.test.tsx @@ -122,58 +122,55 @@ describe('HankeForm', () => { test('Yhteystiedot can be filled', async () => { const { user } = await setupYhteystiedotPage(); + // Hanke owner await user.click(screen.getByRole('button', { name: /tyyppi/i })); await user.click(screen.getByText(/yritys/i)); - fireEvent.change(screen.getByLabelText(/nimi/i), { - target: { value: 'Olli Omistaja' }, + + fireEvent.change(screen.getByTestId('omistajat.0.nimi'), { + target: { value: 'Omistaja Yritys' }, }); fireEvent.change(screen.getByLabelText(/y-tunnus tai henkilötunnus/i), { target: { value: 'y-tunnus' }, }); - fireEvent.change(screen.getByLabelText(/katuosoite/i), { - target: { value: 'Testikuja 1' }, - }); - fireEvent.change(screen.getByLabelText(/postinumero/i), { - target: { value: '00000' }, - }); - fireEvent.change(screen.getByLabelText(/postitoimipaikka/i), { - target: { value: 'Testikaupunki' }, - }); - fireEvent.change(screen.getByLabelText(/sähköposti/i), { + fireEvent.change(screen.getByTestId('omistajat.0.email'), { target: { value: 'test@mail.com' }, }); - fireEvent.change(screen.getByLabelText(/puhelinnumero/i), { - target: { value: '0400000000' }, + fireEvent.change(screen.getByTestId('omistajat.0.puhelinnumero'), { + target: { value: '0401234567' }, }); - expect(screen.queryByRole('group', { name: 'Yhteyshenkilö' })).not.toBeInTheDocument(); - await user.click(screen.getByLabelText(/erillinen yhteyshenkilö/i)); - expect(screen.getByRole('group', { name: 'Yhteyshenkilö' })).toBeInTheDocument(); - - fireEvent.change(screen.getAllByLabelText(/nimi/i)[1], { - target: { value: 'Testi Yhteyshenkilö' }, + // Hanke owner contact person + fireEvent.change(screen.getByTestId('omistajat.0.alikontaktit.0.etunimi'), { + target: { value: 'Olli' }, + }); + fireEvent.change(screen.getByTestId('omistajat.0.alikontaktit.0.sukunimi'), { + target: { value: 'Omistaja' }, + }); + fireEvent.change(screen.getByTestId('omistajat.0.alikontaktit.0.email'), { + target: { value: 'foo@bar.com' }, }); - fireEvent.change(screen.getAllByLabelText(/sähköposti/i)[1], { - target: { value: 'yhteyshenkilo@mail.com' }, + fireEvent.change(screen.getByTestId('omistajat.0.alikontaktit.0.puhelinnumero'), { + target: { value: '0507654321' }, }); - await user.click(screen.getByText(/Rakennuttajan tiedot/i)); + // Rakennuttaja + await user.click(screen.getByText(/rakennuttajan tiedot/i)); await user.click(screen.getByText(/lisää rakennuttaja/i)); expect(screen.getAllByText('Rakennuttaja')).toHaveLength(1); await user.click(screen.getByText(/lisää yhteyshenkilö/i)); await user.click(screen.getByText(/lisää yhteyshenkilö/i)); expect(screen.getByRole('tablist').childElementCount).toBe(2); + await user.click(screen.getByText(/poista yhteyshenkilö/i)); + await user.click(screen.getByText(/poista yhteyshenkilö/i)); await user.click(screen.getByText(/lisää rakennuttaja/i)); expect(screen.getAllByText('Rakennuttaja')).toHaveLength(2); - await user.click(screen.getAllByText(/poista rakennuttaja/i)[1]); - expect(screen.getAllByText('Rakennuttaja')).toHaveLength(1); + await user.click(screen.getByText(/poista rakennuttaja/i)); - await user.click(screen.getByText(/poista yhteyshenkilö/i)); - expect(screen.getByRole('tablist').childElementCount).toBe(1); - await user.click(screen.getByText(/poista yhteyshenkilö/i)); - expect(screen.queryByText('Yhteyshenkilön tiedot')).not.toBeInTheDocument(); + // Check Other types are present and clickable + await user.click(screen.getByText(/toteuttajan tiedot/i)); + await user.click(screen.getByText(/muiden tahojen tiedot/i)); }); }); diff --git a/src/domain/hanke/edit/HankeFormYhteystiedot.tsx b/src/domain/hanke/edit/HankeFormYhteystiedot.tsx index 2c5cdc072..c0df8cffb 100644 --- a/src/domain/hanke/edit/HankeFormYhteystiedot.tsx +++ b/src/domain/hanke/edit/HankeFormYhteystiedot.tsx @@ -1,9 +1,9 @@ -import React, { useState } from 'react'; +import React from 'react'; import { useTranslation } from 'react-i18next'; import { $enum } from 'ts-enum-util'; -import { Accordion, Button, Fieldset, IconCross, IconPlusCircle, ToggleButton } from 'hds-react'; +import { Accordion, Button, Fieldset, IconCross, IconPlusCircle } from 'hds-react'; import { useFieldArray } from 'react-hook-form'; -import { CONTACT_FORMFIELD, FORMFIELD, FormProps } from './types'; +import { CONTACT_FORMFIELD, FORMFIELD, FormProps, SUBCONTACT_FORMFIELD } from './types'; import { HankeContact, CONTACT_TYYPPI, @@ -25,9 +25,6 @@ const CONTACT_FIELDS: Array = [ 'tyyppi', 'nimi', 'ytunnusTaiHetu', - 'osoite', - 'postinumero', - 'postitoimipaikka', 'email', 'puhelinnumero', ]; @@ -36,6 +33,7 @@ const REQUIRED_CONTACT_FIELDS: Array = [ 'nimi', 'ytunnusTaiHetu', 'email', + 'puhelinnumero', ]; function isRequiredContactField(field: keyof HankeContact) { @@ -47,9 +45,6 @@ function getEmptyContact(): Omit { nimi: '', tyyppi: null, ytunnusTaiHetu: '', - osoite: '', - postinumero: '', - postitoimipaikka: '', email: '', puhelinnumero: '', alikontaktit: [], @@ -70,10 +65,8 @@ function getEmptyOtherContact(): HankeMuuTaho { function getEmptySubContact(): HankeSubContact { return { - nimi: '', - osoite: '', - postinumero: '', - postitoimipaikka: '', + etunimi: '', + sukunimi: '', email: '', puhelinnumero: '', }; @@ -93,32 +86,26 @@ const SubContactFields: React.FC<{ fieldPath: string; onRemove: () => void }> = > - - - - + + + {/* Toteuttaja */} = () => { + {/* Muut tahot */} = ({ co

{t(`form:yhteystiedot:contactType:${contact.tyyppi}`)}

{contact.nimi}

{contact.ytunnusTaiHetu}

- {contact.osoite &&

{contact.osoite}

} -

- {contact.postinumero} {contact.postitoimipaikka} -

+

{contact.email}

+

{contact.puhelinnumero}

); } @@ -33,15 +31,16 @@ const ContactSummary: React.FC<{ contact: HankeContact | HankeMuuTaho }> = ({ co ); }; -const SubContactSummary: React.FC<{ contact: HankeSubContact }> = ({ contact }) => { +const SubContactSummary: React.FC<{ contact: HankeSubContact }> = ({ + contact: { etunimi, sukunimi, email, puhelinnumero }, +}) => { return (
-

{contact.nimi}

-

{contact.email}

- {contact.osoite &&

{contact.osoite}

}

- {contact.postinumero} {contact.postitoimipaikka} + {etunimi} {sukunimi}

+

{email}

+

{puhelinnumero}

); }; diff --git a/src/domain/hanke/edit/hankeSchema.ts b/src/domain/hanke/edit/hankeSchema.ts index 1f174a2a1..052597917 100644 --- a/src/domain/hanke/edit/hankeSchema.ts +++ b/src/domain/hanke/edit/hankeSchema.ts @@ -10,35 +10,34 @@ import { HANKE_KAISTAPITUUSHAITTA, CONTACT_TYYPPI, } from '../../types/hanke'; -import { FORMFIELD, CONTACT_FORMFIELD } from './types'; +import { FORMFIELD, CONTACT_FORMFIELD, SUBCONTACT_FORMFIELD } from './types'; const subContactSchema = yup + .object() + .nullable() + .default(null) + .shape({ + [SUBCONTACT_FORMFIELD.ETUNIMI]: yup.string().max(50).required(), + [SUBCONTACT_FORMFIELD.SUKUNIMI]: yup.string().max(50).required(), + [SUBCONTACT_FORMFIELD.EMAIL]: yup.string().email().max(100).required(), + [SUBCONTACT_FORMFIELD.PUHELINNUMERO]: yup.string().nullable().default(null).max(20), + }); + +const contactSchema = yup .object() .nullable() .default(null) .shape({ [CONTACT_FORMFIELD.NIMI]: yup.string().max(100).required(), - [CONTACT_FORMFIELD.OSOITE]: yup.string(), - [CONTACT_FORMFIELD.POSTINRO]: yup.string(), - [CONTACT_FORMFIELD.POSTITOIMIPAIKKA]: yup.string(), + [CONTACT_FORMFIELD.TYYPPI]: yup.string().oneOf($enum(CONTACT_TYYPPI).getValues()).required(), + [CONTACT_FORMFIELD.TUNNUS]: yup.string().required(), [CONTACT_FORMFIELD.EMAIL]: yup.string().email().max(100).required(), [CONTACT_FORMFIELD.PUHELINNUMERO]: yup.string().nullable().default(null).max(20), + [CONTACT_FORMFIELD.ALIKONTAKTIT]: yup.array().ensure().of(subContactSchema), }); -const contactSchema = subContactSchema.shape({ - [CONTACT_FORMFIELD.TYYPPI]: yup.string().oneOf($enum(CONTACT_TYYPPI).getValues()).required(), - [CONTACT_FORMFIELD.TUNNUS]: yup.string().required(), - [CONTACT_FORMFIELD.ALIKONTAKTIT]: yup.array().ensure().of(subContactSchema), -}); - const otherPartySchema = contactSchema - .omit([ - CONTACT_FORMFIELD.TYYPPI, - CONTACT_FORMFIELD.TUNNUS, - CONTACT_FORMFIELD.OSOITE, - CONTACT_FORMFIELD.POSTINRO, - CONTACT_FORMFIELD.POSTITOIMIPAIKKA, - ]) + .omit([CONTACT_FORMFIELD.TYYPPI, CONTACT_FORMFIELD.TUNNUS]) .shape({ [CONTACT_FORMFIELD.ROOLI]: yup.string().required(), [CONTACT_FORMFIELD.ORGANISAATIO]: yup.string(), diff --git a/src/domain/hanke/edit/types.ts b/src/domain/hanke/edit/types.ts index a33bb23ec..a4c89950c 100644 --- a/src/domain/hanke/edit/types.ts +++ b/src/domain/hanke/edit/types.ts @@ -37,9 +37,6 @@ export enum CONTACT_FORMFIELD { ROOLI = 'rooli', NIMI = 'nimi', TUNNUS = 'ytunnusTaiHetu', - OSOITE = 'osoite', - POSTINRO = 'postinumero', - POSTITOIMIPAIKKA = 'postitoimipaikka', EMAIL = 'email', PUHELINNUMERO = 'puhelinnumero', ORGANISAATIO = 'organisaatioNimi', @@ -47,6 +44,14 @@ export enum CONTACT_FORMFIELD { ALIKONTAKTIT = 'alikontaktit', } +export enum SUBCONTACT_FORMFIELD { + ID = 'id', + ETUNIMI = 'etunimi', + SUKUNIMI = 'sukunimi', + EMAIL = 'email', + PUHELINNUMERO = 'puhelinnumero', +} + export interface HankeAlueFormState extends HankeAlue { feature?: Feature; // "virtualField" } diff --git a/src/domain/hanke/hankeView/HankeView.test.tsx b/src/domain/hanke/hankeView/HankeView.test.tsx index 0700cfe39..347d88c16 100644 --- a/src/domain/hanke/hankeView/HankeView.test.tsx +++ b/src/domain/hanke/hankeView/HankeView.test.tsx @@ -83,8 +83,8 @@ test('Correct information about hanke should be displayed', async () => { expect(screen.queryByText('Yritys')).toBeInTheDocument(); expect(screen.queryByText('Kauppisen maansiirtofirma KY')).toBeInTheDocument(); expect(screen.queryByText('y-1234567')).toBeInTheDocument(); - expect(screen.queryByText('Lahdenkatu 3')).toBeInTheDocument(); - expect(screen.queryByText('42100 Lahti')).toBeInTheDocument(); + expect(screen.queryByText('toimisto@testi.com')).toBeInTheDocument(); + expect(screen.queryByText('0501234567')).toBeInTheDocument(); }); test('It is possible to delete hanke if it has no active applications', async () => { diff --git a/src/domain/mocks/data/hankkeet-data.ts b/src/domain/mocks/data/hankkeet-data.ts index 875c014f3..96021c96b 100644 --- a/src/domain/mocks/data/hankkeet-data.ts +++ b/src/domain/mocks/data/hankkeet-data.ts @@ -67,17 +67,12 @@ const hankkeet: HankeDataDraft[] = [ tyyppi: 'YRITYS', nimi: 'Yritys Oy', ytunnusTaiHetu: 'y-1234567', - osoite: 'Yrityskuja 5', - postinumero: '00100', - postitoimipaikka: 'Helsinki', email: 'yritys@testi.com', puhelinnumero: '0000000000', alikontaktit: [ { - nimi: 'Esa Kauppinen', - osoite: 'Lehdenkatu 3', - postinumero: '42100', - postitoimipaikka: 'Lahti', + etunimi: 'Esa', + sukunimi: 'Kauppinen', email: 'esa.kauppinen@maansiirtofirma.com', puhelinnumero: '', }, @@ -90,25 +85,18 @@ const hankkeet: HankeDataDraft[] = [ tyyppi: 'YRITYS', nimi: 'Yritys 2 Oy', ytunnusTaiHetu: 'y-1234567', - osoite: '', - postinumero: '', - postitoimipaikka: '', email: 'yritys2@testi.com', puhelinnumero: '', alikontaktit: [ { - nimi: 'Matti Meikäläinen', - osoite: 'Katukuja 6', - postinumero: '', - postitoimipaikka: '', + etunimi: 'Matti', + sukunimi: 'Meikäläinen', email: 'matti.meikalainen@testi.com', puhelinnumero: '', }, { - nimi: 'Esa Kauppinen', - osoite: 'Lehdenkatu 3', - postinumero: '42100', - postitoimipaikka: 'Lahti', + etunimi: 'Esa', + sukunimi: 'Kauppinen', email: 'esa.kauppinen@maansiirtofirma.com', puhelinnumero: '', }, @@ -126,10 +114,8 @@ const hankkeet: HankeDataDraft[] = [ osasto: '', alikontaktit: [ { - nimi: 'Matti Meikäläinen', - osoite: 'Katukuja 6', - postinumero: '', - postitoimipaikka: '', + etunimi: 'Matti', + sukunimi: 'Meikäläinen', email: 'matti.meikalainen@testi.com', puhelinnumero: '', }, @@ -261,11 +247,16 @@ const hankkeet: HankeDataDraft[] = [ tyyppi: 'YRITYS', nimi: 'Kauppisen maansiirtofirma KY', ytunnusTaiHetu: 'y-1234567', - osoite: 'Lahdenkatu 3', - postinumero: '42100', - postitoimipaikka: 'Lahti', email: 'toimisto@testi.com', - puhelinnumero: '', + puhelinnumero: '0501234567', + alikontaktit: [ + { + etunimi: 'Esa', + sukunimi: 'Kauppinen', + email: 'esa.kauppinen@maansiirtofirma.com', + puhelinnumero: '0507654321', + }, + ], }, ], rakennuttajat: [], diff --git a/src/domain/types/hanke.ts b/src/domain/types/hanke.ts index 11f8adb03..a8a222bc4 100644 --- a/src/domain/types/hanke.ts +++ b/src/domain/types/hanke.ts @@ -111,17 +111,18 @@ export type HankeContactTypeKey = | HANKE_CONTACT_TYPE.MUUTTAHOT; export interface HankeSubContact { - nimi: string; - osoite?: string; - postinumero?: string; - postitoimipaikka?: string; + etunimi: string; + sukunimi: string; email: string; puhelinnumero: string; } -export interface HankeContact extends HankeSubContact { +export interface HankeContact { id: number | null; tyyppi: keyof typeof CONTACT_TYYPPI | null; + nimi: string; + email: string; + puhelinnumero: string; ytunnusTaiHetu: string; alikontaktit?: HankeSubContact[]; } diff --git a/src/locales/en.json b/src/locales/en.json index 1099f9351..1dca2a72e 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -163,6 +163,8 @@ "labels": { "tyyppi": "Type", "nimi": "Name", + "etunimi": "First name", + "sukunimi": "Last name", "ytunnusTaiHetu": "Business ID or personal identity code", "ytunnus": "Business ID", "osoite": "Street address", diff --git a/src/locales/fi.json b/src/locales/fi.json index f9ccb1e90..c94c354c8 100644 --- a/src/locales/fi.json +++ b/src/locales/fi.json @@ -172,6 +172,8 @@ "labels": { "tyyppi": "Tyyppi", "nimi": "Nimi", + "etunimi": "Etunimi", + "sukunimi": "Sukunimi", "ytunnusTaiHetu": "Y-tunnus tai henkilötunnus", "ytunnus": "Y-tunnus", "osoite": "Katuosoite", diff --git a/src/locales/sv.json b/src/locales/sv.json index 482cdc482..bd3441e3b 100644 --- a/src/locales/sv.json +++ b/src/locales/sv.json @@ -163,6 +163,8 @@ "labels": { "tyyppi": "Typ", "nimi": "Namn", + "etunimi": "Förnamn", + "sukunimi": "Efternamn", "ytunnusTaiHetu": "FO-nummer eller personbeteckning", "ytunnus": "FO-nummer", "osoite": "Gatuadress",