From 8ef6f31e5f98c6822fb01d8a020db279fc2fdf89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Demazi=C3=A8re?= Date: Mon, 10 Oct 2022 15:25:24 +0200 Subject: [PATCH] Develop (#101) * v4 datamodel enum implementation * identification component, tabs navigation contactAttempt medium * identification component contactOutcomeConfiguration handling contactAttemptConfiguration handling navigation refactor * prevent full package import with destructuration * remove dead code * ES2020 syntax * syntax update * transmission controls handle identification * extends data managed by identification hook minor graphical/typo fixes * identification questionnaire internal state OK * missing answer * selected tab text color * fix identification component when no data * IASCO transmission control implementation log removal * fix undefined identification evaluation * fix enum entry for API compliance * identification generate valid starting state * typo * label changes * prevent tab switching when saving * fix transmission control * fix panel navigation * save each identification * filter contactOutcome by COconfiguration * UI refactor * fix contact outcome enums by medium * fix enum typo for API compliance * fix selected question highlight prevent valid answers erasure * update identification tab label * sticky tabs bar & prevent overlap on scrolls * extend comment length * new GUI components * fix background color in UE page * GUI improvements : nex compos & CSS * remove previous component * remove unused dictionnary entries * split info in column * update contact data display * update contact component display * fuse individual, phone, mail forms * new components graphical enhancement * GUI AppBar update * contact[attempt/ouctome] GUI update * minor code fix * remove unused imports * update infoTile test : minr GUI change * GUI forms refactor - WIP * user form birthdate change fix * fixes * working phoneNumber * prevent updating fiscal/directory phone numbers * fix missing phone number crash * theme integration for buttons * delete phoneNumbers lateral menu css fix email edition fix birthdate display fix * remove unused import * itw phone number duplicate removal * fix transmission control * GUI fixes * CSS clean-up, dead code removal * rename folder * CSS, fix default contactoutcome * remove unused components * address form refactor * CSS i18n new address data function * infoTile text overflow * fix minor bug * fix tests Co-authored-by: Nicolas Turban <37210319+nicolasTurban@users.noreply.github.com> --- package.json | 3 +- public/configuration.json | 6 +- src/App.js | 7 +- src/components/common/IconStatus.js | 27 -- .../common/Notification/notificationItem.js | 11 +- src/components/common/navigation/component.js | 58 +-- src/components/common/search/component.js | 45 +-- .../sharedComponents/EditableBooleanField.js | 32 ++ .../sharedComponents/EditableTextField.js | 32 ++ .../EditableTextFieldWithClickableIcon.js | 35 ++ .../common/sharedComponents/GenericTile.js | 46 +++ .../common/sharedComponents/IconButton.js | 32 ++ .../sharedComponents/LabelledBoolean.js | 26 ++ .../common/sharedComponents/LabelledSwitch.js | 27 ++ .../common/sharedComponents/LabelledText.js | 41 +++ .../LabelledTextWithClickableIcon.js | 21 ++ .../common/synchronize/component.js | 20 +- .../UESpage/material/surveyUnitCard.js | 33 +- .../UESpage/transmitForm/component.js | 24 -- .../panel-body/UEpage/atomicInfoTile.js | 83 ----- .../UEpage/comments/comment/component.js | 4 +- src/components/panel-body/UEpage/component.js | 2 +- .../contacts/contactAttempts/component.js | 38 +- .../contactAttempts/contactAttemptLine.js | 74 ++-- .../contacts/contactAttempts/upDownCounter.js | 82 ----- .../contacts/contactOutcome/component.js | 99 ++--- .../contactOutcome/contactOutcomeLine.js | 55 +++ .../panel-body/UEpage/details/Individual.js | 93 +++++ .../panel-body/UEpage/details/component.js | 41 +-- .../panel-body/UEpage/details/contact.js | 83 +++-- .../panel-body/UEpage/details/detailTile.js | 34 -- .../panel-body/UEpage/details/phoneList.js | 88 ----- .../panel-body/UEpage/details/phoneTile.js | 88 ----- .../panel-body/UEpage/forms/addressForm.js | 263 ++++++++------ .../UEpage/forms/contactAttemptsForm.js | 104 ++++-- .../UEpage/forms/contactOutcomeForm.js | 28 +- .../panel-body/UEpage/forms/index.js | 22 +- .../panel-body/UEpage/forms/mailForm.js | 94 ----- .../panel-body/UEpage/forms/phoneForm.js | 134 ------- .../panel-body/UEpage/forms/userForm.js | 339 ++++++++++++------ .../panel-body/UEpage/housing/component.js | 62 ++++ .../transmitForm => UEpage/housing}/index.js | 0 .../UEpage/identification/clickableLine.js | 52 +++ .../UEpage/identification/component.js | 101 +++++- .../UEpage/identification/labelledCheckbox.js | 39 ++ .../__snapshots__/infoTile.test.js.snap | 30 +- .../panel-body/UEpage/infoTile/infoTile.js | 59 +-- .../UEpage/infoTile/infoTile.test.js | 35 +- .../panel-body/UEpage/letters/component.js | 22 -- .../panel-body/UEpage/navigation/component.js | 111 +----- .../panel-body/UEpage/navigation/tabPanel.js | 17 + .../UEpage/navigation/tabSwipper.js | 91 +++++ .../UEpage/questionnaires/component.js | 28 ++ .../{letters => questionnaires}/index.js | 0 src/components/panel-body/UEpage/router.js | 145 +++++--- src/components/panel-body/UEpage/stateLine.js | 92 +++-- .../panel-body/UEpage/ueSubInfoTile.js | 36 -- src/components/panel-body/home/component.js | 3 +- .../panel-body/lateralMenu/component.js | 81 ++--- .../panel-body/queen-container/component.js | 14 +- .../sychronizeWrapper/sychronizeDialog.js | 21 +- src/i18n/addressMessage.js | 6 + src/i18n/ageGroupsMessage.js | 10 - src/i18n/buttonMessage.js | 3 + src/i18n/contactAttemptMessage.js | 27 +- src/i18n/contactOutcomeMessage.js | 28 +- src/i18n/detailsMessage.js | 3 +- src/i18n/dictionary.js | 16 +- src/i18n/identificationMessage.js | 25 ++ src/i18n/mediumMessage.js | 8 + src/i18n/navigationMessage.js | 3 +- src/i18n/toDoMessage.js | 2 +- src/theme.js | 62 ++-- .../enum/ContactAttemptConfigurationEnum.js | 4 + src/utils/enum/ContactAttemptEnum.js | 61 +++- .../enum/ContactOutcomeConfigurationEnum.js | 4 + src/utils/enum/ContactOutcomeEnum.js | 51 ++- src/utils/enum/IdentificationAnswersEnum.js | 120 +++++++ .../enum/IdentificationConfigurationEnum.js | 4 + src/utils/enum/IdentificationQuestionsEnum.js | 16 + src/utils/enum/MediumEnum.js | 28 ++ src/utils/enum/SUStateEnum.js | 8 +- src/utils/enum/formEnum.js | 1 + src/utils/functions/dateFunctions.js | 18 + .../functions/identificationFunctions.js | 225 ++++++++++++ src/utils/functions/surveyUnitFunctions.js | 162 +++++++-- src/utils/hooks/database.js | 1 - src/utils/hooks/useCounter.js | 46 --- src/utils/hooks/useTimer.js | 23 -- src/utils/icons/materialIcons.js | 80 ++++- src/utils/indexeddb/idb-config.js | 2 + src/utils/indexeddb/schema-3.json | 3 + yarn.lock | 93 ++++- 93 files changed, 2744 insertions(+), 1812 deletions(-) delete mode 100644 src/components/common/IconStatus.js create mode 100644 src/components/common/sharedComponents/EditableBooleanField.js create mode 100644 src/components/common/sharedComponents/EditableTextField.js create mode 100644 src/components/common/sharedComponents/EditableTextFieldWithClickableIcon.js create mode 100644 src/components/common/sharedComponents/GenericTile.js create mode 100644 src/components/common/sharedComponents/IconButton.js create mode 100644 src/components/common/sharedComponents/LabelledBoolean.js create mode 100644 src/components/common/sharedComponents/LabelledSwitch.js create mode 100644 src/components/common/sharedComponents/LabelledText.js create mode 100644 src/components/common/sharedComponents/LabelledTextWithClickableIcon.js delete mode 100644 src/components/panel-body/UESpage/transmitForm/component.js delete mode 100644 src/components/panel-body/UEpage/atomicInfoTile.js delete mode 100644 src/components/panel-body/UEpage/contacts/contactAttempts/upDownCounter.js create mode 100644 src/components/panel-body/UEpage/contacts/contactOutcome/contactOutcomeLine.js create mode 100644 src/components/panel-body/UEpage/details/Individual.js delete mode 100644 src/components/panel-body/UEpage/details/detailTile.js delete mode 100644 src/components/panel-body/UEpage/details/phoneList.js delete mode 100644 src/components/panel-body/UEpage/details/phoneTile.js delete mode 100644 src/components/panel-body/UEpage/forms/mailForm.js delete mode 100644 src/components/panel-body/UEpage/forms/phoneForm.js create mode 100644 src/components/panel-body/UEpage/housing/component.js rename src/components/panel-body/{UESpage/transmitForm => UEpage/housing}/index.js (100%) create mode 100644 src/components/panel-body/UEpage/identification/clickableLine.js create mode 100644 src/components/panel-body/UEpage/identification/labelledCheckbox.js delete mode 100644 src/components/panel-body/UEpage/letters/component.js create mode 100644 src/components/panel-body/UEpage/navigation/tabPanel.js create mode 100644 src/components/panel-body/UEpage/navigation/tabSwipper.js create mode 100644 src/components/panel-body/UEpage/questionnaires/component.js rename src/components/panel-body/UEpage/{letters => questionnaires}/index.js (100%) delete mode 100644 src/components/panel-body/UEpage/ueSubInfoTile.js delete mode 100644 src/i18n/ageGroupsMessage.js create mode 100644 src/i18n/identificationMessage.js create mode 100644 src/i18n/mediumMessage.js create mode 100644 src/utils/enum/ContactAttemptConfigurationEnum.js create mode 100644 src/utils/enum/ContactOutcomeConfigurationEnum.js create mode 100644 src/utils/enum/IdentificationAnswersEnum.js create mode 100644 src/utils/enum/IdentificationConfigurationEnum.js create mode 100644 src/utils/enum/IdentificationQuestionsEnum.js create mode 100644 src/utils/enum/MediumEnum.js create mode 100644 src/utils/functions/dateFunctions.js create mode 100644 src/utils/functions/identificationFunctions.js delete mode 100644 src/utils/hooks/useCounter.js delete mode 100644 src/utils/hooks/useTimer.js create mode 100644 src/utils/indexeddb/schema-3.json diff --git a/package.json b/package.json index 3292c2e01..31f92059c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pearl", - "version": "0.5.7", + "version": "0.6.0", "private": true, "dependencies": { "@date-io/date-fns": "1.x", @@ -25,6 +25,7 @@ "react-modal": "^3.11.1", "react-router-dom": "^5.2.0", "react-scripts": "4.0.3", + "react-swipeable-views": "^0.14.0", "workbox-cacheable-response": "^5.1.3", "workbox-core": "^5.1.3", "workbox-precaching": "^5.1.3", diff --git a/public/configuration.json b/public/configuration.json index 9b16ca41d..c834f809d 100644 --- a/public/configuration.json +++ b/public/configuration.json @@ -1,11 +1,11 @@ { - "QUEEN_URL": "https://queen.demo.dev.sspcloud.fr", + "QUEEN_URL": "http://localhost:5000", "_QUEEN_URL_COMMENT_": "Final URL of the Queen application", - "PEARL_API_URL": "https://api.organisation-collecte-enqueteurs.enquetes.developpement.insee.fr", + "PEARL_API_URL": "http://localhost:8080", "_PEARL_API_URL_COMMENT_": "url of Pearl API", - "PEARL_AUTHENTICATION_MODE": "anonymous", + "PEARL_AUTHENTICATION_MODE": "keycloak", "_PEARL_AUTHENTICATION_MODE_COMMENT": "The mode of authentication. Currently, App is supporting 'anonymous'", "CHAT_URL": "", "_CHAT_URL_COMMENT_": "url of Pearl Chat" diff --git a/src/App.js b/src/App.js index 3cd2bc939..02172e9e4 100644 --- a/src/App.js +++ b/src/App.js @@ -1,9 +1,6 @@ -import React from 'react'; -import CssBaseline from '@material-ui/core/CssBaseline'; -import { ThemeProvider } from '@material-ui/core/styles'; - import { Route, useLocation } from 'react-router-dom'; +import CssBaseline from '@material-ui/core/CssBaseline'; import D from 'i18n'; import { DatabaseConsole } from 'components/panel-body/databaseConsole'; import Home from 'components/panel-body/home'; @@ -11,8 +8,10 @@ import Notification from 'components/common/Notification'; import { NotificationWrapper } from 'components/notificationWrapper'; import Palette from 'components/common/palette'; import Preloader from 'components/common/loader'; +import React from 'react'; import { ResetData } from 'components/panel-body/resetData'; import SynchronizeWrapper from 'components/sychronizeWrapper'; +import { ThemeProvider } from '@material-ui/core/styles'; import theme from './theme'; import { useAuth } from 'utils/auth/initAuth'; import { useServiceWorker } from 'utils/hooks/useServiceWorker'; diff --git a/src/components/common/IconStatus.js b/src/components/common/IconStatus.js deleted file mode 100644 index 390e5d55b..000000000 --- a/src/components/common/IconStatus.js +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import CheckCircleOutline from '@material-ui/icons/CheckCircleOutline'; -import Warning from '@material-ui/icons/Warning'; -import Clear from '@material-ui/icons/Clear'; -import { makeStyles } from '@material-ui/core/styles'; - -const useStyles = makeStyles(theme => ({ - success: { - color: theme.palette.success.main, - }, - failure: { - color: theme.palette.error.main, - }, - warning: { - color: theme.palette.warning.main, - }, -})); - -export const IconStatus = ({ type, ...other }) => { - const classes = useStyles(); - if (type === 'success') - return ; - if (type === 'error') return ; - if (type === 'warning') - return ; - return <>; -}; diff --git a/src/components/common/Notification/notificationItem.js b/src/components/common/Notification/notificationItem.js index b88272f88..2af48960f 100644 --- a/src/components/common/Notification/notificationItem.js +++ b/src/components/common/Notification/notificationItem.js @@ -1,17 +1,16 @@ -import React, { useContext } from 'react'; -import Link from '@material-ui/core/Link'; -import Typography from '@material-ui/core/Typography'; -import { makeStyles } from '@material-ui/core/styles'; - import { NOTIFICATION_TYPE_MANAGEMENT, NOTIFICATION_TYPE_SYNC } from 'utils/constants'; +import React, { useContext } from 'react'; import D from 'i18n'; -import { FiberManualRecord } from '@material-ui/icons'; +import FiberManualRecord from '@material-ui/icons/FiberManualRecord'; +import Link from '@material-ui/core/Link'; import { NavigationContext } from '../navigation/component'; import { NotificationWrapperContext } from 'components/notificationWrapper'; import { SynchronizeWrapperContext } from 'components/sychronizeWrapper'; +import Typography from '@material-ui/core/Typography'; import { dateFnsLocal } from 'utils'; import { formatDistance } from 'date-fns'; +import { makeStyles } from '@material-ui/core/styles'; import syncReportIdbService from 'utils/indexeddb/services/syncReport-idb-service'; const useStyles = makeStyles(theme => ({ diff --git a/src/components/common/navigation/component.js b/src/components/common/navigation/component.js index ceafd8a8a..383b3367c 100644 --- a/src/components/common/navigation/component.js +++ b/src/components/common/navigation/component.js @@ -1,39 +1,33 @@ -import React, { useContext, useEffect, useState } from 'react'; import { NavLink, Route } from 'react-router-dom'; +import React, { useContext } from 'react'; + import AppBar from '@material-ui/core/AppBar'; import Badge from '@material-ui/core/Badge'; import Card from '@material-ui/core/Card'; import CardMedia from '@material-ui/core/CardMedia'; import ClickAwayListener from '@material-ui/core/ClickAwayListener'; +import D from 'i18n'; import Fade from '@material-ui/core/Fade'; import IconButton from '@material-ui/core/IconButton'; -import Popper from '@material-ui/core/Popper'; -import Tooltip from '@material-ui/core/Tooltip'; import MenuIcon from '@material-ui/icons/Menu'; -import Notifications from '@material-ui/icons/Notifications'; -import Toolbar from '@material-ui/core/Toolbar'; -import Typography from '@material-ui/core/Typography'; -import { makeStyles } from '@material-ui/core/styles'; - -import D from 'i18n'; -import InfoTile from 'components/panel-body/UEpage/infoTile/infoTile'; import { NotificationWrapperContext } from 'components/notificationWrapper'; +import Notifications from '@material-ui/icons/Notifications'; import { NotificationsRoot } from '../Notification/notificationsRoot'; import OnlineStatus from '../online-status'; import { PEARL_USER_KEY } from 'utils/constants'; +import Popper from '@material-ui/core/Popper'; import PropTypes from 'prop-types'; import SearchBar from '../search/component'; import Synchronize from 'components/common/synchronize'; +import Toolbar from '@material-ui/core/Toolbar'; +import Tooltip from '@material-ui/core/Tooltip'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles } from '@material-ui/core/styles'; export const NavigationContext = React.createContext(); -const Navigation = ({ location, textSearch, setTextSearch, setOpenDrawer }) => { +const Navigation = ({ textSearch, setTextSearch, setOpenDrawer }) => { const { unReadNotificationsNumber } = useContext(NotificationWrapperContext); - const [disabled, setDisable] = useState(location.pathname.startsWith('/queen')); - - useEffect(() => { - setDisable(location.pathname.startsWith('/queen')); - }, [location]); const getName = () => { const interviewerFromLocalStorage = window.localStorage.getItem(PEARL_USER_KEY); @@ -65,7 +59,7 @@ const Navigation = ({ location, textSearch, setTextSearch, setOpenDrawer }) => { flex: '1 1 auto', }, notificationsIcon: { - fontSize: 'xxx-large', + fontSize: 'xx-large', color: theme.palette.secondary.main, '&:hover': { color: theme.palette.secondary.dark }, }, @@ -78,6 +72,8 @@ const Navigation = ({ location, textSearch, setTextSearch, setOpenDrawer }) => { '&:focus, &:hover': { backgroundColor: theme.palette.primary.main, }, + marginLeft: '1em', + marginRight: '1em', }, notif: { zIndex: 1200, @@ -103,10 +99,19 @@ const Navigation = ({ location, textSearch, setTextSearch, setOpenDrawer }) => { + + + + + setOpenDrawer(true)} @@ -114,15 +119,11 @@ const Navigation = ({ location, textSearch, setTextSearch, setOpenDrawer }) => { - - - - - + Sabiane + + Collecte + +
{ )} /> - } />
@@ -165,7 +165,7 @@ const Navigation = ({ location, textSearch, setTextSearch, setOpenDrawer }) => { {getName()}
- +
diff --git a/src/components/common/search/component.js b/src/components/common/search/component.js index 39fac0248..76336eaeb 100644 --- a/src/components/common/search/component.js +++ b/src/components/common/search/component.js @@ -1,41 +1,28 @@ -import React from 'react'; import InputBase from '@material-ui/core/InputBase'; -import { makeStyles } from '@material-ui/core/styles'; import PropTypes from 'prop-types'; +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles(theme => ({ + search: { + [theme.breakpoints.up('sm')]: { + marginLeft: theme.spacing(3), + width: '75%', + }, + }, + inputInput: { + padding: theme.spacing(1, 1, 1, 1), + border: 'solid 1px black', + }, +})); const SearchBar = ({ textSearch, setTextSearch }) => { + const classes = useStyles(); const handleChange = e => { const txt = e.target.value; setTextSearch(txt); }; - const useStyles = makeStyles(theme => ({ - search: { - position: 'relative', - marginRight: theme.spacing(2), - marginLeft: 0, - height: '2em', - [theme.breakpoints.up('sm')]: { - marginLeft: theme.spacing(3), - width: '75%', - }, - }, - - inputRoot: { - color: 'inherit', - height: '2em', - }, - inputInput: { - padding: theme.spacing(1, 1, 1, 0), - paddingLeft: `1em`, - // backgroundColor: theme.color.backgroundColor, - border: 'solid 1px black', - color: 'black', - marginRight: '1em', - }, - })); - const classes = useStyles(); - return ( ({ + row: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + gap: '1em', + }, +})); + +export const EditableBooleanField = ({ id, label, defaultValue = undefined, onChangeFunction }) => { + const classes = useStyles(); + return ( +
+ {label} + onChangeFunction(event)} + /> +
+ ); +}; diff --git a/src/components/common/sharedComponents/EditableTextField.js b/src/components/common/sharedComponents/EditableTextField.js new file mode 100644 index 000000000..3b235522f --- /dev/null +++ b/src/components/common/sharedComponents/EditableTextField.js @@ -0,0 +1,32 @@ +import React from 'react'; +import TextField from '@material-ui/core/TextField'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles } from '@material-ui/core'; + +const useStyles = makeStyles(() => ({ + row: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + gap: '1em', + }, +})); + +export const EditableTextField = ({ id, label, defaultValue = '', onChangeFunction }) => { + const classes = useStyles(); + return ( +
+ {label} + onChangeFunction(event)} + /> +
+ ); +}; diff --git a/src/components/common/sharedComponents/EditableTextFieldWithClickableIcon.js b/src/components/common/sharedComponents/EditableTextFieldWithClickableIcon.js new file mode 100644 index 000000000..bffcc2281 --- /dev/null +++ b/src/components/common/sharedComponents/EditableTextFieldWithClickableIcon.js @@ -0,0 +1,35 @@ +import { EditableTextField } from './EditableTextField'; +import React from 'react'; +import { makeStyles } from '@material-ui/core'; + +const useStyles = makeStyles(theme => ({ + row: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + gap: '1em', + }, +})); + +export const EditableTextFieldWithClickableIcon = ({ + id, + label, + defaultValue = '', + icons = [], + onChangeFunction, +}) => { + const classes = useStyles(); + return ( +
+ + {icons.map(Icon => ( + + ))} +
+ ); +}; diff --git a/src/components/common/sharedComponents/GenericTile.js b/src/components/common/sharedComponents/GenericTile.js new file mode 100644 index 000000000..8bc0f53a4 --- /dev/null +++ b/src/components/common/sharedComponents/GenericTile.js @@ -0,0 +1,46 @@ +import { Paper } from '@material-ui/core'; +import PropTypes from 'prop-types'; +import React from 'react'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles(() => ({ + root: { + display: 'flex', + flexDirection: 'column', + gap: '1em', + padding: '1.5em', + borderRadius: '15px', + margin: '2em', + height: 'max-content', + }, + row: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-between', + gap: '1em', + }, +})); + +const GenericTile = ({ title, children, icon: Icon, editionIcon: EditionIcon }) => { + const classes = useStyles(); + return ( + +
+
+ + {title} +
+ {EditionIcon && } +
+ {children} +
+ ); +}; + +export default GenericTile; +GenericTile.propTypes = { + title: PropTypes.string.isRequired, + children: PropTypes.node.isRequired, + icon: PropTypes.func.isRequired, +}; diff --git a/src/components/common/sharedComponents/IconButton.js b/src/components/common/sharedComponents/IconButton.js new file mode 100644 index 000000000..a09e469ef --- /dev/null +++ b/src/components/common/sharedComponents/IconButton.js @@ -0,0 +1,32 @@ +import AddIcon from '@material-ui/icons/Add'; +import AssignmentIcon from '@material-ui/icons/Assignment'; +import Button from '@material-ui/core/Button'; +import CheckIcon from '@material-ui/icons/Check'; +import ChevronRightIcon from '@material-ui/icons/ChevronRight'; +import CloseIcon from '@material-ui/icons/Close'; +import SendIcon from '@material-ui/icons/Send'; + +const ButtonIcons = { + questionnaire: , + transmit: , + rightArrow: , + add: , + check: , + close: , +}; + +const IconButton = ({ iconType, label, onClickFunction, hasArrow, disabled = false }) => { + return ( + + ); +}; + +export default IconButton; diff --git a/src/components/common/sharedComponents/LabelledBoolean.js b/src/components/common/sharedComponents/LabelledBoolean.js new file mode 100644 index 000000000..fd7a01c7f --- /dev/null +++ b/src/components/common/sharedComponents/LabelledBoolean.js @@ -0,0 +1,26 @@ +import MaterialIcons from 'utils/icons/materialIcons'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles(theme => ({ + row: { + display: 'flex', + flexDirection: 'row', + }, + rightMargin: { marginRight: '1em' }, +})); + +const LabelledBoolean = ({ labelText, value = undefined }) => { + const classes = useStyles(); + + return ( +
+ + {labelText} + + {value !== undefined && } +
+ ); +}; + +export default LabelledBoolean; diff --git a/src/components/common/sharedComponents/LabelledSwitch.js b/src/components/common/sharedComponents/LabelledSwitch.js new file mode 100644 index 000000000..d76a24161 --- /dev/null +++ b/src/components/common/sharedComponents/LabelledSwitch.js @@ -0,0 +1,27 @@ +import Switch from '@material-ui/core/Switch'; +import Typography from '@material-ui/core/Typography'; +import { makeStyles } from '@material-ui/core'; + +const useStyles = makeStyles(() => ({ + row: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + }, + marginRight: { + marginRight: '1em', + }, +})); + +export const LabelledSwitch = ({ labelText, text, value, onChangeFunction }) => { + const classes = useStyles(); + return ( +
+ + {labelText} + + {text} + +
+ ); +}; diff --git a/src/components/common/sharedComponents/LabelledText.js b/src/components/common/sharedComponents/LabelledText.js new file mode 100644 index 000000000..cd446649b --- /dev/null +++ b/src/components/common/sharedComponents/LabelledText.js @@ -0,0 +1,41 @@ +import Typography from '@material-ui/core/Typography'; +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles(theme => ({ + row: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + maxWidth: '20em', + }, + column: { + display: 'flex', + flexDirection: 'column', + marginRight: '1em', + }, + overflow: { + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + overflow: 'hidden', + }, +})); + +const LabelledText = ({ labelText, value }) => { + const classes = useStyles(); + return ( +
+
+ {Array.isArray(labelText) ? ( + labelText.map(labelTextLine => ( + {labelTextLine} + )) + ) : ( + {labelText} + )} +
+ {value} +
+ ); +}; + +export default LabelledText; diff --git a/src/components/common/sharedComponents/LabelledTextWithClickableIcon.js b/src/components/common/sharedComponents/LabelledTextWithClickableIcon.js new file mode 100644 index 000000000..ebebc9b6b --- /dev/null +++ b/src/components/common/sharedComponents/LabelledTextWithClickableIcon.js @@ -0,0 +1,21 @@ +import LabelledText from './LabelledText'; +import { makeStyles } from '@material-ui/core'; + +const useStyles = makeStyles(theme => ({ + row: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + }, +})); + +export const LabelledTextWithClickableIcon = ({ labelText, value, icon: Icon }) => { + const classes = useStyles(); + + return ( +
+ + +
+ ); +}; diff --git a/src/components/common/synchronize/component.js b/src/components/common/synchronize/component.js index f5696353a..da50441fb 100644 --- a/src/components/common/synchronize/component.js +++ b/src/components/common/synchronize/component.js @@ -1,19 +1,15 @@ import React, { useContext } from 'react'; -import Tooltip from '@material-ui/core/Tooltip'; -import IconButton from '@material-ui/core/IconButton'; -import { makeStyles } from '@material-ui/core/styles'; -import SyncIcon from 'utils/icons/SyncIcon'; -import PropTypes from 'prop-types'; + import { AppContext } from 'Root'; import D from 'i18n'; +import IconButton from '@material-ui/core/IconButton'; +import MaterialIcons from 'utils/icons/materialIcons'; +import PropTypes from 'prop-types'; import { SynchronizeWrapperContext } from 'components/sychronizeWrapper'; +import Tooltip from '@material-ui/core/Tooltip'; +import { makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles(theme => ({ - dialogPaper: { - padding: '1em', - borderRadius: '15px', - textAlign: 'center', - }, noVisibleFocus: { '&:focus, &:hover': { backgroundColor: theme.palette.primary.main, @@ -28,7 +24,7 @@ const Synchronize = ({ materialClass }) => { const classes = useStyles(); return ( - + { aria-label="launch synchronization" onClick={syncFunction} > - + ); diff --git a/src/components/panel-body/UESpage/material/surveyUnitCard.js b/src/components/panel-body/UESpage/material/surveyUnitCard.js index 53cd6a568..3f42c4fef 100644 --- a/src/components/panel-body/UESpage/material/surveyUnitCard.js +++ b/src/components/panel-body/UESpage/material/surveyUnitCard.js @@ -1,25 +1,26 @@ -import React from 'react'; +import { + getLastState, + getprivilegedPerson, + isSelectable, +} from 'utils/functions/surveyUnitFunctions'; + import Card from '@material-ui/core/Card'; import CardContent from '@material-ui/core/CardContent'; -import { makeStyles } from '@material-ui/core/styles'; -import Typography from '@material-ui/core/Typography'; import CheckCircleOutlineIcon from '@material-ui/icons/CheckCircleOutline'; -import WarningIcon from '@material-ui/icons/Warning'; +import D from 'i18n'; import LocationOnIcon from '@material-ui/icons/LocationOn'; import PersonIcon from '@material-ui/icons/Person'; +import PropTypes from 'prop-types'; import RadioButtonUncheckedSharpIcon from '@material-ui/icons/RadioButtonUncheckedSharp'; +import React from 'react'; import ScheduleIcon from '@material-ui/icons/Schedule'; -import { intervalInDays } from 'utils/functions'; +import Tooltip from '@material-ui/core/Tooltip'; +import Typography from '@material-ui/core/Typography'; +import WarningIcon from '@material-ui/icons/Warning'; import { convertSUStateInToDo } from 'utils/functions/convertSUStateInToDo'; -import { - getLastState, - getprivilegedPerson, - isSelectable, -} from 'utils/functions/surveyUnitFunctions'; -import PropTypes from 'prop-types'; +import { intervalInDays } from 'utils/functions'; +import { makeStyles } from '@material-ui/core/styles'; import { useHistory } from 'react-router-dom'; -import { Tooltip } from '@material-ui/core'; -import D from 'i18n'; const useStyles = makeStyles(theme => ({ root: { @@ -101,17 +102,17 @@ const SurveyUnitCard = ({ surveyUnit, inaccessible = false }) => { } = surveyUnit; const privilegedPerson = getprivilegedPerson(surveyUnit); - // persons.find(p => p.privileged); - const { firstName, lastName } = privilegedPerson ? privilegedPerson : persons[0]; + const { firstName, lastName } = privilegedPerson ?? persons[0]; const lastState = getLastState(surveyUnit); const todo = convertSUStateInToDo(lastState.type); const { order, value: toDoLabel } = todo; const nbJoursRestant = intervalInDays(surveyUnit); + const openSurveyUnitPage = id => history.push(`/survey-unit/${id}/details?panel=0`); return ( (active ? history.push(`/survey-unit/${id}/details`) : {})} + onClick={() => (active ? openSurveyUnitPage(id) : {})} elevation={0} > diff --git a/src/components/panel-body/UESpage/transmitForm/component.js b/src/components/panel-body/UESpage/transmitForm/component.js deleted file mode 100644 index ca1aa0588..000000000 --- a/src/components/panel-body/UESpage/transmitForm/component.js +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import D from 'i18n'; - -const Form = ({ closeModal, summary }) => { - const { ok, ko } = summary; - const txtOk = `${D.transmitOk} : ${ok}`; - const txtKo = `${D.transmitKo} : ${ko}`; - return ( - <> -
{txtOk}
-
{txtKo}
- - - ); -}; - -export default Form; -Form.propTypes = { - closeModal: PropTypes.func.isRequired, - summary: PropTypes.object.isRequired, -}; diff --git a/src/components/panel-body/UEpage/atomicInfoTile.js b/src/components/panel-body/UEpage/atomicInfoTile.js deleted file mode 100644 index 5c1a68a49..000000000 --- a/src/components/panel-body/UEpage/atomicInfoTile.js +++ /dev/null @@ -1,83 +0,0 @@ -import React from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import Paper from '@material-ui/core/Paper'; -import Typography from '@material-ui/core/Typography'; -import MaterialIcons from 'utils/icons/materialIcons'; -import PropTypes from 'prop-types'; - -const useStyles = makeStyles(theme => ({ - root: { - padding: 8, - borderRadius: 15, - '&:hover': { cursor: 'pointer' }, - border: ' LightGray solid 1px', - minHeight: 130, - width: 'max-content', - minWidth: '200px', - display: 'flex', - flexDirection: 'column', - margin: '10px', - }, - firstLine: { - alignSelf: 'flex-end', - }, - row: { - display: 'flex', - flexDirection: 'row', - }, - column: { - display: 'flex', - flexDirection: 'column', - }, - label: { fontWeight: 'bold', marginRight: '0.5em' }, - invisible: { color: theme.palette.primary.main }, -})); - -const AtomicInfoTile = ({ iconType, data, onClickFunction }) => { - const classes = useStyles(); - const labels = data.reduce( - (arr, { label }) => [ - ...arr, - - {label !== undefined ? label : ''} - , - ], - [] - ); - const values = data.reduce((arr, { label, value, favorite }) => { - const valid = value !== undefined && value !== ''; - return [ - ...arr, -
- {favorite !== undefined && } - - {value !== undefined && value !== '' ? value : '-'} - -
, - ]; - }, []); - - return ( - -
- -
-
-
- {[...labels]} -
-
- {[...values]} -
-
-
- ); -}; - -export default AtomicInfoTile; -AtomicInfoTile.propTypes = { - iconType: PropTypes.string.isRequired, - data: PropTypes.arrayOf(PropTypes.shape({ key: PropTypes.string, value: PropTypes.string })) - .isRequired, - onClickFunction: PropTypes.func.isRequired, -}; diff --git a/src/components/panel-body/UEpage/comments/comment/component.js b/src/components/panel-body/UEpage/comments/comment/component.js index ada422a65..3389342d6 100644 --- a/src/components/panel-body/UEpage/comments/comment/component.js +++ b/src/components/panel-body/UEpage/comments/comment/component.js @@ -64,10 +64,10 @@ const Comment = ({ editable }) => { defaultValue={interviewerComment} onBlur={onBlur} onChange={onChange} - maxLength={240} + maxLength={999} /> - {`${interviewerComment.length}/240`} + {`${interviewerComment.length}/999`} ); }; diff --git a/src/components/panel-body/UEpage/component.js b/src/components/panel-body/UEpage/component.js index 63c5867fb..6b99f0313 100644 --- a/src/components/panel-body/UEpage/component.js +++ b/src/components/panel-body/UEpage/component.js @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react'; import { addNewState, getLastState } from 'utils/functions'; import { useHistory, useParams } from 'react-router-dom'; + import D from 'i18n'; import PropTypes from 'prop-types'; import Router from './router'; @@ -61,5 +62,4 @@ const UEPage = ({ match }) => { export default UEPage; UEPage.propTypes = { match: PropTypes.shape({}).isRequired, - refresh: PropTypes.func.isRequired, }; diff --git a/src/components/panel-body/UEpage/contacts/contactAttempts/component.js b/src/components/panel-body/UEpage/contacts/contactAttempts/component.js index 284704dfb..75e009392 100644 --- a/src/components/panel-body/UEpage/contacts/contactAttempts/component.js +++ b/src/components/panel-body/UEpage/contacts/contactAttempts/component.js @@ -1,27 +1,18 @@ import React, { useContext, useEffect, useState } from 'react'; -import { makeStyles } from '@material-ui/core/styles'; + +import ContactAttemptLine from './contactAttemptLine'; import Paper from '@material-ui/core/Paper'; -import Typography from '@material-ui/core/Typography'; -import formEnum from 'utils/enum/formEnum'; -import { getSortedContactAttempts } from 'utils/functions'; -import D from 'i18n'; import PropTypes from 'prop-types'; import SurveyUnitContext from '../../UEContext'; -import ContactAttemptLine from './contactAttemptLine'; +import formEnum from 'utils/enum/formEnum'; +import { getSortedContactAttempts } from 'utils/functions'; +import { makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles(() => ({ column: { display: 'flex', flexDirection: 'column', - cursor: 'pointer', - marginRight: '1em', - marginTop: '1em', - padding: '1em', - boxShadow: 'unset', - border: 'LightGray solid 1px', - borderRadius: '15px', - minWidth: '300px', - minHeight: '200px', + gap: '0.25em', }, })); @@ -37,18 +28,17 @@ const ContactAttempts = ({ selectFormType, setInjectableData }) => { const classes = useStyles(); return ( - { - selectFormType(formEnum.CONTACT_ATTEMPT, false); - setInjectableData({ status: 'NOC', date: new Date().getTime() }); - }} - > - {D.contactAttempts} + {Array.isArray(contactAttempts) && contactAttempts.length > 0 && contactAttempts.map(contAtt => ( - + { + selectFormType(formEnum.CONTACT_ATTEMPT, true); + setInjectableData(contAtt); + }} + /> ))} ); diff --git a/src/components/panel-body/UEpage/contacts/contactAttempts/contactAttemptLine.js b/src/components/panel-body/UEpage/contacts/contactAttempts/contactAttemptLine.js index 52602b9d1..a6f4bb18a 100644 --- a/src/components/panel-body/UEpage/contacts/contactAttempts/contactAttemptLine.js +++ b/src/components/panel-body/UEpage/contacts/contactAttempts/contactAttemptLine.js @@ -1,56 +1,59 @@ +import D from 'i18n'; +import MaterialIcons from 'utils/icons/materialIcons'; +import Paper from '@material-ui/core/Paper'; +import PropTypes from 'prop-types'; import React from 'react'; -import { makeStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; -import DeleteIcon from '@material-ui/icons/Delete'; import { findContactAttemptValueByType } from 'utils/enum/ContactAttemptEnum'; -import format from 'date-fns/format'; -import { fr } from 'date-fns/locale'; -import PropTypes from 'prop-types'; +import { findMediumValueByType } from 'utils/enum/MediumEnum'; +import { getDateAttributes } from 'utils/functions/dateFunctions'; +import { grey } from '@material-ui/core/colors'; +import { makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles(() => ({ - button: { - '&:hover': { cursor: 'pointer' }, - marginLeft: '0.5em', - }, alignEnd: { alignSelf: 'flex-end', }, + root: { + display: 'flex', + height: 'max-content', + justifyContent: 'space-between', + borderRadius: '15px', + backgroundColor: grey[100], + padding: '0.5em', + '&:not(:last-child)': { marginBottom: '1em' }, + }, column: { display: 'flex', flexDirection: 'column', }, - flex: { - display: 'flex', - justifyContent: 'space-between', + fullWidth: { + grow: '1', }, - top: { marginTop: '0.5em' }, })); -const ContactAttemptLine = ({ contactAttempt, deleteParams, selected }) => { +const ContactAttemptLine = ({ contactAttempt, deleteFunction }) => { const classes = useStyles(); if (contactAttempt === undefined) return ''; - const { deleteFunction, deleteIsAvailable } = deleteParams; + const { dayOfWeek, twoDigitdayNumber, month, hour, minutes } = getDateAttributes( + contactAttempt.date + ); - const dayOfWeek = format(new Date(contactAttempt.date), 'EEEE', { locale: fr }); - const date = format(new Date(contactAttempt.date), 'dd/MM/yyyy'); - const hour = format(new Date(contactAttempt.date), 'HH'); - const minutes = format(new Date(contactAttempt.date), 'mm'); + const upcasedDayOfWeek = dayOfWeek[0].toUpperCase() + dayOfWeek.slice(1); + const date = `${twoDigitdayNumber} ${month}`; return ( -
- - {`${dayOfWeek} ${date} - ${hour}h${minutes} - ${findContactAttemptValueByType( - contactAttempt.status - )}`} - - {deleteIsAvailable && ( - - )} -
+ +
+ + {`${upcasedDayOfWeek} ${date} ${D.at} ${hour}h${minutes} - ${findMediumValueByType( + contactAttempt.medium + )}`} + + {findContactAttemptValueByType(contactAttempt.status)} +
+ {deleteFunction && } +
); }; @@ -67,8 +70,3 @@ ContactAttemptLine.propTypes = { }), selected: PropTypes.bool, }; -ContactAttemptLine.defaultProps = { - deleteParams: { deleteFunction: () => {}, deleteIsAvailable: false }, - selected: false, - contactAttempt: { date: new Date().getTime(), id: 999, status: 'NOC' }, -}; diff --git a/src/components/panel-body/UEpage/contacts/contactAttempts/upDownCounter.js b/src/components/panel-body/UEpage/contacts/contactAttempts/upDownCounter.js deleted file mode 100644 index 86248363d..000000000 --- a/src/components/panel-body/UEpage/contacts/contactAttempts/upDownCounter.js +++ /dev/null @@ -1,82 +0,0 @@ -import React, { useEffect } from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import IconButton from '@material-ui/core/IconButton'; -import ExpandLessIcon from '@material-ui/icons/ExpandLess'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; -import useCounter from 'utils/hooks/useCounter'; -import { getHours, getMinutes, setHours, setMinutes } from 'date-fns'; -import PropTypes from 'prop-types'; - -const useStyles = makeStyles(() => ({ - icon: { - fontSize: '1.5em', - }, - column: { - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - }, -})); - -const UpDownCounter = ({ selectedDate, handleDateChange, type }) => { - const classes = useStyles(); - - const isHours = type === 'hours'; - const getData = () => (isHours ? getHours(selectedDate) : getMinutes(selectedDate)); - - const counterParams = { - initialValue: getData(), - interval: isHours ? 13 : 10, - deltaValue: isHours ? 2 : 5, - }; - const { - counter: { current: counter }, - startAdd, - startRemove, - stop, - } = useCounter(counterParams); - //* keep value in [min-max] range to prevent hour/day leap - - const clean = value => (isHours ? (value + 24) % 24 : (value + 60) % 60); - const setData = value => { - const cleanedValue = clean(value); - return isHours ? setHours(selectedDate, cleanedValue) : setMinutes(selectedDate, cleanedValue); - }; - useEffect(() => { - handleDateChange(setData(counter)); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [counter, handleDateChange, isHours]); - - return ( -
- - - - {getData() - .toString() - .padStart(2, '0')} - - - -
- ); -}; - -export default UpDownCounter; -UpDownCounter.propTypes = { - selectedDate: PropTypes.shape({}).isRequired, - handleDateChange: PropTypes.func.isRequired, - type: PropTypes.string.isRequired, -}; diff --git a/src/components/panel-body/UEpage/contacts/contactOutcome/component.js b/src/components/panel-body/UEpage/contacts/contactOutcome/component.js index 09d3788c2..8e89c4aa4 100644 --- a/src/components/panel-body/UEpage/contacts/contactOutcome/component.js +++ b/src/components/panel-body/UEpage/contacts/contactOutcome/component.js @@ -1,74 +1,77 @@ import React, { useContext, useEffect, useState } from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import Paper from '@material-ui/core/Paper'; -import Typography from '@material-ui/core/Typography'; + +import AddIcon from '@material-ui/icons/Add'; +import ContactAttempts from '../contactAttempts'; +import ContactOutcomeLine from './contactOutcomeLine'; import D from 'i18n'; +import { Divider } from '@material-ui/core'; +import IconButton from 'components/common/sharedComponents/IconButton'; +import Paper from '@material-ui/core/Paper'; import PropTypes from 'prop-types'; import SurveyUnitContext from '../../UEContext'; -import { findContactOutcomeValueByType } from 'utils/enum/ContactOutcomeEnum'; import formEnum from 'utils/enum/formEnum'; +import { makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles(() => ({ column: { display: 'flex', flexDirection: 'column', - cursor: 'pointer', - marginRight: '1em', - marginTop: '1em', - padding: '1em', - boxShadow: 'unset', - border: 'LightGray solid 1px', - borderRadius: '15px', - minWidth: '300px', - minHeight: '200px', + gap: '1em', }, - upDownMargin: { - marginTop: '1em', - marginBottom: '1em', + row: { + display: 'flex', + flexDirection: 'row', + gap: '1em', }, })); -const ContactOutcome = ({ selectFormType }) => { - const contextSu = useContext(SurveyUnitContext); - const { surveyUnit } = contextSu; +const ContactOutcome = ({ selectFormType, setInjectableData }) => { + const classes = useStyles(); + const { surveyUnit } = useContext(SurveyUnitContext); + const { contactAttempts } = surveyUnit; - const defaultContactOutcome = - surveyUnit.contactOutcome !== undefined && surveyUnit.contactOutcome !== null - ? surveyUnit.contactOutcome - : { - date: new Date().getTime(), - type: undefined, - totalNumberOfContactAttempts: '0', - }; + const defaultContactOutcome = surveyUnit.contactOutcome ?? { + date: new Date().getTime(), + type: undefined, + totalNumberOfContactAttempts: '0', + }; const [contactOutcome, setContactOutcome] = useState(defaultContactOutcome); useEffect(() => { setContactOutcome( - surveyUnit.contactOutcome !== undefined && surveyUnit.contactOutcome !== null - ? surveyUnit.contactOutcome - : { - date: new Date().getTime(), - type: undefined, - totalNumberOfContactAttempts: '0', - } + surveyUnit.contactOutcome ?? { + date: new Date().getTime(), + type: undefined, + totalNumberOfContactAttempts: '0', + } ); }, [surveyUnit]); - const outcomeValue = findContactOutcomeValueByType(contactOutcome.type); - const classes = useStyles(); + const isSeparator = contactOutcome.type && contactAttempts?.length > 0; + return ( - { - selectFormType(formEnum.CONTACT_OUTCOME, true); - }} - > - {D.contactOutcome} - {outcomeValue} - - {contactOutcome.totalNumberOfContactAttempts > 0 && - `> ${contactOutcome.totalNumberOfContactAttempts} ${D.contactOutcomeAttempts}`} - + + + {isSeparator && } + +
+ { + selectFormType(formEnum.CONTACT_ATTEMPT, true); + }} + /> + + } + onClickFunction={() => { + selectFormType(formEnum.CONTACT_OUTCOME, true); + }} + > +
); }; diff --git a/src/components/panel-body/UEpage/contacts/contactOutcome/contactOutcomeLine.js b/src/components/panel-body/UEpage/contacts/contactOutcome/contactOutcomeLine.js new file mode 100644 index 000000000..d02e1c011 --- /dev/null +++ b/src/components/panel-body/UEpage/contacts/contactOutcome/contactOutcomeLine.js @@ -0,0 +1,55 @@ +import MaterialIcons from 'utils/icons/materialIcons'; +import Paper from '@material-ui/core/Paper'; +import PropTypes from 'prop-types'; +import React from 'react'; +import Typography from '@material-ui/core/Typography'; +import { findContactOutcomeValueByType } from 'utils/enum/ContactOutcomeEnum'; +import { getDateAttributes } from 'utils/functions/dateFunctions'; +import { grey } from '@material-ui/core/colors'; +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles(() => ({ + root: { + display: 'flex', + borderRadius: '15px', + backgroundColor: grey[100], + padding: '0.5em', + justifyContent: 'space-between', + }, + overflow: { + maxWidth: '30em', + }, +})); + +const ContactOutcomeLine = ({ contactOutcome }) => { + const classes = useStyles(); + if (contactOutcome?.type === undefined) return ''; + + const { dayOfWeek, twoDigitdayNumber, month } = getDateAttributes(contactOutcome.date); + + const upcasedDayOfWeek = dayOfWeek[0].toUpperCase() + dayOfWeek.slice(1); + const date = `${twoDigitdayNumber} ${month}`; + + return ( + + + {`${upcasedDayOfWeek} ${date} - ${findContactOutcomeValueByType(contactOutcome.type)}`} + + + + ); +}; + +export default ContactOutcomeLine; +ContactOutcomeLine.propTypes = { + deleteParams: PropTypes.shape({ + deleteFunction: PropTypes.func.isRequired, + deleteIsAvailable: PropTypes.bool.isRequired, + }), + contactAttempt: PropTypes.shape({ + date: PropTypes.number.isRequired, + id: PropTypes.number, + status: PropTypes.string.isRequired, + }), + selected: PropTypes.bool, +}; diff --git a/src/components/panel-body/UEpage/details/Individual.js b/src/components/panel-body/UEpage/details/Individual.js new file mode 100644 index 000000000..30050f0f2 --- /dev/null +++ b/src/components/panel-body/UEpage/details/Individual.js @@ -0,0 +1,93 @@ +import { Paper, makeStyles } from '@material-ui/core'; +import { + displayAgeInYears, + getTitle, + sortPhoneNumbers, + toggleFavoriteEmailAndPersist, + toggleFavoritePhoneNumberAndPersist, +} from 'utils/functions'; + +import D from 'i18n'; +import LabelledText from 'components/common/sharedComponents/LabelledText'; +import { LabelledTextWithClickableIcon } from 'components/common/sharedComponents/LabelledTextWithClickableIcon'; +import MaterialIcons from 'utils/icons/materialIcons'; +import SurveyUnitContext from '../UEContext'; +import { useContext } from 'react'; + +const useStyles = makeStyles(theme => ({ + root: { + display: 'flex', + flexDirection: 'column', + gap: '0.5em', + }, + row: { display: 'flex', flexDirection: 'row' }, + spaceAround: { + marginLeft: '1em', + marginRight: '1em', + }, +})); + +export const Individual = ({ person }) => { + const classes = useStyles(); + const { surveyUnit } = useContext(SurveyUnitContext); + const { id, title, lastName, firstName, birthdate, email, favoriteEmail, phoneNumbers } = person; + const { fiscalPhoneNumbers, directoryPhoneNumbers, interviewerPhoneNumbers } = sortPhoneNumbers( + phoneNumbers + ); + const age = displayAgeInYears(birthdate); + + const toggleFavoriteEmail = () => toggleFavoriteEmailAndPersist(surveyUnit, id); + + const toggleFavoritePhoneNumber = phoneNumber => + toggleFavoritePhoneNumberAndPersist(surveyUnit, id, phoneNumber); + + const favoriteIcon = (favorite, onClickFunction) => ( + onClickFunction()} + /> + ); + + return ( + + + + + + + favoriteIcon(favoriteEmail, toggleFavoriteEmail)} + /> + + favoriteIcon(fiscalPhoneNumbers?.[0]?.favorite ?? false, () => + toggleFavoritePhoneNumber(fiscalPhoneNumbers?.[0]) + ) + } + /> + + favoriteIcon(directoryPhoneNumbers?.[0]?.favorite ?? false, () => + toggleFavoritePhoneNumber(directoryPhoneNumbers?.[0]) + ) + } + /> + {(interviewerPhoneNumbers?.length > 0 && + interviewerPhoneNumbers.map(phone => ( + <> + favoriteIcon(phone.favorite, () => toggleFavoritePhoneNumber(phone))} + /> + + ))) || } + + ); +}; diff --git a/src/components/panel-body/UEpage/details/component.js b/src/components/panel-body/UEpage/details/component.js index 3445a3004..76c4b849d 100644 --- a/src/components/panel-body/UEpage/details/component.js +++ b/src/components/panel-body/UEpage/details/component.js @@ -1,47 +1,30 @@ import React, { useContext } from 'react'; -import formEnum from 'utils/enum/formEnum'; -import { getAddressData, personPlaceholder } from 'utils/functions'; -import D from 'i18n'; + +import Contact from './contact'; import PropTypes from 'prop-types'; -import AtomicInfoTile from '../atomicInfoTile'; import SurveyUnitContext from '../UEContext'; -import Contact from './contact'; -import DetailTile from './detailTile'; +import { personPlaceholder } from 'utils/functions'; const UEItem = ({ selectFormType, setInjectableData }) => { const { surveyUnit } = useContext(SurveyUnitContext); const { persons } = surveyUnit; + const sortedPersons = persons?.sort((a, b) => b.privileged - a.privileged); return ( <> - {persons && - persons - .sort((a, b) => b.privileged - a.privileged) - .map((person, index) => { - return ( - - ); - })} - {(!persons || persons.length === 0) && ( + {persons && ( )} - - selectFormType(formEnum.ADDRESS, true)} + {(!persons || persons.length === 0) && ( + - + )} ); }; diff --git a/src/components/panel-body/UEpage/details/contact.js b/src/components/panel-body/UEpage/details/contact.js index 13e225379..59b4d78f7 100644 --- a/src/components/panel-body/UEpage/details/contact.js +++ b/src/components/panel-body/UEpage/details/contact.js @@ -1,52 +1,51 @@ -import React from 'react'; +import Divider from '@material-ui/core/Divider'; import Grid from '@material-ui/core/Grid'; -import formEnum from 'utils/enum/formEnum'; -import { getMailData, getPhoneData, getUserData } from 'utils/functions'; -import D from 'i18n'; +import { Individual } from './Individual'; import PropTypes from 'prop-types'; -import AtomicInfoTile from '../atomicInfoTile'; -import DetailTile from './detailTile'; -import PhoneTile from './phoneTile'; +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; -const Contact = ({ person, selectFormType, setInjectableData, index }) => { - return ( - - - { - selectFormType(formEnum.USER, true); - setInjectableData(person); - }} - /> - { - selectFormType(formEnum.PHONE, true); - setInjectableData(person); - }} - > +const useStyles = makeStyles(() => ({ + spaceAround: { + marginLeft: '1em', + marginRight: '1em', + }, + centered: { + justifySelf: 'center', + width: 'max-content', + margin: 'auto', + }, + flex: { + display: 'flex', + flexDirection: 'column', + }, +})); - { - selectFormType(formEnum.MAIL, true); - setInjectableData(person); - }} - /> - - +const Contact = ({ persons }) => { + const classes = useStyles(); + + return ( + + {persons.map((person, index) => { + return ( + <> + {index > 0 && ( + + )} + + + ); + })} + ); }; export default Contact; Contact.propTypes = { - selectFormType: PropTypes.func.isRequired, - setInjectableData: PropTypes.func.isRequired, - person: PropTypes.shape({}).isRequired, - index: PropTypes.number.isRequired, + persons: PropTypes.arrayOf(PropTypes.shape({})).isRequired, }; diff --git a/src/components/panel-body/UEpage/details/detailTile.js b/src/components/panel-body/UEpage/details/detailTile.js deleted file mode 100644 index 5d0a10c4e..000000000 --- a/src/components/panel-body/UEpage/details/detailTile.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import Paper from '@material-ui/core/Paper'; -import Typography from '@material-ui/core/Typography'; -import PropTypes from 'prop-types'; - -const useStyles = makeStyles(() => ({ - root: { - padding: 8, - borderRadius: 15, - border: ' LightGray solid 2px', - width: 'max-content', - display: 'flex', - flexDirection: 'column', - margin: '10px', - }, - label: { fontWeight: 'bold' }, -})); - -const DetailTile = ({ label, children }) => { - const classes = useStyles(); - return ( - - {label} - {children} - - ); -}; - -export default DetailTile; -DetailTile.propTypes = { - label: PropTypes.string.isRequired, - children: PropTypes.node.isRequired, -}; diff --git a/src/components/panel-body/UEpage/details/phoneList.js b/src/components/panel-body/UEpage/details/phoneList.js deleted file mode 100644 index 298360d4f..000000000 --- a/src/components/panel-body/UEpage/details/phoneList.js +++ /dev/null @@ -1,88 +0,0 @@ -import React from 'react'; -import TextField from '@material-ui/core/TextField'; -import Typography from '@material-ui/core/Typography'; -import { makeStyles } from '@material-ui/core/styles'; -import { getPhoneSource } from 'utils/functions'; -import MaterialIcons from 'utils/icons/materialIcons'; -import PropTypes from 'prop-types'; - -const useStyles = makeStyles(theme => ({ - root: { - padding: 8, - '&:hover': { cursor: 'pointer' }, - width: 'max-content', - minWidth: '200px', - display: 'flex', - flexDirection: 'column', - margin: '10px', - }, - center: { - alignSelf: 'center', - color: theme.palette.text.secondary, - }, - row: { - display: 'flex', - flexDirection: 'row', - }, - label: { marginLeft: '0.5em' }, -})); - -const PhoneList = ({ - numbers, - type, - toggleFavoritePhone, - editable = false, - updatePhoneNumber = () => {}, - deletePhoneNumber = () => {}, -}) => { - const classes = useStyles(); - const isEditable = editable && type === 'INTERVIEWER'; - return ( -
- {getPhoneSource(type)} - {numbers.map((phoneNumber, index) => { - return ( -
- toggleFavoritePhone(phoneNumber)} - /> - - {isEditable ? ( - <> - updatePhoneNumber(phoneNumber)(e)} - variant="outlined" - /> - deletePhoneNumber(phoneNumber.number)} - /> - - ) : ( - {phoneNumber.number} - )} -
- ); - })} -
- ); -}; - -export default PhoneList; -PhoneList.propTypes = { - toggleFavoritePhone: PropTypes.func.isRequired, - numbers: PropTypes.arrayOf(PropTypes.shape({})).isRequired, - type: PropTypes.string.isRequired, - editable: PropTypes.bool, - updatePhoneNumber: PropTypes.func, - deletePhoneNumber: PropTypes.func, -}; diff --git a/src/components/panel-body/UEpage/details/phoneTile.js b/src/components/panel-body/UEpage/details/phoneTile.js deleted file mode 100644 index 206182a03..000000000 --- a/src/components/panel-body/UEpage/details/phoneTile.js +++ /dev/null @@ -1,88 +0,0 @@ -import React from 'react'; -import Paper from '@material-ui/core/Paper'; -import { makeStyles } from '@material-ui/core/styles'; -import { sortPhoneNumbers } from 'utils/functions'; -import MaterialIcons from 'utils/icons/materialIcons'; -import PropTypes from 'prop-types'; -import PhoneList from './phoneList'; - -const useStyles = makeStyles(() => ({ - root: { - padding: 8, - borderRadius: 15, - border: ' LightGray solid 1px', - minHeight: 130, - width: 'max-content', - minWidth: '200px', - display: 'flex', - flexDirection: 'column', - margin: '10px', - }, - clickable: { - '&:hover': { cursor: 'pointer' }, - }, - firstLine: { - alignSelf: 'flex-end', - }, - row: { - display: 'flex', - flexDirection: 'row', - }, - label: { fontWeight: 'bold', marginRight: '0.5em' }, -})); - -const PhoneTile = ({ - phoneNumbers, - onClickFunction = () => {}, - toggleFavoritePhone = () => {}, - editionMode = false, - updatePhoneNumber = () => {}, - deletePhoneNumber = () => {}, -}) => { - const classes = useStyles(); - const { fiscalPhoneNumbers, directoryPhoneNumbers, interviewerPhoneNumbers } = sortPhoneNumbers( - phoneNumbers - ); - - return ( - onClickFunction()} - variant="outlined" - > -
- -
-
- - - -
-
- ); -}; - -export default PhoneTile; -PhoneTile.propTypes = { - phoneNumbers: PropTypes.arrayOf(PropTypes.shape({})).isRequired, - onClickFunction: PropTypes.func, - editionMode: PropTypes.bool, - toggleFavoritePhone: PropTypes.func, - updatePhoneNumber: PropTypes.func, - deletePhoneNumber: PropTypes.func, -}; diff --git a/src/components/panel-body/UEpage/forms/addressForm.js b/src/components/panel-body/UEpage/forms/addressForm.js index 9d3c7809c..ce75d38f8 100644 --- a/src/components/panel-body/UEpage/forms/addressForm.js +++ b/src/components/panel-body/UEpage/forms/addressForm.js @@ -1,61 +1,83 @@ import React, { useContext, useState } from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import Button from '@material-ui/core/Button'; -import DialogActions from '@material-ui/core/DialogActions'; -import DialogTitle from '@material-ui/core/DialogTitle'; -import TextField from '@material-ui/core/TextField'; import D from 'i18n'; +import DialogActions from '@material-ui/core/DialogActions'; +import { Divider } from '@material-ui/core'; +import { EditableBooleanField } from 'components/common/sharedComponents/EditableBooleanField'; +import { EditableTextField } from 'components/common/sharedComponents/EditableTextField'; +import GenericTile from 'components/common/sharedComponents/GenericTile'; +import IconButton from 'components/common/sharedComponents/IconButton'; +import MaterialIcons from 'utils/icons/materialIcons'; import PropTypes from 'prop-types'; import SurveyUnitContext from '../UEContext'; +import { makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles(() => ({ column: { display: 'flex', flexDirection: 'column', + gap: '1em', + }, + row: { + display: 'flex', + flexDirection: 'row', + gap: '1em', }, })); const Form = ({ closeModal, save, previousValue }) => { const { surveyUnit } = useContext(SurveyUnitContext); - const previousData = previousValue.reduce( - (obj, { label, value }) => ({ ...obj, [label]: value }), - {} - ); + const { + deliveryPoint, + additionalAddress, + streetName, + locality, + postCode, + building, + cityName, + floor, + door, + staircase, + elevator, + cityPriorityDistrict, + } = previousValue; - const [deliveryPoint, setDeliveryPoint] = useState( - previousData[D.addressDeliveryPoint] ? previousData[D.addressDeliveryPoint] : '' - ); - const [additionalAddress, setAdditionalAddress] = useState( - previousData[D.addressAdditionalAddress] ? previousData[D.addressAdditionalAddress] : '' - ); - const [streetName, setStreetName] = useState( - previousData[D.addressStreetName] ? previousData[D.addressStreetName] : '' - ); - const [locality, setLocality] = useState( - previousData[D.addressLocality] ? previousData[D.addressLocality] : '' - ); - const [postcode, setPostcode] = useState( - previousData[D.addressPostcode] ? previousData[D.addressPostcode] : '' - ); - const [city, setCity] = useState(previousData[D.addressCity] ? previousData[D.addressCity] : ''); + const [deliveryPointForm, setDeliveryPoint] = useState(deliveryPoint); + const [additionalAddressForm, setAdditionalAddress] = useState(additionalAddress); + const [streetNameForm, setStreetName] = useState(streetName); + const [localityForm, setLocality] = useState(locality); + const [postcodeForm, setPostcode] = useState(postCode); + const [cityForm, setCity] = useState(cityName); + const [buildingForm, setBuilding] = useState(building); + const [floorForm, setFloor] = useState(floor); + const [doorForm, setDoor] = useState(door); + const [staircaseForm, setStaircase] = useState(staircase); + const [elevatorForm, setElevator] = useState(elevator); + const [cityPriorityDistrictForm, setCityPriorityDistrict] = useState(cityPriorityDistrict); const buildAddress = surveyUnit => { const { address } = surveyUnit; return { l1: address.l1, - l2: deliveryPoint, - l3: additionalAddress, - l4: streetName, - l5: locality, - l6: `${postcode} ${city}`, + l2: deliveryPointForm, + l3: additionalAddressForm, + l4: streetNameForm, + l5: localityForm, + l6: `${postcodeForm} ${cityForm}`, + building: buildingForm, + floor: floorForm, + door: doorForm, + staircase: staircaseForm, + elevator: elevatorForm, + cityPriorityDistrict: cityPriorityDistrictForm, }; }; const onChange = event => { const key = event.target.name; const value = event.target.value.trim(); + const checked = event.target.checked; switch (key) { case 'deliveryPoint': setDeliveryPoint(value); @@ -75,6 +97,25 @@ const Form = ({ closeModal, save, previousValue }) => { case 'city': setCity(value); break; + case 'building': + setBuilding(value); + break; + case 'floor': + setFloor(value); + break; + case 'door': + setDoor(value); + break; + case 'staircase': + setStaircase(value); + break; + case 'elevator': + setElevator(checked); + break; + case 'cityPriorityDistrict': + setCityPriorityDistrict(checked); + break; + default: break; } @@ -87,88 +128,92 @@ const Form = ({ closeModal, save, previousValue }) => { const classes = useStyles(); return ( -
- {D.surveyUnitAddressChange} - - - - - - + }> +
+
+ + + + + + +
+ +
+ + + + + + +
+
-