Skip to content

Commit

Permalink
Merge pull request #115 from InseeFr/feat/send-communication-restrict…
Browse files Browse the repository at this point in the history
…ion-rules

Feat/send communication restriction rules
  • Loading branch information
ddecrulle committed Jul 12, 2023
2 parents b46b0b6 + 2abc744 commit 12d5045
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 119 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pearl",
"version": "1.1.2",
"version": "1.1.3",
"private": true,
"dependencies": {
"@date-io/date-fns": "1.x",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,95 +28,103 @@ export const CommunicationRequestForm = ({
userInformation,
recipientInformation,
}) => {
const classes = useStyles();

const [communicationRequest, setCommunicationRequest] = useState(EMPTY_COMMUNICATION_REQUEST);
const steps = COMMUNICATION_REQUEST_FORM_STEPS;
const [selectedStep, setSelectedStep] = useState(steps[0]);

const [currentAttributeIsValid, setCurrentAttributeIsValid] = useState(false);

const getStepIndex = () => steps.findIndex(step => step.title === selectedStep.title);
const isFirstStep = () => getStepIndex() === 0;
const isLastStep = () => getStepIndex() === steps.length - 1;

const generateCommunicationRequest = commRequ => ({
...commRequ,
status: [{ date: new Date().getTime(), status: communicationStatusEnum.INITIATED.type }],
});

const nextStepAction = () => {
if (isLastStep()) {
saveFunction(generateCommunicationRequest(communicationRequest));
closeModalFunction();
} else {
setSelectedStep(steps[getStepIndex() + 1]);
}
};

const previousStepAction = () => {
if (isFirstStep()) {
closeModalFunction();
} else {
setSelectedStep(steps[getStepIndex() - 1]);
}
};

const isValueValid = value => !!value;

const isAddressingValid = () => {
// TODO : add business rules here
return true;
};

const checkValidity = useCallback((communicationRequest, selectedStep) => {
if (selectedStep.valueName !== undefined)
return isValueValid(communicationRequest[selectedStep.valueName]);

return isAddressingValid();
}, []);

useEffect(() => {
setCurrentAttributeIsValid(checkValidity(communicationRequest, selectedStep));
}, [checkValidity, communicationRequest, selectedStep]);

const handleChange = event => {
const {
target: { value },
} = event;
setCommunicationRequest({ ...communicationRequest, [selectedStep.valueName]: value });
};
const addressesErrors = checkCommunicationRequestFormAddressesValidity(
recipientInformation,
userInformation,
communicationRequest
);

return (
<>
<Typography className={classes.title}>{selectedStep.title}</Typography>
{selectedStep.valueName ? (
<FormRadioGroup
groupSelectedValue={communicationRequest[selectedStep.valueName]}
groupHandleChange={handleChange}
groupValues={selectedStep.values}
/>
) : (
<CommunicationRequestValidation
communicationRequest={communicationRequest}
recipientInformation={recipientInformation}
userInformation={userInformation}
addressesErrors={addressesErrors}
/>
)}
<div className={classes.buttons}>
<Button variant="outlined" onClick={previousStepAction}>
{selectedStep.previousLabel}
</Button>
<Button disabled={!currentAttributeIsValid} onClick={nextStepAction}>
{selectedStep.nextLabel}
</Button>
</div>
</>
);
};
const classes = useStyles();

const [communicationRequest, setCommunicationRequest] = useState(
EMPTY_COMMUNICATION_REQUEST
);
const steps = COMMUNICATION_REQUEST_FORM_STEPS;
const [selectedStep, setSelectedStep] = useState(steps[0]);

const [currentStepIsValid, setCurrentStepIsValid] = useState(false);

const getStepIndex = () => steps.findIndex(step => step.title === selectedStep.title);
const isFirstStep = () => getStepIndex() === 0;
const isLastStep = () => getStepIndex() === steps.length - 1;

const generateCommunicationRequest = commRequ => ({
...commRequ,
status: [{ date: new Date().getTime(), status: communicationStatusEnum.INITIATED.type }],
});

const nextStepAction = () => {
if (isLastStep()) {
saveFunction(generateCommunicationRequest(communicationRequest));
closeModalFunction();
} else {
setSelectedStep(steps[getStepIndex() + 1]);
}
};

const previousStepAction = () => {
if (isFirstStep()) {
closeModalFunction();
} else {
setSelectedStep(steps[getStepIndex() - 1]);
}
};

const isValueValid = value => !!value;

const isAddressingValid = communicationRequest =>
!Object.values(
checkCommunicationRequestFormAddressesValidity(
recipientInformation,
userInformation,
communicationRequest
)
)
.map(Object.values)
.flat()
.some(error => error === true);

useEffect(() => {
const stepValidity =
selectedStep.valueName !== undefined
? isValueValid(communicationRequest[selectedStep.valueName])
: isAddressingValid(communicationRequest);

setCurrentStepIsValid(stepValidity);
}, [communicationRequest, selectedStep]);

const handleChange = event => {
const {
target: { value },
} = event;
setCommunicationRequest({ ...communicationRequest, [selectedStep.valueName]: value });
};

const addressesErrors = checkCommunicationRequestFormAddressesValidity(
recipientInformation,
userInformation,
communicationRequest
);

return (
<>
<Typography className={classes.title}>{selectedStep.title}</Typography>
{selectedStep.valueName ? (
<FormRadioGroup
groupSelectedValue={communicationRequest[selectedStep.valueName]}
groupHandleChange={handleChange}
groupValues={selectedStep.values}
/>
) : (
<CommunicationRequestValidation
communicationRequest={communicationRequest}
recipientInformation={recipientInformation}
userInformation={userInformation}
addressesErrors={addressesErrors}
/>
)}
<div className={classes.buttons}>
<Button variant="outlined" onClick={previousStepAction}>
{selectedStep.previousLabel}
</Button>
<Button disabled={!currentStepIsValid} onClick={nextStepAction}>
{selectedStep.nextLabel}
</Button>
</div>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import { getTitle } from 'utils/functions';
import { makeStyles } from '@material-ui/core';
import clsx from 'clsx';

const useStyles = makeStyles(() => ({
commRequestContent: {
Expand All @@ -28,6 +29,9 @@ const useStyles = makeStyles(() => ({
lines: {
fontWeight: 'bold',
},
invalidAddress: {
border: '1px red solid',
},
}));

export const CommunicationRequestValidation = ({
Expand Down Expand Up @@ -62,6 +66,10 @@ export const CommunicationRequestValidation = ({
recipientLastName
);
const userAddress = buildAddressFirstLine(civility, firstName, lastName);
const hasError = data => Object.values(data).some(error => error === true);

const isUserInfoValid = !hasError(userError);
const isRecipientInfoValid = !hasError(recipientError);

return (
<Paper className={classes.commRequestContent} elevation={0}>
Expand All @@ -71,26 +79,28 @@ export const CommunicationRequestValidation = ({
<li>{typeLabel}</li>
{reason && <li>{reasonLabel}</li>}
</ul>
<Typography className={classes.lines}>{D.communicationSummaryRecipient}</Typography>
<Typography className={classes.address}>{recipientAddress}</Typography>
{address
.filter(addressLine => addressLine.length > 0)
.map(addressLine => (
<Typography key={addressLine} className={classes.address}>
{addressLine}
</Typography>
))}
<Typography
className={classes.address}
>{`${recipientPostcode}, ${recipientCityName}`}</Typography>

<Typography className={classes.lines}>{D.communicationSummaryInterviewer}</Typography>

<Typography className={classes.address}>{userAddress}</Typography>
<Typography className={classes.address}>{email}</Typography>
<Typography className={classes.address}>{phoneNumber}</Typography>
<ErrorDisplayer errors={userError} prefix="user" />
<ErrorDisplayer errors={recipientError} prefix="recipient" />
<Typography className={classes.lines}>{D.communicationSummaryRecipientAddress}</Typography>
<div className={clsx(isRecipientInfoValid ? '' : classes.invalidAddress)}>
<Typography className={classes.address}>{recipientAddress}</Typography>
{address
.filter(addressLine => addressLine.length > 0)
.map(addressLine => (
<Typography key={addressLine} className={classes.address}>
{addressLine}
</Typography>
))}
<Typography
className={classes.address}
>{`${recipientPostcode}, ${recipientCityName}`}</Typography>
</div>
<Typography className={classes.lines}>{D.communicationSummaryInterviewerAddress}</Typography>
<div className={clsx(isUserInfoValid ? '' : classes.invalidAddress)}>
<Typography className={classes.address}> {userAddress} </Typography>
<Typography className={classes.address}>{email}</Typography>
<Typography className={classes.address}>{phoneNumber}</Typography>
</div>
<ErrorDisplayer errors={userError} prefix={D.communicationSummaryInterviewer} />
<ErrorDisplayer errors={recipientError} prefix={D.communicationSummaryRecipient} />
<ErrorDisplayer errors={communicationRequestError} prefix="communication-request" />
</Paper>
);
Expand All @@ -100,10 +110,10 @@ const ErrorDisplayer = ({ errors, prefix }) => {
const classes = useStyles();
return Object.entries(errors)
.filter(([, val]) => val)
.map(([key, value]) => (
.map(([key]) => (
<Typography
key={`${prefix}-error-${key}`}
className={classes.error}
>{`${prefix}-${key} : ${value}`}</Typography>
>{`${prefix} : ${key}`}</Typography>
));
};
15 changes: 13 additions & 2 deletions src/i18n/communicationMessage.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,22 @@ const communicationMessage = {
},
communicationRequestValidation: { fr: 'Confirmation', en: 'Confirmation' },
communicationSummaryContent: { fr: 'Vous souhaitez envoyer :', en: 'You are about to send :' },
communicationSummaryRecipient: { fr: "A l'adresse suivante :", en: 'To the following address :' },
communicationSummaryInterviewer: {
communicationSummaryRecipientAddress: {
fr: "A l'adresse suivante :",
en: 'To the following address :',
},
communicationSummaryRecipient: {
fr: 'Destinataire',
en: 'Recipient',
},
communicationSummaryInterviewerAddress: {
fr: "Coordonnées de l'enquêteur·rice :",
en: 'Interviewer contact information :',
},
communicationSummaryInterviewer: {
fr: 'Enquêteur',
en: 'Interviewer',
},
communicationStatusInit: { fr: 'Créé', en: 'Created' },
communicationStatusReady: { fr: 'Enregistré', en: 'Created' },
communicationStatusSubmitted: { fr: 'Envoyé', en: 'Submitted' },
Expand Down
7 changes: 5 additions & 2 deletions src/utils/functions/communicationFunctions.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import { CIVILITIES } from 'utils/constants';
import { communicationEmiterEnum } from 'utils/enum/CommunicationEnums';
import { getAddressData } from './surveyUnitFunctions';
import { icons } from 'utils/icons/materialIcons';
import { contactOutcomeEnum } from 'utils/enum/ContactOutcomeEnum';

export const canSendCommunication = surveyUnit => {
// TODO : insert business rules here
return true;
// #1 communicationRequestConfiguration should be set to true
const {communicationRequestConfiguration = false, contactOutcome} = surveyUnit;
// #2 contactOutcome should be different from INTERVIEW_ACCEPTED
return communicationRequestConfiguration && contactOutcome?.type!==contactOutcomeEnum.INTERVIEW_ACCEPTED.type;
};

export const getCommunicationIconFromType = emiter =>
Expand Down

0 comments on commit 12d5045

Please sign in to comment.