Skip to content

Commit

Permalink
feat : individual phone tracking + identification refactoring (#184)
Browse files Browse the repository at this point in the history
* fix: jsx to tsx

* feat: testing a new way of computing identifications (writing a simpler code) with telephone as a mean

* fix: RadioLineProps label as possibly undefined

* fix: started refactoring

* fix: continuing refactoring

* fix: started refactoring for identification

* fix : import

* fix: updated translations

* fix: IdentificationQuestionValueOption renamed to IdentificationQuestionOption

* fix: partial records for unused questions id, type expansion for generalizing to other configurations

* fix : formatting

* fix : typo

* fix: formatting

* fix: continuing refactoring

* fix: bug fixes

* fix : continue debugging

* feat: add identifications questioning as differents files

* fix: removed logs

* fix: checkAvailability parameters fix

* fix: add new state only when all previous questions have been answered

* fix: removed all obselete files

* fix: deleted obselete useIdentificationQuestion

* fix : imports update, removed obselete code

* fix: update imports

* feat: add tests for checkAvailibility

* fix: update file names for vite test inclusion

* fix: made button label as optional

* fix : update seeder to match new identification model

* fix: typo

* fix: made clearer comments

* fix : making sure persistence is done everytime user select a field

* feat: transmission with rules support for INDTEL

* fix: update use effect to match new identification objects construction

* fix: removed obselete tests

* fix: added todo

* fix: minor code quality improvement

* fix: update identifcation questions names and values

* feat: add all identification configuration entries

* fix: reorder contactOutcome options

* fix : add an object defining for each identification a pair value/label

* fix: making sure identification is never null in case value retrieved from the api is null

* fix: updated code for HOUSEF2F options mapping

* fix: removed log

* fix : add IdentificationConfiguration.HOUSEF2F for validity checking

* fix: adapted validation for iasco by using identificationQuestionsTree options

* fix: fixed switch case

* fix : cognitive complexity

* fix : formatting

* fix: updated empty undefined identification handling

* fix : removed duplicated code

* fix: cleaner syntax, null identification in seeder

* fix : removed imports

* fix: formatting

* fix : identificationIsFinished safe return

* fix: code refactoring for handleResponse

* fix: moved identificationIsFinished as its the condition for updateStates to be called

* fix : improved use of allAnswered

* fix: setting dialog id after iterating over all questions

* fix : removed Object.entries nesting in Object.fromEntries

* fix moved up availableQuestionIds computation in parent scope for resolving nesting issue

* fix: renamed hook to match script name

* fix : add unit tests for useIdentificationQuestions.ts

* fix : applying useIdentificationQuestions

* feat: add HOUSETEL questions

* chore: rebase branch

* feat: use optionsMap

* fix: NOT_APPLICABLE swapped with DEFINITLY_UNAVAILABLE

* feat: add a missing label translation

* fix: removed unused file

* fix: removed useless boolean since disabled is always false as Button is called if selectedOption is true

* fix: disclaimer message for deprecated outcomes

* fix : updated tests

* fix : removed useless condition

* feat : identificationIsFinished unit tests

* feat : updated package.json version

* fix: made responses as undefined for following questions after the selected one

* fix: no default option selected for each dialog

* fix: fixed imports

* fix : made clear distinction between house identification and occupant identification

* fix : add missing option values for identification

* fix : clearer translation object name for house identification

* fix : simpler handling of selected responses update, we only need to update current selected question and next ones

* fix: merged conditionnal branches

* fix: removed deprecated test

* fix: using dictionnary instead of hardcoded value

* fix: add return true for any conclusive value since its means identification is finished

* fix: playwright tests

---------

Co-authored-by: Lea Renaux <lea.renaux@insee.fr>
Co-authored-by: Simon Demaziere <simon.demaziere@insee.fr>
Co-authored-by: Emmanuel <demey.emmanuel@gmail.com>
  • Loading branch information
4 people authored Jan 13, 2025
1 parent 00eba83 commit ca3cf89
Show file tree
Hide file tree
Showing 32 changed files with 1,364 additions and 826 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": "2.5.1",
"version": "2.6.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.11.1",
Expand Down
5 changes: 5 additions & 0 deletions src/i18n/errorMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ const errorMessage = {
en: "There's no survey unit with the identifier",
sq: 'Nuk ka njësi anketimi me identifikuesin',
},
missingLabel: {
fr: 'Label manquant',
en: 'Missing label',
sq: 'Etiketë e humbur',
},
};

