react-cm-ui
Version:
React UI for Healthy Church
934 lines (856 loc) • 27.6 kB
JSX
import {
filter,
find,
isEmpty,
isNil,
map,
} from 'lodash';
import ClassNames from 'classnames';
import moment from 'moment-timezone';
import MomentPropTypes from 'react-moment-proptypes';
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
import {
BEM_DETAILS_NAME,
GENDER_PROP_TYPE,
RECORD_TYPE_PROP_TYPE,
} from './personPanelConstants';
import { UI_CLASS_NAME } from '../../global/constants';
import Address from '../address/address';
import Collapse from '../../utils/collapse/collapse';
import DataGroups from '../dataGroups';
import EmailLink from '../emailLink';
import makeStyles from '../../styles/makeStyles';
import PersonCoreMilestones from '../personCoreMilestones';
import PersonPanelDetailsActionButton from './personPanelDetailsActionButton';
import TelephoneLink from '../telephoneLink';
import Typography from '../typography/typography';
const propTypes = {
/**
* The content of the PersonPanelDetails.
*/
children: PropTypes.node,
/**
* Override or extend the styles applied to PersonPanelDetails.
*/
classes: PropTypes.shape({
root: PropTypes.string,
}),
/**
* Assign additional class names to PersonPanelDetails.
*/
className: PropTypes.string,
/**
* The data that the PersonPanelDetails uses to build the UI.
*/
data: PropTypes.shape({
acceptedChristDate: PropTypes.string,
activeInMissionsDate: PropTypes.string,
addresses: PropTypes.arrayOf(PropTypes.shape({
address1: PropTypes.string,
address2: PropTypes.string,
city: PropTypes.string,
country: PropTypes.string,
countryAlpha2: PropTypes.string,
isPrimary: PropTypes.bool,
postalCode: PropTypes.string,
region: PropTypes.string,
regionCode: PropTypes.string,
})),
allergies: PropTypes.string,
attendedClass101Date: PropTypes.string,
attendedClass201Date: PropTypes.string,
attendedClass301Date: PropTypes.string,
attendedClass401Date: PropTypes.string,
baptismDate: PropTypes.string,
birthdate: PropTypes.number,
campus: PropTypes.string,
churchEntities: PropTypes.arrayOf(PropTypes.shape({})),
commonlyAttendedService: PropTypes.arrayOf(PropTypes.shape({})),
congregationDate: PropTypes.oneOfType([
MomentPropTypes.momentString,
PropTypes.oneOf([null]),
]),
deceasedDate: PropTypes.number,
emails: PropTypes.arrayOf(PropTypes.shape({
isPrimary: PropTypes.bool,
value: PropTypes.string,
})),
emergencyContactAddresses: PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.string,
isPrimary: PropTypes.bool,
})),
emergencyContactEmails: PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.string,
isPrimary: PropTypes.bool,
})),
emergencyContactName: PropTypes.string,
emergencyContactPhones: PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.string,
isPrimary: PropTypes.bool,
})),
emergencyContactPreferMethod: PropTypes.oneOf([
'email',
'letter',
'none',
'phone',
'text',
]),
emergencyContactRelation: PropTypes.string,
firstContactDate: PropTypes.oneOfType([
MomentPropTypes.momentString,
PropTypes.oneOf([null]),
]),
firstMinistryJoinDate: PropTypes.string,
firstSmallGroupJoinDate: PropTypes.string,
gender: GENDER_PROP_TYPE,
gradeLevel: PropTypes.string,
hasAcceptedChrist: PropTypes.bool,
hasSignedMaturityCovenant: PropTypes.bool,
hasSignedMembershipAgreement: PropTypes.bool,
hasSignedMinistryCovenant: PropTypes.bool,
hasSignedMissionCovenant: PropTypes.bool,
hasTakenClass101: PropTypes.bool,
hasTakenClass201: PropTypes.bool,
hasTakenClass301: PropTypes.bool,
hasTakenClass401: PropTypes.bool,
iconColor: PropTypes.string,
isActiveInMissions: PropTypes.bool,
isBaptised: PropTypes.bool,
isDoNotContact: PropTypes.bool,
isInMinistry: PropTypes.bool,
isInSmallGroup: PropTypes.bool,
phones: PropTypes.arrayOf(PropTypes.shape({
type: PropTypes.oneOf([
1,
2,
3,
'cell',
'home',
'work',
]),
isPrimary: PropTypes.bool,
value: PropTypes.string,
})),
preferredService: PropTypes.string,
recordType: RECORD_TYPE_PROP_TYPE,
signedMembershipAgreementDate: PropTypes.string,
signedMaturityCovenantDate: PropTypes.string,
signedMinistryCovenantDate: PropTypes.string,
signedMissionCovenantDate: PropTypes.string,
}),
/**
* The `id` of the PersonPanelDetails.
*/
id: PropTypes.string,
/**
/**
* If `true`, expand PersonPanelDetails, otherwise collapse it.
*/
isExpanded: PropTypes.bool,
/**
* Flag used to handle mobile popover behaviors onTap
*/
isMobile: PropTypes.bool,
/**
* For passing in custom Data Groups.
*/
otherDataGroups: PropTypes.arrayOf(PropTypes.shape({})),
/**
* Button `props` to setup the Select button.
*/
selectButtonProps: PropTypes.shape({
className: PropTypes.string,
disable: PropTypes.bool,
id: PropTypes.string,
label: PropTypes.string,
onClick: PropTypes.func,
onKeyDown: PropTypes.func,
onPromptCancelClick: PropTypes.func,
onPromptConfirmClick: PropTypes.func,
prompt: PropTypes.bool,
promptId: PropTypes.string,
}),
/**
* If `false`, select button isn't displayed for PersonPanelDetails.
*/
showSelectButton: PropTypes.bool,
/**
* Button `props` to setup the View Record button.
*/
viewRecordButtonProps: PropTypes.shape({
className: PropTypes.string,
id: PropTypes.string,
onClick: PropTypes.func,
onKeyDown: PropTypes.func,
onPromptCancelClick: PropTypes.func,
onPromptConfirmClick: PropTypes.func,
prompt: PropTypes.bool,
promptId: PropTypes.string,
}),
};
const defaultProps = {
children: null,
className: null,
classes: null,
data: {},
id: null,
isExpanded: false,
isMobile: false,
otherDataGroups: null,
selectButtonProps: {
label: 'Select',
disable: false,
},
showSelectButton: true,
viewRecordButtonProps: {},
};
function setAddressDataGroup({
addresses,
isChild,
isDoNotContact,
}) {
if (!isEmpty(addresses) && !isDoNotContact && !isChild) {
const addressRows = map(addresses, (address) => ({
accessor: () => (
<Address
address1={address.address1}
address2={address.address2}
city={address.city}
country={address.country}
countryAlpha2={address.countryAlpha2}
postalCode={address.postalCode}
region={address.region}
regionCode={address.regionCode}
/>
),
fieldName: address.isPrimary ? 'Primary' : 'Address',
}));
return [
{
header: 'Address',
rows: [
...addressRows,
],
},
];
}
return [];
}
const setEmailDataGroup = ({
emails,
isChild,
isDoNotContact,
}) => {
if (!isEmpty(emails) && !isDoNotContact && !isChild) {
const emailRows = map(emails, (email) => ({
accessor: () => (
<EmailLink
email={email.value}
/>
),
fieldName: email.isPrimary ? 'Primary' : 'Email',
}));
return [
{
header: 'Email',
rows: [
...emailRows,
],
},
];
}
return [];
};
function getPhoneFieldName(type, isPrimary) {
let fieldName;
switch (type) {
case 'cell':
case 3:
fieldName = 'Cell';
break;
case 'home':
case 1:
fieldName = 'Home';
break;
case 'work':
case 2:
fieldName = 'Work';
break;
default:
}
if (isPrimary) {
fieldName = 'Primary';
}
return fieldName;
}
const setPhoneDataGroup = ({
isChild,
isDoNotContact,
phones,
}) => {
if (!isEmpty(phones) && !isDoNotContact && !isChild) {
const phoneRows = map(phones, (phone) => ({
accessor: () => (
<TelephoneLink
number={phone.value}
/>
),
fieldName: getPhoneFieldName(phone.type, phone.isPrimary),
}));
return [
{
header: 'Phone',
rows: [
...phoneRows,
],
},
];
}
return [];
};
const setEmergencyContactDataGroup = ({
emergencyContactAddresses,
emergencyContactEmails,
emergencyContactName,
emergencyContactPhones,
emergencyContactRelation,
emergencyContactPreferMethod,
}) => {
let nameRow = [];
if (emergencyContactName) {
nameRow = [{
accessor: 'emergencyContactName',
fieldName: 'Name',
}];
}
let relationRow = [];
if (emergencyContactRelation) {
relationRow = [{
accessor: 'emergencyContactRelation',
fieldName: 'Relation',
}];
}
let preferMethodRow = [];
let addressRows = [];
if (emergencyContactAddresses) {
if (emergencyContactPreferMethod === 'letter') {
const primaryPreferredAddress = filter(emergencyContactAddresses, 'isPrimary')[0];
preferMethodRow = [
...preferMethodRow,
{
accessor: () => (
<EmailLink
email={primaryPreferredAddress.value}
/>
),
fieldName: `Prefers ${emergencyContactPreferMethod}`,
},
];
}
addressRows = map(emergencyContactAddresses, (address) => ({
accessor: () => (
<Address
address1={address.address1}
address2={address.address2}
city={address.city}
country={address.country}
countryAlpha2={address.countryAlpha2}
postalCode={address.postalCode}
region={address.region}
regionCode={address.regionCode}
/>
),
fieldName: 'Address',
}));
}
let emailRows = [];
if (emergencyContactEmails) {
if (emergencyContactPreferMethod === 'email') {
const primaryPreferredEmail = filter(emergencyContactEmails, 'isPrimary')[0];
preferMethodRow = [
...preferMethodRow,
{
accessor: () => (
<EmailLink
email={primaryPreferredEmail.value}
/>
),
fieldName: `Prefers ${emergencyContactPreferMethod}`,
},
];
}
emailRows = map(emergencyContactEmails, (email) => ({
accessor: () => (
<EmailLink
email={email.value}
/>
),
fieldName: 'Email',
}));
}
let phoneRows = [];
if (emergencyContactPhones && emergencyContactPreferMethod) {
if (
emergencyContactPreferMethod === 'text' ||
emergencyContactPreferMethod === 'phone'
) {
const primaryPreferredPhone = filter(emergencyContactPhones, 'isPrimary')[0];
preferMethodRow = [
...preferMethodRow,
{
accessor: () => (
<TelephoneLink
number={primaryPreferredPhone.value}
/>
),
fieldName: `Prefers ${emergencyContactPreferMethod}`,
},
];
}
phoneRows = map(emergencyContactPhones, (phone) => ({
accessor: () => (
<TelephoneLink
number={phone.value}
/>
),
fieldName: 'Phone',
}));
}
if (
!isEmpty(nameRow) &&
!isEmpty(relationRow) &&
(
!isEmpty(addressRows) ||
!isEmpty(emailRows) ||
!isEmpty(phoneRows)
)
) {
return [
{
header: 'Emergency Contact',
rows: [
...nameRow,
...relationRow,
...preferMethodRow,
...phoneRows,
...emailRows,
...addressRows,
],
},
];
}
return [];
};
const setPersonalDataGroup = ({
allergies,
birthdate,
campus,
churchEntities,
commonlyAttendedService,
deceasedDate,
gradeLevel,
isChild,
isStudent,
preferredService,
}) => {
let birthdayRow = [];
if (birthdate) {
birthdayRow = [{
accessor: () => (
birthdate ? moment.unix(birthdate).utc().format('MM/DD/YYYY') : 'N/A'
),
fieldName: 'Birthday',
}];
}
let deceasedDateRow = [];
if (deceasedDate) {
deceasedDateRow = [{
accessor: 'deceasedDate',
fieldName: 'Deceased',
}];
}
let commonlyAttendedServiceContractedRow = [];
let personAllergiesContractedRow = [];
let personGradeLevelRow = [];
if (isStudent || isChild) {
if (allergies) {
personAllergiesContractedRow = [{
accessor: 'allergies',
fieldName: 'Allergies',
}];
}
if (!isEmpty(commonlyAttendedService)) {
const daysOfTheWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
commonlyAttendedServiceContractedRow = {
accessor: () => (
<Typography
variant="body1"
>
{map(commonlyAttendedService, (item) => {
if (
item &&
item.id &&
item.schedule &&
item.schedule.sequences &&
item.schedule.sequences.length > 0
) {
const churchEntity = find(churchEntities, (c) => (
c.id === item.churchEntityId
));
const name = churchEntity && churchEntity.name;
return (
<span
key={`commonly_attended_service-${item.id}`}
>
{daysOfTheWeek[item.schedule.sequences[0].dayOfWeek]}
{' '}
{item.schedule.sequences[0].startTime ?
moment(item.schedule.sequences[0].startTime, ['HH.mm:ss']).format('h:mm a') :
null}
,
{' '}
{name}
</span>
);
}
return null;
})}
</Typography>
),
fieldName: 'Commonly Attended Services',
};
}
if (gradeLevel) {
personGradeLevelRow = [{
accessor: 'gradeLevel',
fieldName: 'Grade',
}];
}
}
let preferredServiceContractedRow = [];
if (!isChild && preferredService) {
preferredServiceContractedRow = [{
accessor: 'preferredService',
fieldName: 'Preferred Service',
}];
}
let homeCampusRow = [];
if (campus) {
homeCampusRow = [{
accessor: 'campus',
fieldName: 'Home Campus',
}];
}
if (
!isEmpty(birthdayRow) ||
!isEmpty(personGradeLevelRow) ||
!isEmpty(homeCampusRow) ||
!isEmpty(preferredServiceContractedRow) ||
!isEmpty(commonlyAttendedServiceContractedRow) ||
!isEmpty(deceasedDateRow) ||
!isEmpty(personAllergiesContractedRow)
) {
return [
{
header: 'Personal',
rows: [
...birthdayRow,
...personGradeLevelRow,
...homeCampusRow,
...preferredServiceContractedRow,
...commonlyAttendedServiceContractedRow,
...deceasedDateRow,
...personAllergiesContractedRow,
],
},
];
}
return [];
};
const useStyles = makeStyles((theme) => {
const {
palette,
shape: {
borderRadius,
},
} = theme;
return {
actions: {
marginTop: '33px',
},
dataGroups: {
margin: '22px -22px -22px !important',
},
innerContainer: {
padding: 22,
},
isExpanded: {},
personCoreMilestones: {
borderBottom: `1px solid ${palette.border.secondary}`,
margin: '-22px -21px 11px',
padding: '18px 21px',
},
root: {
backgroundColor: palette.background.primary,
borderRadius: `0 0 ${borderRadius}px ${borderRadius}px`,
boxShadow: 'inset -1px -1px 0px 0px transparent, inset 1px 0px 0px 0px transparent',
color: palette.text.primary,
flexGrow: 1,
padding: 0,
transition: 'box-shadow 200ms ease-in-out',
width: '100%',
'&$isExpanded': {
boxShadow: `inset -1px -1px 0px 0px ${palette.border.secondary}, inset 1px 0px 0px 0px ${palette.border.secondary}`,
},
},
};
});
function PersonPanelDetails(props) {
const {
children,
className,
data,
id,
isExpanded,
isMobile,
otherDataGroups,
selectButtonProps,
showSelectButton,
viewRecordButtonProps,
} = props;
const classes = useStyles(props);
const [dataGroupsColumns, setDataGroupsColumns] = useState([]);
const {
acceptedChristDate,
activeInMissionsDate,
addresses,
allergies,
attendedClass101Date,
attendedClass201Date,
attendedClass301Date,
attendedClass401Date,
baptismDate,
birthdate,
campus,
churchEntities,
commonlyAttendedService,
congregationDate,
deceasedDate,
emails,
emergencyContactAddresses,
emergencyContactEmails,
emergencyContactName,
emergencyContactPhones,
emergencyContactPreferMethod,
emergencyContactRelation,
firstContactDate,
firstMinistryJoinDate,
firstSmallGroupJoinDate,
gender,
gradeLevel,
hasAcceptedChrist,
hasSignedMaturityCovenant,
hasSignedMembershipAgreement,
hasSignedMinistryCovenant,
hasSignedMissionCovenant,
hasTakenClass101,
hasTakenClass201,
hasTakenClass301,
hasTakenClass401,
iconColor,
isActiveInMissions,
isBaptised,
isDoNotContact,
isInMinistry,
isInSmallGroup,
phones,
preferredService,
recordType,
signedMembershipAgreementDate,
signedMaturityCovenantDate,
signedMinistryCovenantDate,
signedMissionCovenantDate,
} = data;
const isChild = recordType === 'child';
const isStudent = recordType === 'student';
useEffect(() => {
const personalDataGroup = setPersonalDataGroup({
allergies,
birthdate,
campus,
churchEntities,
commonlyAttendedService,
deceasedDate,
gradeLevel,
isChild,
isStudent,
preferredService,
});
const addressDataGroup = setAddressDataGroup({
addresses,
isChild,
isDoNotContact,
});
const emailDataGroup = setEmailDataGroup({
emails,
isChild,
isDoNotContact,
});
const phoneDataGroup = setPhoneDataGroup({
isChild,
isDoNotContact,
phones,
});
const emergencyContactDataGroup = setEmergencyContactDataGroup({
emergencyContactAddresses,
emergencyContactEmails,
emergencyContactName,
emergencyContactPhones,
emergencyContactPreferMethod,
emergencyContactRelation,
});
let newDataGroupsColumns = [
...personalDataGroup,
...phoneDataGroup,
...emailDataGroup,
...addressDataGroup,
...emergencyContactDataGroup,
];
if (!isNil(otherDataGroups)) {
newDataGroupsColumns = [
...personalDataGroup,
...phoneDataGroup,
...emailDataGroup,
...addressDataGroup,
...emergencyContactDataGroup,
...otherDataGroups,
];
} else {
newDataGroupsColumns = [
...personalDataGroup,
...phoneDataGroup,
...emailDataGroup,
...addressDataGroup,
...emergencyContactDataGroup,
];
}
setDataGroupsColumns(newDataGroupsColumns);
}, [
addresses,
allergies,
birthdate,
campus,
churchEntities,
commonlyAttendedService,
deceasedDate,
emails,
emergencyContactAddresses,
emergencyContactEmails,
emergencyContactName,
emergencyContactPhones,
emergencyContactPreferMethod,
emergencyContactRelation,
gradeLevel,
isChild,
isDoNotContact,
isStudent,
otherDataGroups,
phones,
preferredService,
]);
const rootClasses = ClassNames(
classes.root,
UI_CLASS_NAME,
[`${BEM_DETAILS_NAME}`],
className,
{
[classes.isExpanded]: isExpanded,
},
);
const shouldDataGroupsRender = !isEmpty(data) && !isEmpty(dataGroupsColumns);
const shouldRenderSelectButton = showSelectButton && !isEmpty(selectButtonProps);
const shouldRenderViewButton = !isEmpty(viewRecordButtonProps);
return (
<div
className={rootClasses}
id={id}
>
<Collapse in={isExpanded}>
<div className={classes.innerContainer}>
<PersonCoreMilestones
className={classes.personCoreMilestones}
data={{
acceptedChristDate,
firstSmallGroupJoinDate,
firstMinistryJoinDate,
activeInMissionsDate,
attendedClass101Date,
attendedClass201Date,
attendedClass301Date,
attendedClass401Date,
baptismDate,
congregationDate,
firstContactDate,
gender,
hasAcceptedChrist,
hasSignedMaturityCovenant,
hasSignedMembershipAgreement,
hasSignedMinistryCovenant,
hasSignedMissionCovenant,
hasTakenClass101,
hasTakenClass201,
hasTakenClass301,
hasTakenClass401,
isActiveInMissions,
isBaptised,
isInMinistry,
isInSmallGroup,
recordType,
signedMembershipAgreementDate,
signedMaturityCovenantDate,
signedMinistryCovenantDate,
signedMissionCovenantDate,
}}
iconColor={iconColor}
iconSize={16}
isMobile={isMobile}
/>
{shouldDataGroupsRender && (
<DataGroups
className={classes.dataGroups}
columns={dataGroupsColumns}
data={data}
moduleType="page"
/>
)}
{children}
{(shouldRenderSelectButton || shouldRenderViewButton) && (
<div
className={classes.actions}
>
{shouldRenderSelectButton && (
<PersonPanelDetailsActionButton
label="Select"
// eslint-disable-next-line react/jsx-props-no-spreading
{...selectButtonProps}
/>
)}
{shouldRenderViewButton && (
<PersonPanelDetailsActionButton
// eslint-disable-next-line react/jsx-props-no-spreading
{...viewRecordButtonProps}
label="View Record"
outlined
/>
)}
</div>
)}
</div>
</Collapse>
</div>
);
}
PersonPanelDetails.propTypes = propTypes;
PersonPanelDetails.defaultProps = defaultProps;
export default PersonPanelDetails;