diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx index 713f77eea..9b6af14a0 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx @@ -68,7 +68,7 @@ interface EditContactAddressModalProps { accountListId: string; address: ContactMailingFragment['addresses']['nodes'][0]; contactId: string; - handleClose: () => void; + handleClose: (deletedAddress: boolean) => void; } export const EditContactAddressModal: React.FC< @@ -115,7 +115,7 @@ export const EditContactAddressModal: React.FC< enqueueSnackbar(t('Address updated successfully'), { variant: 'success', }); - handleClose(); + handleClose(false); }; const deleteContactAddress = async (): Promise => { @@ -158,10 +158,11 @@ export const EditContactAddressModal: React.FC< }, }); } - handleClose(); + handleClose(true); }; - const editingDisabled = address.source === 'Siebel'; + const editingDisabled = + address.source === 'Siebel' || address.source === 'DataServer'; const { data: emailData } = useDonationServicesEmailQuery({ variables: { accountListId, @@ -171,7 +172,11 @@ export const EditContactAddressModal: React.FC< }); return ( - + handleClose(false)} + > {editingDisabled && ( - - - {t('This address is provided by Donation Services')} - -

- {t( - 'The address that syncs with Donation Services cannot be edited here. Please email Donation Services with the updated address, or you can create a new address and select it as your primary mailing address.', + {address.source === 'Siebel' && ( + + + {t('This address is provided by Donation Services')} + +

+ {t( + 'The address that syncs with Donation Services cannot be edited here. Please email Donation Services with the updated address, or you can create a new address and select it as your primary mailing address.', + )} +

+ {emailData && ( +

+ + {t('Email Donation Services here')} + +

)} -

- {emailData && ( +
+ )} + {address.source === 'DataServer' && ( + + + {t('This address is provided by your organization.')} +

- - {t('Email Donation Services here')} - + {t( + 'The address that syncs with your organization’s donations cannot be edited here. Please email your donation department with the updated address, or you can create a new address and select it as your primary mailing address.', + )}

- )} -
+ + )}
)} @@ -391,8 +410,13 @@ export const EditContactAddressModal: React.FC< - {address && } - + {address && !editingDisabled && ( + + )} + handleClose(false)} + disabled={isSubmitting} + /> {(updating || deleting || settingPrimaryAddress) && ( diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/StyledComponents.ts b/src/components/Contacts/ContactDetails/ContactDetailsTab/StyledComponents.ts index 1f1437701..f6ba84de5 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/StyledComponents.ts +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/StyledComponents.ts @@ -1,5 +1,6 @@ import Add from '@mui/icons-material/Add'; import Create from '@mui/icons-material/Create'; +import Lock from '@mui/icons-material/Lock'; import { Button, Typography } from '@mui/material'; import { styled } from '@mui/material/styles'; @@ -23,3 +24,10 @@ export const AddText = styled(Typography)(({ theme }) => ({ textTransform: 'uppercase', fontWeight: 'bold', })); + +export const LockIcon = styled(Lock)(({ theme }) => ({ + width: '18px', + height: '18px', + margin: theme.spacing(0), + color: theme.palette.cruGrayMedium.main, +})); diff --git a/src/components/Tool/FixMailingAddresses/AddressModal.tsx b/src/components/Tool/FixMailingAddresses/AddressModal.tsx deleted file mode 100644 index c62e7963b..000000000 --- a/src/components/Tool/FixMailingAddresses/AddressModal.tsx +++ /dev/null @@ -1,369 +0,0 @@ -import React from 'react'; -import { mdiCloseThick, mdiInformation, mdiMap } from '@mdi/js'; -import Icon from '@mdi/react'; -import { - Box, - Card, - CardActions, - CardContent, - CardHeader, - Checkbox, - Grid, - IconButton, - Modal, - NativeSelect, - TextField, - Typography, -} from '@mui/material'; -import clsx from 'clsx'; -import { useTranslation } from 'react-i18next'; -import { makeStyles } from 'tss-react/mui'; -import { - CancelButton, - SubmitButton, -} from 'src/components/common/Modal/ActionButtons/ActionButtons'; -import theme from '../../../theme'; -import { StyledInput } from '../StyledInput'; -import { ContactAddressFragment } from './GetInvalidAddresses.generated'; - -const useStyles = makeStyles()(() => ({ - modal: { - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - overflow: 'auto', - }, - paddingX: { - paddingLeft: theme.spacing(1), - paddingRight: theme.spacing(1), - }, - paddingY: { - paddingBottom: theme.spacing(2), - }, - blue: { - color: 'white', - backgroundColor: theme.palette.mpdxBlue.main, - }, - iconButton: { - position: 'absolute', - top: -theme.spacing(2), - right: -theme.spacing(2), - }, - headerBox: { - padding: 0, - position: 'relative', - }, - mapBox: { - marginTop: theme.spacing(1), - height: 200, - width: '100%', - backgroundColor: theme.palette.cruGrayDark.main, - }, - widthFull: { - width: '100%', - }, - cardContent: { - paddingLeft: theme.spacing(1), - paddingRight: theme.spacing(1), - paddingTop: 0, - paddingBottom: 0, - border: 'none', - outline: 'none', - }, - infoBox: { - border: `1px solid ${theme.palette.mpdxBlue.main}`, - backgroundColor: theme.palette.cruGrayLight.main, - color: theme.palette.mpdxBlue.main, - marginLeft: theme.spacing(1), - marginRight: theme.spacing(1), - marginBottom: theme.spacing(2), - padding: theme.spacing(2), - }, - infoMain: { - display: 'flex', - fontWeight: 600, - marginBottom: theme.spacing(1), - }, -})); - -interface Props { - modalState: { - open: boolean; - address: ContactAddressFragment; - }; - handleClose: () => void; - handleChange: ( - event: - | React.ChangeEvent - | React.ChangeEvent, - props: string, - ) => void; -} - -const AddressModal: React.FC = ({ - modalState, - handleClose, - handleChange, -}) => { - const { classes } = useStyles(); - const { t } = useTranslation(); - - const disableAll = !modalState.address.source.includes('MPDX'); - - return ( - - - - - - - {modalState.address.id === 'new' - ? 'Add Address' - : 'Edit Address'} - - - - - - - - - } - /> - - {disableAll && ( - - - - This address is provided by your organization. - - - The address that syncs with your organization’s donations - cannot be edited here. Please email your donation department - with the updated address, or you can create a new address - and select it as your primary mailing address. - - - )} - - - ) => - handleChange(event, 'street') - } - disabled={disableAll} - /> - - - } - disabled={disableAll} - onChange={( - event: React.ChangeEvent< - HTMLSelectElement & HTMLInputElement - >, - ) => handleChange(event, 'location')} - className={classes.widthFull} - > - - - - - - - - - - - - ) => - handleChange(event, 'city') - } - /> - - - ) => - handleChange(event, 'state') - } - /> - - - ) => - handleChange(event, 'postalCode') - } - /> - - - ) => - handleChange(event, 'country') - } - /> - - - ) => - handleChange(event, 'region') - } - /> - - - ) => - handleChange(event, 'metroArea') - } - /> - - - - ) => - handleChange(event, 'historic') - } - /> - - Address no longer valid - - - - - - - - - {/*TODO: make "newAddress" field in address false so it says "edit" instead of "add" */} - Save - - - - - - - ); -}; - -export default AddressModal; diff --git a/src/components/Tool/FixMailingAddresses/Contact.tsx b/src/components/Tool/FixMailingAddresses/Contact.tsx index 9cff5d6a0..79ead8ff2 100644 --- a/src/components/Tool/FixMailingAddresses/Contact.tsx +++ b/src/components/Tool/FixMailingAddresses/Contact.tsx @@ -1,70 +1,59 @@ import React, { Fragment } from 'react'; -import { mdiCheckboxMarkedCircle, mdiLock, mdiPencil, mdiPlus } from '@mdi/js'; +import styled from '@emotion/styled'; +import { mdiCheckboxMarkedCircle, mdiPlus } from '@mdi/js'; import { Icon } from '@mdi/react'; import StarIcon from '@mui/icons-material/Star'; import StarOutlineIcon from '@mui/icons-material/StarOutline'; -import { Avatar, Box, Button, Grid, Hidden, Typography } from '@mui/material'; +import { + Avatar, + Box, + Button, + Card, + CardContent, + CardHeader, + Grid, + Hidden, + IconButton, + Typography, +} from '@mui/material'; import clsx from 'clsx'; import { DateTime } from 'luxon'; import { useTranslation } from 'react-i18next'; import { makeStyles } from 'tss-react/mui'; +import { + EditIcon, + LockIcon, +} from 'src/components/Contacts/ContactDetails/ContactDetailsTab/StyledComponents'; import { useLocale } from 'src/hooks/useLocale'; import { dateFormatShort } from 'src/lib/intlFormat'; +import { contactPartnershipStatus } from 'src/utils/contacts/contactPartnershipStatus'; import theme from '../../../theme'; import { emptyAddress } from './FixMailingAddresses'; import { ContactAddressFragment } from './GetInvalidAddresses.generated'; -const useStyles = makeStyles()(() => ({ - left: { - [theme.breakpoints.up('lg')]: { - border: `1px solid ${theme.palette.cruGrayMedium.main}`, - }, - }, - container: { - display: 'flex', - alignItems: 'center', - marginBottom: theme.spacing(2), - [theme.breakpoints.down('md')]: { - border: `1px solid ${theme.palette.cruGrayMedium.main}`, - }, - }, - boxBottom: { - backgroundColor: theme.palette.cruGrayLight.main, - width: '100%', - [theme.breakpoints.down('sm')]: { - paddingTop: theme.spacing(2), - }, - }, - buttonTop: { - padding: theme.spacing(1), - [theme.breakpoints.down('md')]: { - display: 'flex', - justifyContent: 'center', - padding: theme.spacing(2), - }, - '& .MuiButton-root': { - backgroundColor: theme.palette.mpdxBlue.main, - width: '100%', - color: 'white', - [theme.breakpoints.down('md')]: { - width: '50%', - }, - [theme.breakpoints.down('xs')]: { - width: '100%', - }, - }, +const ContactHeader = styled(CardHeader)(() => ({ + '.MuiCardHeader-action': { + alignSelf: 'center', }, - buttonIcon: { +})); + +const ContactIconContainer = styled(IconButton)(() => ({ + margin: theme.spacing(0, 1), + width: theme.spacing(4), + height: theme.spacing(4), +})); + +const ContactAvatar = styled(Avatar)(() => ({ + width: theme.spacing(4), + height: theme.spacing(4), +})); + +const useStyles = makeStyles()(() => ({ + confirmButon: { marginRight: theme.spacing(1), }, - rowChangeResponsive: { - flexDirection: 'column', - [theme.breakpoints.down('sm')]: { - marginTop: -20, - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - }, + contactCard: { + marginBottom: theme.spacing(2), }, responsiveBorder: { [theme.breakpoints.down('sm')]: { @@ -80,38 +69,47 @@ const useStyles = makeStyles()(() => ({ paddingTop: theme.spacing(2), paddingBottom: theme.spacing(2), }, + paddingL2: { + paddingLeft: theme.spacing(2), + '@media(max-width: 900px)': { + paddingLeft: 0, + }, + }, paddingB2: { paddingBottom: theme.spacing(2), }, address: { borderBottom: '1px solid gray', width: '100%', - cursor: 'text', + cursor: 'pointer', }, hoverHighlight: { + cursor: 'pointer', '&:hover': { color: theme.palette.mpdxBlue.main, }, }, - avatar: { - width: theme.spacing(7), - height: theme.spacing(7), + alignCenter: { + textAlign: 'center', }, })); interface Props { - id?: string; + id: string; name: string; status: string; addresses: ContactAddressFragment[]; - openFunction: (address: ContactAddressFragment) => void; + openEditAddressModal: (address: ContactAddressFragment, id: string) => void; + openNewAddressModal: (address: ContactAddressFragment, id: string) => void; } const Contact: React.FC = ({ + id, name, status, addresses, - openFunction, + openEditAddressModal, + openNewAddressModal, }) => { const { t } = useTranslation(); const locale = useLocale(); @@ -121,179 +119,138 @@ const Contact: React.FC = ({ //TODO: Make contact name a link to contact page return ( - - - - - - + + } + action={ + + } + title={{name}} + subheader={{contactPartnershipStatus[status]}} + /> + + + + + + + + + {t('Source')} + + + + + {t('Primary')} + + + + + - - - {name} - {status} - + + {t('Address')} + - - - - - - - - {t('Source')} - - - {t('Primary')} + + {addresses.map((address) => ( + + + + + + + {t('Source')}: - + + + {address.source}{' '} + + + {dateFormatShort( + DateTime.fromISO(address.createdAt), + locale, + )} + - - - - {t('Address')} - - + + + {address.primaryMailingAddress ? ( + + ) : ( + + )} + - - {addresses.map((address) => ( - - - - - - - {t('Source')}: - - - - {address.source}{' '} - - - {dateFormatShort( - DateTime.fromISO(address.createdAt), - locale, - )} - - - - {address.primaryMailingAddress ? ( - - ) : ( - - )} - - - - - - openFunction(address)} - className={classes.address} - > - - {`${address.street}, ${address.city} ${ - address.state ? address.state : '' - }. ${address.postalCode}`} - - - - - - - - ))} - - - - - - {t('Source')}: - - - MPDX - - - - - - openFunction(newAddress)} - className={classes.address} - /> - + + + + openEditAddressModal(address, id)} + > + + + {`${address.street}, ${address.city} ${ + address.state ? address.state : '' + }. ${address.postalCode}`} + - + + + {address.source === 'MPDX' ? : } + + - + + ))} + + + + + + {t('Source')}: + + + MPDX + + - - - - - - - - + + + + - - + + ); }; diff --git a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx index 43102d14a..58e191129 100644 --- a/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx +++ b/src/components/Tool/FixMailingAddresses/FixMailingAddresses.tsx @@ -7,15 +7,16 @@ import { CircularProgress, Divider, Grid, - NativeSelect, + MenuItem, + Select, + SelectChangeEvent, Typography, } from '@mui/material'; import { Trans, useTranslation } from 'react-i18next'; import { makeStyles } from 'tss-react/mui'; +import { EditContactAddressModal } from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal'; import theme from '../../../theme'; import NoData from '../NoData'; -import { StyledInput } from '../StyledInput'; -import AddressModal from './AddressModal'; import Contact from './Contact'; import { ContactAddressFragment, @@ -49,13 +50,11 @@ const useStyles = makeStyles()(() => ({ display: 'flex', justifyContent: 'center', }, - buttonBlue: { - backgroundColor: theme.palette.mpdxBlue.main, - paddingRight: theme.spacing(1.5), - color: 'white', - [theme.breakpoints.down('xs')]: { - marginTop: theme.spacing(1), - marginBottom: theme.spacing(2), + confirmAllButton: { + [theme.breakpoints.down('md')]: { + width: '100%', + maxWidth: '200px', + margin: `${theme.spacing(1)} auto 0`, }, }, buttonIcon: { @@ -67,21 +66,23 @@ const useStyles = makeStyles()(() => ({ alignItems: 'center', flexWrap: 'wrap', marginTop: theme.spacing(1), + [theme.breakpoints.down('sm')]: { + justifyContent: 'center', + }, [theme.breakpoints.down('xs')]: { flexDirection: 'column', alignItems: 'start', }, }, - nativeSelect: { + select: { minWidth: theme.spacing(20), - width: '10%', marginLeft: theme.spacing(2), marginRight: theme.spacing(2), - [theme.breakpoints.down('xs')]: { - marginLeft: theme.spacing(0), - marginRight: theme.spacing(0), - marginTop: theme.spacing(1), - marginBottom: theme.spacing(1), + + [theme.breakpoints.down('md')]: { + width: '100%', + maxWidth: '200px', + margin: `${theme.spacing(1)} auto 0`, }, }, })); @@ -104,142 +105,147 @@ interface Props { accountListId: string; } +const sourceOptions = ['MPDX', 'DataServer']; + const FixSendNewsletter: React.FC = ({ accountListId }: Props) => { const { classes } = useStyles(); const [modalState, setModalState] = useState({ open: false, address: emptyAddress, + contactId: '', }); const { t } = useTranslation(); const [defaultSource, setDefaultSource] = useState('MPDX'); - const { data, loading } = useInvalidAddressesQuery({ + const { data, loading, refetch } = useInvalidAddressesQuery({ variables: { accountListId }, }); - const handleOpen = (address: ContactAddressFragment): void => { - setModalState({ open: true, address: address }); - }; - - const handleClose = (): void => { - setModalState({ open: false, address: emptyAddress }); + const handleOpen = ( + address: ContactAddressFragment, + contactId: string, + ): void => { + setModalState({ open: true, address, contactId }); }; - const handleChange = ( - event: - | React.ChangeEvent - | React.ChangeEvent, - props: string, - ): void => { - const tempAddress = modalState.address; // Error prevention, can remove later - setModalState((prevState) => ({ - ...prevState, - address: { - ...tempAddress, - [props]: - event.target.name === 'checkbox' - ? event.target.checked - : event.target.value, - }, - })); + const handleClose = (deleteAddress: boolean): void => { + if (deleteAddress) { + refetch(); + } + setModalState({ open: false, address: emptyAddress, contactId: '' }); }; - const handleSourceChange = ( - event: React.ChangeEvent, - ): void => { + const handleSourceChange = (event: SelectChangeEvent): void => { setDefaultSource(event.target.value); }; + const totalContacts = data?.contacts?.nodes?.length || 0; + //TODO: Make navbar selectId = "fixSendNewsletter" when other branch gets merged return ( - {!loading && data ? ( - - - {t('Fix Mailing Addresses')} - - - {data.contacts?.nodes.length > 0 ? ( - <> - - - - - {t('You have {{amount}} mailing addresses to confirm.', { - amount: data?.contacts.nodes.length, - })} - - - - {t( - 'Choose below which mailing address will be set as primary. Primary mailing addresses will be used for Newsletter exports.', - )} - - - {t('Default Primary Source:')} - - } - className={classes.nativeSelect} - value={defaultSource} - onChange={(event: React.ChangeEvent) => - handleSourceChange(event) - } - > - - - - + + + {t('Fix Mailing Addresses')} + + + + {loading && !data && ( + + )} + + {!loading && data && ( + + {!totalContacts && } + {totalContacts && ( + <> + + + + + {t( + 'You have {{amount}} mailing addresses to confirm.', + { + amount: totalContacts, + }, + )} + + + + {t( + 'Choose below which mailing address will be set as primary. Primary mailing addresses will be used for Newsletter exports.', + )} + + + {t('Default Primary Source:')} + + + + - - - - {data.contacts.nodes.map((contact) => ( - - ))} - - - - - }} + + + {data.contacts.nodes.map((contact) => ( + - - - - - ) : ( - - )} - - ) : ( - + ))} + + + + + }} + /> + + + + + )} + + )} + + {modalState.open && ( + )} - ); };