export default errorMessage;
50 changes: 45 additions & 5 deletions src/i18n/identificationMessage.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,45 @@
const identificationMessage = {
identificationIdentified: {
sameAddress: {
fr: "Oui, à l'adresse indiquée",
en: 'Yes, at the specified address',
sq: 'Po, në adresën e specifikuar',
},
otherAddress: {
fr: 'Oui, mais à une autre adresse située dans le champs géographique',
en: 'Yes, but at another address within the geographical area',
sq: 'Po, por në një adresë tjetër brenda zonës gjeografike',
},
noField: {
fr: 'Oui, mais hors du champs géographique',
en: 'Yes, but outside the geographical area',
sq: 'Po, por jashtë zonës gjeografike',
},
noIdent: {
fr: 'Non',
en: 'No',
sq: 'Jo',
},
deceased: {
fr: 'Décédé',
en: 'Deceased',
sq: 'I ndjerë',
},
situationOrdinary: {
fr: 'Ordinaire',
en: 'Ordinary',
sq: 'Zakonshëm',
},
situationNonOrdinary: {
fr: 'Non Ordinaire',
en: 'Not Ordinary',
sq: 'Jo i zakonshëm',
},
houseIdentified: {
fr: 'Logement identifié',
en: 'Identified housing',
sq: 'Banimit i identifikuar',
},
identificationUnidentified: {
houseUnidentified: {
fr: 'Logement non identifié',
en: 'Unidentified housing',
sq: 'Banimi i paidentifikuar',
Expand All @@ -14,13 +49,16 @@ const identificationMessage = {
en: 'Destroyed housing',
sq: 'Banimi i shkatërruar',
},
accessAccessible: { fr: 'Logement accessible', en: 'Accessible housing', sq: 'Banimi i qasshëm' },
accessNotAccessible: {
accessibleHousing: {
fr: 'Logement accessible',
en: 'Accessible housing',
sq: 'Banimi i qasshëm',
},
notAccessibleHousing: {
fr: 'Logement non accessible',
en: 'Not accessible housing',
sq: 'Banimi jo i qasshëm',
},
situationOrdinary: { fr: 'Logement ordinaire', en: 'Ordinary housing', sq: 'Banimi i zakonshëm' },
situationNotOrdinary: {
fr: 'Logement non ordinaire',
en: 'Not ordinary housing',
Expand All @@ -29,6 +67,7 @@ const identificationMessage = {
situationAbsorbed: { fr: 'Logement absorbé', en: 'Absorbed housing', sq: 'Banimi i absorbuar' },
categoryPrimary: { fr: 'Résidence principale', en: 'Primary housing', sq: 'Banimi kryesor' },
categorySecondary: { fr: 'Résidence secondaire', en: 'Secondary housing', sq: 'Banimi dytësor' },
vacant: { fr: 'Vacant', en: 'Vacant', sq: 'Bosh' },
categoryOccasional: {
fr: 'Résidence occasionnelle',
en: 'Occasional housing',
Expand Down Expand Up @@ -67,6 +106,7 @@ const identificationMessage = {
en: 'Occupant identification',
sq: 'Identifikimi i banorit',
},
absorbed: { fr: 'Absorbé', en: 'Absorbed', sq: 'Banimi i absorbuar' },
identification: { fr: 'Repérage', en: 'Identification', sq: 'Identifikimi' },
move: { fr: 'Déplacement terrain', en: 'Onsite move', sq: 'Lëvizja në terren' },
noLocation: {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/SurveyUnitPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import D from 'i18n';
import { CommunicationsCard } from 'ui/SurveyUnit/Communication/CommunicationsCard';
import Box from '@mui/material/Box';
import { AddressCard } from '../ui/SurveyUnit/AddressCard';
import { IdentificationCard } from '../ui/SurveyUnit/IdentificationCard';
import { IdentificationCard } from '../ui/SurveyUnit/Identification/IdentificationCard';
import { PersonsCard } from '../ui/SurveyUnit/PersonsCard';
import { CommentCard } from '../ui/SurveyUnit/CommentCard';
import Stack from '@mui/material/Stack';
Expand Down
11 changes: 0 additions & 11 deletions src/types/IdentificationConfiguration.ts

This file was deleted.

24 changes: 8 additions & 16 deletions src/types/pearl.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { Answer } from 'utils/hooks/useIdentificationQuestions';

export type Question = {
answer: Answer;
value: string;
};
import {
IdentificationConfiguration,
IdentificationQuestionsId,
} from 'utils/enum/identifications/IdentificationsQuestions';

export type SurveyUnitPhoneNumber = {
source: string;
Expand Down Expand Up @@ -46,7 +44,7 @@ type SurveyUnitComment = {
};

type SurveyUnitState = {
id: number;
id?: number;
date: number;
type: string;
};
Expand All @@ -65,13 +63,7 @@ type SurveyUnitSampleIdentifiers = {
nograp: string;
};

type SurveyUnitIdentification = {
identification: unknown;
access: unknown;
situation: unknown;
category: unknown;
occupant: unknown;
};
export type SurveyUnitIdentification = Partial<Record<IdentificationQuestionsId, string>>;

export type SurveyUnitContactAttempt = {
status: string;
Expand Down Expand Up @@ -108,15 +100,15 @@ export type SurveyUnit = {
states: SurveyUnitState[];
contactAttempts: SurveyUnitContactAttempt[];
contactOutcome?: ContactOutcome;
identification: SurveyUnitIdentification;
identification?: SurveyUnitIdentification;
campaignLabel: string;
managementStartDate: number;
interviewerStartDate: number;
identificationPhaseStartDate: number;
collectionStartDate: number;
collectionEndDate: number;
endDate: number;
identificationConfiguration: string;
identificationConfiguration: IdentificationConfiguration;
contactOutcomeConfiguration: string;
contactAttemptConfiguration: string;
useLetterCommunication: boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/ui/ButtonLine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ButtonProps } from '@mui/material';

type ButtonLineProps = {
disabled: boolean;
label: string;
label?: string;
checked: boolean;
} & Omit<ButtonProps, 'disabled'>;

Expand Down
64 changes: 64 additions & 0 deletions src/ui/SurveyUnit/Identification/IdentificationCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Typography } from '../../Typography';
import D from 'i18n';
import { Row } from '../../Row';
import Stack from '@mui/material/Stack';
import { SurveyUnit } from 'types/pearl';
import { Card, CardContent } from '@mui/material';
import AssignmentIndOutlinedIcon from '@mui/icons-material/AssignmentIndOutlined';
import { ButtonLine } from 'ui/ButtonLine';
import { IdentificationQuestionsId } from 'utils/enum/identifications/IdentificationsQuestions';
import { useIdentificationQuestions } from 'utils/hooks/useIdentificationQuestions';
import { IdentificationDialog } from './IdentificationDialog';

type IdentificationCardProps = {
surveyUnit: SurveyUnit;
};

export function IdentificationCard({ surveyUnit }: Readonly<IdentificationCardProps>) {
const {
questions,
responses,
selectedDialogId,
availableQuestions,
setSelectedDialogId,
handleResponse,
} = useIdentificationQuestions(surveyUnit);

return (
<Card elevation={0}>
<CardContent>
<Stack gap={3}>
<Row gap={1}>
<AssignmentIndOutlinedIcon fontSize="large" />
<Typography as="h2" variant="xl" fontWeight={700}>
{D.identification}
</Typography>
</Row>
<Stack gap={1}>
{(Object.keys(questions) as IdentificationQuestionsId[]).map(questionId => (
<ButtonLine
key={questionId}
onClick={() => setSelectedDialogId(questionId)}
label={responses[questionId]?.label ?? questions[questionId]?.text}
checked={!!(responses[questionId] && availableQuestions[questionId])}
disabled={!availableQuestions[questionId]}
></ButtonLine>
))}
</Stack>
</Stack>
</CardContent>
{selectedDialogId && (
<IdentificationDialog
questionId={selectedDialogId}
key={`dialog-${selectedDialogId}`}
question={questions[selectedDialogId]}
defaultOption={responses[selectedDialogId] ?? undefined}
onSubmit={handleResponse}
onClose={() => {
setSelectedDialogId(undefined);
}}
/>
)}
</Card>
);
}
90 changes: 90 additions & 0 deletions src/ui/SurveyUnit/Identification/IdentificationDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import {
Dialog,
DialogTitle,
DialogContent,
RadioGroup,
Stack,
DialogActions,
Button,
} from '@mui/material';
import { useState } from 'react';
import { RadioLine } from 'ui/RadioLine';
import { IdentificationQuestionsId } from 'utils/enum/identifications/IdentificationsQuestions';
import {
IdentificationQuestionValue,
IdentificationQuestionOption,
} from 'utils/functions/identifications/identificationFunctions';
import D from 'i18n';

interface IdentificationDialogProps {
question?: IdentificationQuestionValue;
questionId: IdentificationQuestionsId;
defaultOption?: IdentificationQuestionOption;
onClose: () => void;
onSubmit: (questionId: IdentificationQuestionsId, option: IdentificationQuestionOption) => void;
}

export function IdentificationDialog({
question,
questionId,
defaultOption,
onClose,
onSubmit,
}: Readonly<IdentificationDialogProps>) {
const options = question?.options;
const [selectedOption, setSelectedOption] = useState(defaultOption);

const handleChange = (newOption: IdentificationQuestionOption) => {
setSelectedOption(newOption);
};

return (
<Dialog maxWidth="sm" open={question ? question.text.length > 0 : false} onClose={onClose}>
<DialogTitle id="identification-title">{question?.text}</DialogTitle>
<DialogContent>
<RadioGroup
onChange={e =>
handleChange({
value: e.target.value,
label: options?.find(o => o.value === e.target.value)?.label ?? D.missingLabel,
concluding:
options?.find(o => o.value === e.target.value)?.concluding ?? D.missingLabel,
})
}
defaultValue={selectedOption?.value}
aria-labelledby="identification-title"
name="identification-radio-group"
>
<Stack gap={1}>
{options?.map((option: IdentificationQuestionOption) => (
<RadioLine
value={option.value}
key={option.value}
label={option.label}
disabled={false}
/>
))}
</Stack>
</RadioGroup>
</DialogContent>
<DialogActions>
<Button type="button" color="primary" variant="contained" onClick={onClose}>
{D.cancelButton}
</Button>
{selectedOption && (
<Button
variant="contained"
type="button"
disabled={false}
onClick={() => {
onClose();
onSubmit(questionId, selectedOption);
}}
>
{D.confirmButton}
</Button>
)}
</DialogActions>
</Dialog>
);
}
Loading

0 comments on commit ca3cf89

Please sign in to comment.