Skip to content

Commit 3406dbf

Browse files
committed
fix: hearing project fields KER-422
1 parent ceea87d commit 3406dbf

File tree

3 files changed

+163
-82
lines changed

3 files changed

+163
-82
lines changed

src/components/admin/HearingFormStep5.jsx

Lines changed: 13 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,11 @@ import PropTypes from 'prop-types';
44
import { v1 as uuid } from 'uuid';
55
import { connect, useDispatch } from 'react-redux';
66
import { isEmpty } from 'lodash';
7-
import { Button, Notification, Select, TextInput } from 'hds-react';
7+
import { Select } from 'hds-react';
88
import { injectIntl, FormattedMessage } from 'react-intl';
9-
import classNames from 'classnames';
109

11-
import Icon from '../../utils/Icon';
1210
import { createNotificationPayload, NOTIFICATION_TYPES } from '../../utils/notify';
1311
import * as ProjectsSelector from '../../selectors/projectLists';
14-
import Phase from './Phase';
1512
import { hearingShape } from '../../types';
1613
import {
1714
changeProjectName,
@@ -22,6 +19,7 @@ import {
2219
changePhase,
2320
} from '../../actions/hearingEditor';
2421
import { addToast } from '../../actions/toast';
22+
import Project from './Project';
2523

2624
const HearingFormStep5 = ({ errors, hearing, hearingLanguages, language, projects, intl }) => {
2725
const dispatch = useDispatch();
@@ -51,70 +49,6 @@ const HearingFormStep5 = ({ errors, hearing, hearingLanguages, language, project
5149

5250
const onActivePhase = (phaseId) => dispatch(activePhase(phaseId));
5351

54-
const renderProject = (selectedProject) => {
55-
const phasesLength = hearing.project ? hearing.project.phases.length : null;
56-
const errorStyle = !errors.project_phase_active && phasesLength === 0 ? 'has-error' : null;
57-
58-
return (
59-
<div>
60-
{selectedProject &&
61-
hearingLanguages.map((usedLanguage) => (
62-
<div id='projectName' key={usedLanguage}>
63-
<TextInput
64-
id='projectName'
65-
name='projectName'
66-
label={
67-
<>
68-
<FormattedMessage id='projectName' /> ({usedLanguage})
69-
</>
70-
}
71-
maxLength={100}
72-
value={selectedProject.title[usedLanguage]}
73-
onBlur={(event) => onChangeProjectName(usedLanguage, event.target.value)}
74-
invalid={!!errors.project_title}
75-
errorText={errors.project_title}
76-
style={{ marginBottom: 'var(--spacing-s)' }}
77-
required
78-
/>
79-
</div>
80-
))}
81-
<div className='phases-container'>
82-
{selectedProject &&
83-
selectedProject.phases.map((phase, index) => {
84-
const key = index;
85-
86-
return (
87-
<Phase
88-
onChange={onChangePhase}
89-
phaseInfo={phase}
90-
key={key}
91-
indexNumber={index}
92-
onDelete={deletePhase}
93-
onActive={onActivePhase}
94-
languages={hearingLanguages}
95-
errors={errors}
96-
/>
97-
);
98-
})}
99-
</div>
100-
{selectedProject && (
101-
<div>
102-
<Button className={classNames([errorStyle, 'kerrokantasi-btn'])} onClick={addPhase} size='small'>
103-
<Icon className='icon' name='plus' /> <FormattedMessage id='addProcess'>{(txt) => txt}</FormattedMessage>
104-
</Button>
105-
</div>
106-
)}
107-
{!!errors.project_phase_active && phasesLength === 0 && (
108-
<Notification type='error' size='small'>
109-
{errors.project_phase_active}
110-
</Notification>
111-
)}
112-
</div>
113-
);
114-
};
115-
116-
const selectedProject = hearing.project;
117-
11852
const defaultProjectOptions = [
11953
{ value: uuid(), label: intl.formatMessage({ id: 'noProject' }) },
12054
{ value: '', label: intl.formatMessage({ id: 'defaultProject' }) },
@@ -129,7 +63,7 @@ const HearingFormStep5 = ({ errors, hearing, hearingLanguages, language, project
12963

13064
const options = [...defaultProjectOptions, ...projectsOptions];
13165

132-
const projectsInitialValue = selectedProject?.id ? selectedProject.id : options[0];
66+
const projectsInitialValue = hearing.project?.id ? hearing.project?.id : options[0];
13367

13468
return (
13569
<div>
@@ -144,7 +78,16 @@ const HearingFormStep5 = ({ errors, hearing, hearingLanguages, language, project
14478
defaultValue={projectsInitialValue}
14579
/>
14680
</div>
147-
{renderProject(selectedProject)}
81+
<Project
82+
hearing={hearing}
83+
errors={errors}
84+
hearingLanguages={hearingLanguages}
85+
onChangeProjectName={onChangeProjectName}
86+
onChangePhase={onChangePhase}
87+
deletePhase={deletePhase}
88+
onActivePhase={onActivePhase}
89+
addPhase={addPhase}
90+
/>
14891
</div>
14992
);
15093
};

src/components/admin/Phase.jsx

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,18 @@ const Phase = ({ phaseInfo, indexNumber, onDelete, onChange, onActive, languages
1212
const dispatch = useDispatch();
1313
const intl = useIntl();
1414

15-
const durationsInitial = languages.reduce((acc, current) => {
16-
acc[current] = phaseInfo.schedule[current];
15+
const getValuePerLanguage = (selector) =>
16+
languages.reduce((acc, current) => {
17+
acc[current] = selector[current];
1718

18-
return acc;
19-
}, {});
19+
return acc;
20+
}, {});
2021

21-
const descriptionsInitial = languages.reduce((acc, current) => {
22-
acc[current] = phaseInfo.description[current];
23-
24-
return acc;
25-
}, {});
22+
const titlesInitial = getValuePerLanguage(phaseInfo.title);
23+
const durationsInitial = getValuePerLanguage(phaseInfo.schedule);
24+
const descriptionsInitial = getValuePerLanguage(phaseInfo.description);
2625

26+
const [phaseTitles, setPhaseTitles] = useState(titlesInitial);
2727
const [phaseDurations, setPhaseDurations] = useState(durationsInitial);
2828
const [phaseDescriptions, setPhaseDescriptions] = useState(descriptionsInitial);
2929

@@ -49,8 +49,13 @@ const Phase = ({ phaseInfo, indexNumber, onDelete, onChange, onActive, languages
4949
<FormattedMessage id='phase' /> {indexNumber + 1} ({usedLanguage})
5050
</>
5151
}
52-
value={phaseInfo.title[usedLanguage]}
52+
value={phaseTitles[usedLanguage]}
5353
maxLength={100}
54+
onChange={(event) => {
55+
const { value } = event.target;
56+
57+
setPhaseTitles((prevState) => ({ ...prevState, [usedLanguage]: value }));
58+
}}
5459
onBlur={(event) =>
5560
onChange(phaseInfo.id || phaseInfo.frontId, 'title', usedLanguage, event.target.value)
5661
}
@@ -90,7 +95,7 @@ const Phase = ({ phaseInfo, indexNumber, onDelete, onChange, onActive, languages
9095
onChange={(event) => {
9196
const { value } = event.target;
9297

93-
setPhaseDurations({ ...phaseDurations, [usedLanguage]: value });
98+
setPhaseDurations((prevState) => ({ ...prevState, [usedLanguage]: value }));
9499
}}
95100
onBlur={(event) =>
96101
onChange(phaseInfo.id || phaseInfo.frontId, 'schedule', usedLanguage, event.target.value)
@@ -107,7 +112,7 @@ const Phase = ({ phaseInfo, indexNumber, onDelete, onChange, onActive, languages
107112
onChange={(event) => {
108113
const { value } = event.target;
109114

110-
setPhaseDescriptions({ ...phaseDescriptions, [usedLanguage]: value });
115+
setPhaseDescriptions((prevState) => ({ ...prevState, [usedLanguage]: value }));
111116
}}
112117
onBlur={(event) =>
113118
onChange(phaseInfo.id || phaseInfo.frontId, 'description', usedLanguage, event.target.value)

src/components/admin/Project.jsx

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import React, { useEffect, useState } from 'react';
2+
import PropTypes from 'prop-types';
3+
import { Button, Notification, TextInput } from 'hds-react';
4+
import { FormattedMessage } from 'react-intl';
5+
import classNames from 'classnames';
6+
import { isEmpty } from 'lodash';
7+
8+
import Phase from './Phase';
9+
import Icon from '../../utils/Icon';
10+
11+
const Project = ({
12+
hearing,
13+
errors,
14+
hearingLanguages,
15+
onChangeProjectName,
16+
onChangePhase,
17+
deletePhase,
18+
onActivePhase,
19+
addPhase,
20+
}) => {
21+
const phasesLength = hearing.project ? hearing.project.phases.length : null;
22+
const errorStyle = !errors.project_phase_active && phasesLength === 0 ? 'has-error' : null;
23+
24+
const getProjectTitles = (project) =>
25+
hearingLanguages.reduce((acc, current) => {
26+
if (!isEmpty(project?.title)) {
27+
acc[current] = project?.title[current];
28+
}
29+
30+
return acc;
31+
}, {});
32+
33+
const projectTitlesInitial = getProjectTitles(hearing.project);
34+
35+
const [selectedProject, setSelectedProject] = useState(hearing.project);
36+
const [selectedTitles, setSelectedTitles] = useState(projectTitlesInitial);
37+
38+
useEffect(() => {
39+
setSelectedProject(hearing.project);
40+
setSelectedTitles(!isEmpty(getProjectTitles(hearing.project)) ? getProjectTitles(hearing.project) : undefined);
41+
// eslint-disable-next-line react-hooks/exhaustive-deps
42+
}, [hearing.project]);
43+
44+
if (!selectedProject) {
45+
return null;
46+
}
47+
48+
return (
49+
<div>
50+
{hearingLanguages.map((usedLanguage) => (
51+
<div id='projectName' key={usedLanguage}>
52+
<TextInput
53+
id='projectName'
54+
name='projectName'
55+
label={
56+
<>
57+
<FormattedMessage id='projectName' /> ({usedLanguage})
58+
</>
59+
}
60+
maxLength={100}
61+
value={selectedTitles ? selectedTitles[usedLanguage] : ''}
62+
onChange={(event) => {
63+
const { value } = event.target;
64+
65+
setSelectedTitles((prevState) => ({ ...prevState, [usedLanguage]: value }));
66+
}}
67+
onBlur={(event) => onChangeProjectName(usedLanguage, event.target.value)}
68+
invalid={!!errors.project_title}
69+
errorText={errors.project_title}
70+
style={{ marginBottom: 'var(--spacing-s)' }}
71+
required
72+
/>
73+
</div>
74+
))}
75+
<div className='phases-container'>
76+
{selectedProject.phases.map((phase, index) => {
77+
const key = index;
78+
79+
return (
80+
<Phase
81+
onChange={onChangePhase}
82+
phaseInfo={phase}
83+
key={key}
84+
indexNumber={index}
85+
onDelete={deletePhase}
86+
onActive={onActivePhase}
87+
languages={hearingLanguages}
88+
errors={errors}
89+
/>
90+
);
91+
})}
92+
</div>
93+
94+
<div>
95+
<Button className={classNames([errorStyle, 'kerrokantasi-btn'])} onClick={addPhase} size='small'>
96+
<Icon className='icon' name='plus' /> <FormattedMessage id='addProcess'>{(txt) => txt}</FormattedMessage>
97+
</Button>
98+
</div>
99+
100+
{!!errors.project_phase_active && phasesLength === 0 && (
101+
<Notification type='error' size='small'>
102+
{errors.project_phase_active}
103+
</Notification>
104+
)}
105+
</div>
106+
);
107+
};
108+
109+
Project.propTypes = {
110+
hearing: PropTypes.shape({
111+
project: PropTypes.shape({
112+
// eslint-disable-next-line react/forbid-prop-types
113+
phases: PropTypes.arrayOf(PropTypes.object),
114+
title: PropTypes.shape({
115+
en: PropTypes.string,
116+
fi: PropTypes.string,
117+
sv: PropTypes.string,
118+
}),
119+
}),
120+
}).isRequired,
121+
errors: PropTypes.shape({
122+
project_phase_active: PropTypes.string,
123+
project_title: PropTypes.string,
124+
}).isRequired,
125+
hearingLanguages: PropTypes.arrayOf(PropTypes.string).isRequired,
126+
onChangeProjectName: PropTypes.func.isRequired,
127+
onChangePhase: PropTypes.func.isRequired,
128+
deletePhase: PropTypes.func.isRequired,
129+
onActivePhase: PropTypes.func.isRequired,
130+
addPhase: PropTypes.func.isRequired,
131+
};
132+
133+
export default Project;

0 commit comments

Comments
 (0)