From 247561f197f433ce362237b451ca18c6e7d398af Mon Sep 17 00:00:00 2001 From: Mike Mathew Date: Tue, 17 Jan 2023 15:13:07 -0600 Subject: [PATCH 01/13] refactor(payment): add helper text above Paypal button --- src/components/register/register-member-payment.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/components/register/register-member-payment.tsx b/src/components/register/register-member-payment.tsx index 8d4665eb..c35ae941 100644 --- a/src/components/register/register-member-payment.tsx +++ b/src/components/register/register-member-payment.tsx @@ -357,6 +357,13 @@ const RegisterMemberPayment: FC = ({ + + Click on the PayPal button below to pay with credit card. + + Date: Wed, 18 Jan 2023 17:25:39 -0600 Subject: [PATCH 02/13] refactor(payment): move PrintInvoiceUI to separate component --- src/pages/members/PrintInvoiceUI.js | 81 +++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/pages/members/PrintInvoiceUI.js diff --git a/src/pages/members/PrintInvoiceUI.js b/src/pages/members/PrintInvoiceUI.js new file mode 100644 index 00000000..1b2a9402 --- /dev/null +++ b/src/pages/members/PrintInvoiceUI.js @@ -0,0 +1,81 @@ +// External Dependencies +import PropTypes from 'prop-types'; +import React, { useRef } from 'react'; +import ReactToPrint from 'react-to-print'; +import { useMediaQuery } from '@mui/material'; +import { useTheme } from 'styled-components'; + +// Internal Dependencies +import Invoice from '../../components/register/invoice'; +import RegisterButton from '../../components/register/register-button'; + +// Local Variables +const propTypes = { + currentUser: PropTypes.shape({ + Address1: PropTypes.string, + Address2: PropTypes.string, + AmountPaid: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + CellPhone: PropTypes.string, + City: PropTypes.string, + District: PropTypes.string, + FirstName: PropTypes.string, + LastName: PropTypes.string, + MemberType: PropTypes.string, + OfficePhone: PropTypes.string, + PaymentOption: PropTypes.string, + State: PropTypes.string, + Title: PropTypes.string, + ZipCode: PropTypes.string, + invoiceId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + receiptId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + }), +}; + +const defaultProps = { + currentUser: null, +}; + +// Component Definition +const PrintInvoiceUI = ({ currentUser }) => { + const theme = useTheme(); + const printInvoiceRef = useRef(); + + const isTabletOrSmallerScreen = useMediaQuery(theme.breakpoints.down('md')); + + if (!currentUser) { + return null; + } + + const buttonText = `View${!isTabletOrSmallerScreen ? ' Invoice' : ''}`; + + return ( +
+
+ printInvoiceRef.current} + trigger={() => ( + + {buttonText} + + )} + /> +
+ +
+ +
+
+ ); +}; + +PrintInvoiceUI.propTypes = propTypes; +PrintInvoiceUI.defaultProps = defaultProps; + +export default PrintInvoiceUI; From 6c7856f8b55c6e2235e484eee7809a1d3f42eb7b Mon Sep 17 00:00:00 2001 From: Mike Mathew Date: Wed, 18 Jan 2023 18:05:46 -0600 Subject: [PATCH 03/13] refactor(member): show invoice and paypal payment option to member who is registered but not paid --- .../register/paypal/paypal-button-wrapper.tsx | 21 ++- .../register/paypal/paypal-button.tsx | 7 +- src/components/register/register-button.js | 18 +-- src/pages/members/index.js | 2 + src/pages/members/member-content.js | 50 ++++-- src/pages/members/member-info.js | 146 +++++++++++++++--- src/pages/members/member-list.js | 6 +- src/pages/members/member-tasks.js | 80 +++------- src/pages/members/register.tsx | 4 +- 9 files changed, 220 insertions(+), 114 deletions(-) diff --git a/src/components/register/paypal/paypal-button-wrapper.tsx b/src/components/register/paypal/paypal-button-wrapper.tsx index ec7646f9..774db18d 100644 --- a/src/components/register/paypal/paypal-button-wrapper.tsx +++ b/src/components/register/paypal/paypal-button-wrapper.tsx @@ -34,6 +34,7 @@ export interface PaypalPaymentCancel { interface Props { amount: number; + noMargin?: boolean; onSuccessfulPayment: (payment: PaypalPayment) => void; } @@ -57,6 +58,7 @@ const ENV = process.env.NODE_ENV === 'production' // Component Definition const PaypalButtonWrapper: FC = ({ amount, + noMargin, onSuccessfulPayment, }) => { const [ @@ -64,11 +66,18 @@ const PaypalButtonWrapper: FC = ({ setPaymentError, ] = useState(null); + if (!amount) { + return null; + } + const errorText = ( <> There was an issue using PayPal. Please try clicking the{' '} "Pay with PayPal"{' '} - button again or print an invoice and send payment to the TMAC Treasurer. + button again. +
+ You can also print an invoice and + send payment to the TMAC Treasurer. ); @@ -89,9 +98,13 @@ const PaypalButtonWrapper: FC = ({ }; return ( - <> + - + = ({ - + ); }; diff --git a/src/components/register/paypal/paypal-button.tsx b/src/components/register/paypal/paypal-button.tsx index cdf29fb5..926f839b 100644 --- a/src/components/register/paypal/paypal-button.tsx +++ b/src/components/register/paypal/paypal-button.tsx @@ -137,7 +137,12 @@ const PaypalButton: FC = ({ onCancel={onCancel} onError={onError} payment={payment} - style={{ label: 'pay', tagline: 'false', size: 'medium' }} + style={{ + label: 'paypal', + shape: 'rect', + size: 'medium', + tagline: 'false', + }} /> ); }; diff --git a/src/components/register/register-button.js b/src/components/register/register-button.js index e62eceed..55ee3551 100644 --- a/src/components/register/register-button.js +++ b/src/components/register/register-button.js @@ -2,12 +2,12 @@ import PropTypes from 'prop-types'; import React from 'react'; import hex2rgba from 'hex2rgba'; -import { green, red } from '@mui/material/colors'; import styled from 'styled-components'; +import { green, red } from '@mui/material/colors'; // Internal Dependencies +import { options } from '../../utils/typography'; import presets from '../../utils/presets'; -import { rhythm, scale, options } from '../../utils/typography'; // Local Variables const propTypes = { @@ -29,20 +29,12 @@ const defaultProps = { }; const StyledButton = styled.button(({ $isDisabled, $isRed, theme }) => ({ - [presets.Tablet]: { - ...scale(2 / 5), - padding: `${rhythm(1 / 4)} ${rhythm(3 / 5)}`, - }, - [presets.VHd]: { - padding: `${rhythm(1 / 2)} ${rhythm(1)}`, - }, - - ...scale(1 / 5), border: `1px solid ${$isRed ? texasFlagRed : green500}`, - borderRadius: presets.radius, + borderRadius: theme.shape.borderRadius, display: 'inline-block', fontFamily: options.headerFontFamily.join(','), - padding: `${rhythm(2 / 5)} ${rhythm(1 / 2)}`, + padding: '3px 9px', + // Increase specificity '&&': { backgroundColor: $isRed || $isDisabled ? red['50'] : green['50'], diff --git a/src/pages/members/index.js b/src/pages/members/index.js index 4520260f..84a8f508 100644 --- a/src/pages/members/index.js +++ b/src/pages/members/index.js @@ -112,6 +112,8 @@ Members.propTypes = { const MembersWithContext = (props) => ( {(authUser) => { + console.log('MembersWithContext : authUser', authUser); + return ( { const [isLoadingUserData, setIsLoadingUserData] = useState(true); + const [allUsersData, setAllUsersData] = useState([]); + const [currentMemberData, setCurrentMemberData] = useState(null); + + console.log('MemberContent : currentMemberData', currentMemberData); + + const handleUpdateUserList = (userList) => { + setAllUsersData(userList); + }; useEffect(() => { - if (authUser) { + if (allUsersData.length < 1) { + const userList = []; + + doGetUsers('registration', userList, handleUpdateUserList); + } + }, [allUsersData.length]); + + useEffect(() => { + if (authUser && isLoadingUserData) { setIsLoadingUserData(!isLoadingUserData); } - }, [authUser]); + }, [authUser, isLoadingUserData]); + + useEffect(() => { + if (allUsersData.length > 0 && !currentMemberData) { + const currentMember = allUsersData.find( + // We used to use authUser.uid as the unique key in the Firestore + // Now we use authUser.email + // We have to search for both for backwards compatibility + (user) => user.userId === authUser.uid || user.userId === authUser.email, + ); + + setCurrentMemberData(currentMember); + } + }, [allUsersData, authUser.email, authUser.uid, currentMemberData]); const isRegisteredForCurrentYear = useMemo( () => (!currentMemberList?.length ? false : currentMemberList?.some( - (member) => member.userId === userId, + (member) => member.userId === userId || member.email === userId, )), [currentMemberList, userId] ); - const currentUser = currentMemberList?.find( - (member) => member.userId === userId, - ); - if (isLoadingUserData) { return ( diff --git a/src/pages/members/member-info.js b/src/pages/members/member-info.js index 094e145c..0fd5ab6a 100644 --- a/src/pages/members/member-info.js +++ b/src/pages/members/member-info.js @@ -18,11 +18,14 @@ import React, { useCallback, useState } from 'react'; import styled from 'styled-components'; // Internal Dependencies +import { doUpdateEmail } from '../../firebase/auth'; +import { emailRegex, currentSchoolYearLong } from '../../utils/helpers'; +import { options } from '../../utils/typography'; import Card from '../../components/shared/cards/card'; import CardSubtitle from '../../components/shared/CardSubtitle'; +import PaypalButtonWrapper from '../../components/register/paypal/paypal-button-wrapper'; +import PrintInvoiceUI from './PrintInvoiceUI'; import presets from '../../utils/presets'; -import { doUpdateEmail } from '../../firebase/auth'; -import { emailRegex, currentSchoolYearLong } from '../../utils/helpers'; // Local Variables const propTypes = { @@ -40,6 +43,7 @@ const propTypes = { Title: PropTypes.string, ZipCode: PropTypes.string, }), + memberEmail: PropTypes.string.isRequired, isRegisteredForCurrentYear: PropTypes.bool.isRequired, setShouldRefetchUserList: PropTypes.func.isRequired, }; @@ -56,11 +60,17 @@ const StyledRoot = styled.div(({ theme }) => ({ marginBottom: theme.spacing(2), }, '.divider': { - marginBottom: theme.spacing(2), + marginBottom: theme.spacing(3), }, '.emailContainer': { marginLeft: theme.spacing(2), }, + '.listItemSecondaryText': { + [theme.breakpoints.down('sm')]: { + fontSize: '0.9rem', + }, + maxWidth: '80%', + }, '.listItemText': { [theme.breakpoints.down('xs')]: { fontSize: '0.9rem', @@ -76,6 +86,17 @@ const StyledRoot = styled.div(({ theme }) => ({ maxWidth: '80%', }, fontSize: '1rem', + fontWeight: 500, + }, + '.paymentActionContainer': { + display: 'flex', + justifyContent: 'flex-end', + }, + '.paymentListItem': { + '&:not(:first-child)': { + marginTop: theme.spacing(1), + }, + marginBottom: 0, }, '.subheader': { marginTop: theme.spacing(3), @@ -86,14 +107,18 @@ const StyledRoot = styled.div(({ theme }) => ({ '.textField': { width: '75%', }, + width: '100%', })); // Component Definition const MemberInfo = ({ currentUser, isRegisteredForCurrentYear, + memberEmail, setShouldRefetchUserList, }) => { + // console.log('MemberInfo : currentUser', currentUser); + const [isChangeEmailDialogOpen, setIsChangeEmailDialogOpen] = useState(false); const [newEmailValue, setNewEmailValue] = useState(''); const [newEmailError, setNewEmailError] = useState(''); @@ -145,9 +170,101 @@ const MemberInfo = ({ } }, [newEmailError, newEmailValue, setShouldRefetchUserList]); + const isInvoiced = currentUser?.PaymentOption.toLowerCase() === 'invoiced'; + + const amountToPay = currentUser?.MemberType === 'Active' ? 50.00 : 30.00; + return ( +
+ Membership status + + + + + {!isRegisteredForCurrentYear ? 'Inactive' : currentUser?.MemberType || 'Active'} member + + )} + secondary={`for the ${currentSchoolYearLong} school year`} + /> + + + + {!isRegisteredForCurrentYear && isInvoiced && ( + <> + + You have a{' '} + + {currentUser?.MemberType === 'Active' ? '$50.00' : '$30.00'} + + {' '} + outstanding balance for the{' '} + {currentSchoolYearLong} school year. + + + + Payment Options + + + + + + + +
+ +
+ + + + + +
+ console.log('you did it!')} + /> +
+
+ + )} + + +
+
{currentUser && ( <> @@ -186,28 +303,6 @@ const MemberInfo = ({ )}
-
- Membership status - - - - - {!isRegisteredForCurrentYear ? 'Inactive' : currentUser?.MemberType || 'Active'} member - - )} - secondary={`for the ${currentSchoolYearLong} school year`} - /> - - - - -
-
Member actions @@ -218,6 +313,7 @@ const MemberInfo = ({ primary: 'listItemText', }} primary="Update email for TMAC website login" + secondary={`Current sign-in email: ${memberEmail}`} /> diff --git a/src/pages/members/member-list.js b/src/pages/members/member-list.js index 371598af..4a4f5982 100644 --- a/src/pages/members/member-list.js +++ b/src/pages/members/member-list.js @@ -43,14 +43,14 @@ const StyledRoot = styled.div(({ theme }) => ({ width: '0 auto', })); -const EMPTY_ARRAY = []; - // Component Definition const MemberListContent = ({ isAuthenticated, userEmail, }) => { - const [userData, setUserData] = useState(EMPTY_ARRAY); + const [userData, setUserData] = useState([]); + + console.log('MemberListContent : userData', userData); const handleUpdateUserList = (userList) => { setUserData(userList); diff --git a/src/pages/members/member-tasks.js b/src/pages/members/member-tasks.js index 71864840..4d090b7c 100644 --- a/src/pages/members/member-tasks.js +++ b/src/pages/members/member-tasks.js @@ -1,21 +1,21 @@ // External Dependencies -import { green, red } from '@mui/material/colors'; -import AnnouncementIcon from '@mui/icons-material/Announcement'; -import CheckIcon from '@mui/icons-material/Check'; +import Box from '@mui/material/Box'; import PropTypes from 'prop-types'; import React, { useRef } from 'react'; import ReactToPrint from 'react-to-print'; +import Typography from '@mui/material/Typography'; import styled from 'styled-components'; // Internal Dependencies +import { currentSchoolYearLong } from '../../utils/helpers'; import Card from '../../components/shared/cards/card'; import CardSubtitle from '../../components/shared/CardSubtitle'; import CtaButton from '../../components/masthead/cta-button'; import FuturaAnchor from '../../components/shared/FuturaAnchor'; import FuturaDiv from '../../components/shared/futura-div'; import Invoice from '../../components/register/invoice'; +import PrintInvoiceUI from './PrintInvoiceUI'; import RegisterButton from '../../components/register/register-button'; -import { currentSchoolYearLong } from '../../utils/helpers'; // Local Variables const propTypes = { @@ -58,40 +58,8 @@ const MemberTasks = ({ currentUser, isRegisteredForCurrentYear, }) => { - const printInvoiceRef = useRef(); const printReceiptRef = useRef(); - const registeredIcon = isRegisteredForCurrentYear ? ( - - ) : ( - - ); - - const invoiceInfo = currentUser && ( - -
Need a copy of your invoice?
- If you need to pay via invoice please send payment to the TMAC Treasurer as indicated on your - invoice. -
- printInvoiceRef.current} - trigger={() => Print Invoice} - /> -
- -
- -
-
- ); - const receiptInfo = currentUser && (

@@ -124,9 +92,9 @@ const MemberTasks = ({ ); - const isInvoiced = currentUser && currentUser.PaymentOption.toLowerCase() === 'invoiced'; + const isInvoiced = currentUser?.PaymentOption.toLowerCase() === 'invoiced'; - const isPaypal = currentUser && currentUser.PaymentOption.toLowerCase() === 'paypal'; + const isPaypal = currentUser?.PaymentOption.toLowerCase() === 'paypal'; return ( @@ -134,25 +102,27 @@ const MemberTasks = ({ Tasks for {currentSchoolYearLong} school year - ( -

- {registeredIcon} - Join TMAC for {currentSchoolYearLong} school year -
- )} - /> - - {!isRegisteredForCurrentYear && ( - - Join TMAC - + {!isRegisteredForCurrentYear && !isInvoiced && ( + + + Join TMAC + + )} - {isInvoiced && invoiceInfo} + {isInvoiced && ( + <> + + If you need to pay via invoice, please send payment + to the TMAC Treasurer as indicated on your + invoice. + + + + )} {isPaypal && receiptInfo} diff --git a/src/pages/members/register.tsx b/src/pages/members/register.tsx index 3fd20ee7..12b91477 100644 --- a/src/pages/members/register.tsx +++ b/src/pages/members/register.tsx @@ -281,12 +281,12 @@ const RegisterMemberWithContext: FC = (props) => ( {(authUser) => { const isAuthenticated = Boolean(authUser); - const authUserId = authUser ? authUser.email : undefined; + const authUserEmail = authUser ? authUser.email : undefined; return ( ); From 7e971864461ff62789827a49dff2e3c759680633 Mon Sep 17 00:00:00 2001 From: Mike Mathew Date: Thu, 19 Jan 2023 15:15:43 -0600 Subject: [PATCH 04/13] refactor(member): show registration status and if they need to pay --- src/components/masthead/cta-button.tsx | 27 ++++---- src/pages/members/PrintInvoiceUI.js | 9 +-- src/pages/members/index.js | 1 - src/pages/members/member-content.js | 43 ++++--------- src/pages/members/member-info.js | 69 ++++++++++++++------- src/pages/members/member-tasks.js | 85 +++++++++++++++++++++----- 6 files changed, 142 insertions(+), 92 deletions(-) diff --git a/src/components/masthead/cta-button.tsx b/src/components/masthead/cta-button.tsx index 09922d88..c19ca8e9 100644 --- a/src/components/masthead/cta-button.tsx +++ b/src/components/masthead/cta-button.tsx @@ -1,13 +1,14 @@ // External Dependencies -import hex2rgba from 'hex2rgba'; -import React, { FC } from 'react'; import { Link } from 'gatsby-theme-material-ui'; +import { Theme } from '@mui/material/styles'; import { blue, green } from '@mui/material/colors'; +import hex2rgba from 'hex2rgba'; +import React, { FC } from 'react'; import styled from 'styled-components'; // Internal Dependencies +import { options } from '../../utils/typography'; import presets from '../../utils/presets'; -import { rhythm, scale, options } from '../../utils/typography'; // Local Typings interface Props { @@ -15,6 +16,9 @@ interface Props { children: React.ReactNode; to?: string; } +interface StyledLinkProps { + $buttonColor: 'blue' | 'green'; +} // Local Variables const green500 = '#4caf50'; @@ -22,6 +26,9 @@ const green500 = '#4caf50'; const getButtonColor = ({ buttonColor, theme +}: { + buttonColor: 'blue' | 'green'; + theme: Theme; }) => { let background: string = blue['50']; let hover: string = blue['800']; @@ -40,28 +47,20 @@ const getButtonColor = ({ }; }; -const StyledLink = styled(Link)(({ $buttonColor, theme }) => { +const StyledLink = styled(Link)(({ $buttonColor, theme }) => { const color = getButtonColor({ buttonColor: $buttonColor, theme }); return { - [presets.Tablet]: { - ...scale(2 / 5), - padding: `${rhythm(1 / 4)} ${rhythm(3 / 5)}`, - }, - [presets.VHd]: { - padding: `${rhythm(1 / 2)} ${rhythm(1)}`, - }, + padding: '3px 9px', // Increase specificity '&&': { - ...scale(1 / 5), display: 'inline-block', fontFamily: options.headerFontFamily.join(','), - padding: `${rhythm(2 / 5)} ${rhythm(1 / 2)}`, - borderRadius: presets.radiusLg, + borderRadius: theme.shape.borderRadius, backgroundColor: color.background, boxShadow: 'none', border: `2px solid ${color.primary}`, diff --git a/src/pages/members/PrintInvoiceUI.js b/src/pages/members/PrintInvoiceUI.js index 1b2a9402..35f413a9 100644 --- a/src/pages/members/PrintInvoiceUI.js +++ b/src/pages/members/PrintInvoiceUI.js @@ -2,8 +2,6 @@ import PropTypes from 'prop-types'; import React, { useRef } from 'react'; import ReactToPrint from 'react-to-print'; -import { useMediaQuery } from '@mui/material'; -import { useTheme } from 'styled-components'; // Internal Dependencies import Invoice from '../../components/register/invoice'; @@ -37,17 +35,12 @@ const defaultProps = { // Component Definition const PrintInvoiceUI = ({ currentUser }) => { - const theme = useTheme(); const printInvoiceRef = useRef(); - const isTabletOrSmallerScreen = useMediaQuery(theme.breakpoints.down('md')); - if (!currentUser) { return null; } - const buttonText = `View${!isTabletOrSmallerScreen ? ' Invoice' : ''}`; - return (
@@ -55,7 +48,7 @@ const PrintInvoiceUI = ({ currentUser }) => { content={() => printInvoiceRef.current} trigger={() => ( - {buttonText} + Print Invoice )} /> diff --git a/src/pages/members/index.js b/src/pages/members/index.js index 84a8f508..d2f16fa3 100644 --- a/src/pages/members/index.js +++ b/src/pages/members/index.js @@ -80,7 +80,6 @@ const MembersHome = ({ currentMemberList={userData} memberEmail={authUser.email} setShouldRefetchUserList={setShouldRefetchUserList} - userId={authUser.uid} /> ) : ( diff --git a/src/pages/members/member-content.js b/src/pages/members/member-content.js index eff9f557..568fd55c 100644 --- a/src/pages/members/member-content.js +++ b/src/pages/members/member-content.js @@ -5,7 +5,7 @@ import { } from '@mui/material'; import { Link } from 'gatsby-theme-material-ui'; import PropTypes from 'prop-types'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useState } from 'react'; // Internal Dependencies import { @@ -13,7 +13,6 @@ import { TMAC_WEB_EXECUTIVE_SECRETARY, TMAC_WEB_ADMIN_EMAIL_LIST, } from '../../utils/member-constants'; -import { doGetUsers } from '../../firebase/db'; import Cards from '../../components/shared/cards'; import EnhancedAlert from '../../components/shared/EnhancedAlert'; @@ -38,7 +37,6 @@ const propTypes = { currentMemberList: PropTypes.arrayOf(PropTypes.shape({})), memberEmail: PropTypes.string, setShouldRefetchUserList: PropTypes.func.isRequired, - userId: PropTypes.string, }; const defaultProps = { @@ -46,7 +44,6 @@ const defaultProps = { contentfulFileShareData: null, contentfulFileShareDescriptionData: null, currentMemberList: null, - userId: null, }; // Component Definition @@ -57,26 +54,10 @@ const MemberContent = ({ currentMemberList, memberEmail, setShouldRefetchUserList, - userId, }) => { const [isLoadingUserData, setIsLoadingUserData] = useState(true); - const [allUsersData, setAllUsersData] = useState([]); const [currentMemberData, setCurrentMemberData] = useState(null); - console.log('MemberContent : currentMemberData', currentMemberData); - - const handleUpdateUserList = (userList) => { - setAllUsersData(userList); - }; - - useEffect(() => { - if (allUsersData.length < 1) { - const userList = []; - - doGetUsers('registration', userList, handleUpdateUserList); - } - }, [allUsersData.length]); - useEffect(() => { if (authUser && isLoadingUserData) { setIsLoadingUserData(!isLoadingUserData); @@ -84,8 +65,8 @@ const MemberContent = ({ }, [authUser, isLoadingUserData]); useEffect(() => { - if (allUsersData.length > 0 && !currentMemberData) { - const currentMember = allUsersData.find( + if (currentMemberList?.length > 0 && !currentMemberData) { + const currentMember = currentMemberList.find( // We used to use authUser.uid as the unique key in the Firestore // Now we use authUser.email // We have to search for both for backwards compatibility @@ -94,16 +75,14 @@ const MemberContent = ({ setCurrentMemberData(currentMember); } - }, [allUsersData, authUser.email, authUser.uid, currentMemberData]); - - const isRegisteredForCurrentYear = useMemo( - () => - (!currentMemberList?.length ? false - : currentMemberList?.some( - (member) => member.userId === userId || member.email === userId, - )), - [currentMemberList, userId] - ); + }, [ + authUser.email, + authUser.uid, + currentMemberData, + currentMemberList, + ]); + + const isRegisteredForCurrentYear = Boolean(currentMemberData); if (isLoadingUserData) { return ( diff --git a/src/pages/members/member-info.js b/src/pages/members/member-info.js index 0fd5ab6a..b6de841f 100644 --- a/src/pages/members/member-info.js +++ b/src/pages/members/member-info.js @@ -92,6 +92,9 @@ const StyledRoot = styled.div(({ theme }) => ({ display: 'flex', justifyContent: 'flex-end', }, + '.paymentList': { + marginBottom: theme.spacing(1), + }, '.paymentListItem': { '&:not(:first-child)': { marginTop: theme.spacing(1), @@ -110,6 +113,14 @@ const StyledRoot = styled.div(({ theme }) => ({ width: '100%', })); +export const StyledStrong = styled.strong(({ theme }) => ({ + [theme.breakpoints.down('sm')]: { + fontSize: '1.1rem', + }, + fontSize: '1.2rem', + whiteSpace: 'pre', +})); + // Component Definition const MemberInfo = ({ currentUser, @@ -117,8 +128,6 @@ const MemberInfo = ({ memberEmail, setShouldRefetchUserList, }) => { - // console.log('MemberInfo : currentUser', currentUser); - const [isChangeEmailDialogOpen, setIsChangeEmailDialogOpen] = useState(false); const [newEmailValue, setNewEmailValue] = useState(''); const [newEmailError, setNewEmailError] = useState(''); @@ -172,6 +181,8 @@ const MemberInfo = ({ const isInvoiced = currentUser?.PaymentOption.toLowerCase() === 'invoiced'; + const needsToPay = !currentUser?.AmountPaid; + const amountToPay = currentUser?.MemberType === 'Active' ? 50.00 : 30.00; return ( @@ -188,7 +199,15 @@ const MemberInfo = ({ }} primary={( <> - {!isRegisteredForCurrentYear ? 'Inactive' : currentUser?.MemberType || 'Active'} member + {!isRegisteredForCurrentYear && 'Not registered'} + + {isRegisteredForCurrentYear && needsToPay && 'Inactive member'} + + {isRegisteredForCurrentYear && !needsToPay && ( + <> + {currentUser?.MemberType || 'Active'} member + + )} )} secondary={`for the ${currentSchoolYearLong} school year`} @@ -203,13 +222,11 @@ const MemberInfo = ({ paragraph variant="body2" > - You have a{' '} - - {currentUser?.MemberType === 'Active' ? '$50.00' : '$30.00'} - + Outstanding balance: {' '} - outstanding balance for the{' '} - {currentSchoolYearLong} school year. + + {currentUser?.MemberType === 'Active' ? '$50.00' : '$30.00'} + - +
- + console.log('you did it!')} + />
@@ -246,17 +265,15 @@ const MemberInfo = ({ primary: 'listItemText', secondary: 'listItemSecondaryText', }} - primary="Pay online with credit card" - secondary="TMAC uses PayPal to securely process online credit card payments." + primary="Send invoice with payment" + secondary="Mail invoice with payment + to the TMAC Treasurer as indicated on your + invoice." />
- console.log('you did it!')} - /> +
@@ -313,7 +330,13 @@ const MemberInfo = ({ primary: 'listItemText', }} primary="Update email for TMAC website login" - secondary={`Current sign-in email: ${memberEmail}`} + secondary={( + <> + Current sign-in email: +
+ {memberEmail} + + )} /> diff --git a/src/pages/members/member-tasks.js b/src/pages/members/member-tasks.js index 4d090b7c..3688cad3 100644 --- a/src/pages/members/member-tasks.js +++ b/src/pages/members/member-tasks.js @@ -1,5 +1,8 @@ // External Dependencies -import Box from '@mui/material/Box'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemText from '@mui/material/ListItemText'; +import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction'; import PropTypes from 'prop-types'; import React, { useRef } from 'react'; import ReactToPrint from 'react-to-print'; @@ -44,13 +47,33 @@ const defaultProps = { currentUser: null, }; -const StyledCard = styled(Card)(({ theme }) => ({ +const StyledRoot = styled(Card)(({ theme }) => ({ '.buttonContainer': { marginTop: theme.spacing(2), }, '.hidden': { display: 'none', }, + '.listItemSecondaryText': { + [theme.breakpoints.down('sm')]: { + fontSize: '0.9rem', + }, + maxWidth: '80%', + }, + '.listItemText': { + [theme.breakpoints.down('xs')]: { + fontSize: '0.9rem', + maxWidth: '70%', + }, + fontSize: '1rem', + fontWeight: 500, + }, + '.paymentListItem': { + '&:not(:first-child)': { + marginTop: theme.spacing(1), + }, + marginBottom: 0, + }, })); // Component Definition @@ -97,21 +120,54 @@ const MemberTasks = ({ const isPaypal = currentUser?.PaymentOption.toLowerCase() === 'paypal'; return ( - + Tasks for {currentSchoolYearLong} school year - {!isRegisteredForCurrentYear && !isInvoiced && ( - - - Join TMAC - - - )} + + + + + {!isRegisteredForCurrentYear && ( + + + Register + + + )} + + + + + + + + Register + + + + {isInvoiced && ( <> @@ -120,6 +176,7 @@ const MemberTasks = ({ to the TMAC Treasurer as indicated on your invoice.
+ )} @@ -140,7 +197,7 @@ const MemberTasks = ({ )} - + ); }; From 3cf2f4474d0482af687606053b2372eae771db29 Mon Sep 17 00:00:00 2001 From: Mike Mathew Date: Thu, 19 Jan 2023 15:38:14 -0600 Subject: [PATCH 05/13] refactor(members): separate MemberStatus into own component --- src/pages/members/MemberStatus.js | 210 ++++++++++++++++++++++++++++ src/pages/members/member-content.js | 7 +- src/pages/members/member-info.js | 107 +------------- 3 files changed, 217 insertions(+), 107 deletions(-) create mode 100644 src/pages/members/MemberStatus.js diff --git a/src/pages/members/MemberStatus.js b/src/pages/members/MemberStatus.js new file mode 100644 index 00000000..e90b8939 --- /dev/null +++ b/src/pages/members/MemberStatus.js @@ -0,0 +1,210 @@ +// External Dependencies +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemText from '@mui/material/ListItemText'; +import Typography from '@mui/material/Typography'; +import PropTypes from 'prop-types'; +import React from 'react'; +import styled from 'styled-components'; + +// Internal Dependencies +import { currentSchoolYearLong } from '../../utils/helpers'; +import { options } from '../../utils/typography'; +import Card from '../../components/shared/cards/card'; +import CardSubtitle from '../../components/shared/CardSubtitle'; +import PaypalButtonWrapper from '../../components/register/paypal/paypal-button-wrapper'; +import PrintInvoiceUI from './PrintInvoiceUI'; +import presets from '../../utils/presets'; + +// Local Variables +const propTypes = { + currentMemberData: PropTypes.shape({ + Address1: PropTypes.string, + Address2: PropTypes.string, + CellPhone: PropTypes.string, + City: PropTypes.string, + District: PropTypes.string, + FirstName: PropTypes.string, + LastName: PropTypes.string, + MemberType: PropTypes.string, + OfficePhone: PropTypes.string, + State: PropTypes.string, + Title: PropTypes.string, + ZipCode: PropTypes.string, + }), + isRegisteredForCurrentYear: PropTypes.bool.isRequired, +}; + +const defaultProps = { + currentMemberData: null, +}; + +const StyledRoot = styled(Card)(({ theme }) => ({ + '.contentText': { + marginBottom: theme.spacing(2), + }, + '.innerContainer': { + paddingBottom: theme.spacing(2), + }, + '.listItemSecondaryText': { + [theme.breakpoints.down('sm')]: { + fontSize: '0.9rem', + }, + maxWidth: '80%', + }, + '.listItem': { + marginBottom: 0, + paddingBottom: 0, + }, + '.listItemText': { + [theme.breakpoints.down('xs')]: { + fontSize: '0.9rem', + maxWidth: '70%', + }, + [presets.Mobile]: { + maxWidth: '60%', + }, + [presets.Phablet]: { + maxWidth: '70%', + }, + [presets.Tablet]: { + maxWidth: '80%', + }, + fontSize: '1rem', + fontWeight: 500, + }, + '.paymentActionContainer': { + display: 'flex', + justifyContent: 'flex-end', + }, + '.paymentList': { + marginBottom: theme.spacing(1), + }, + '.paymentListItem': { + '&:not(:first-child)': { + marginTop: theme.spacing(1), + }, + marginBottom: 0, + }, + marginBottom: 0, + width: '100%', +})); + +export const StyledStrong = styled.strong(({ theme }) => ({ + [theme.breakpoints.down('sm')]: { + fontSize: '1.1rem', + }, + fontSize: '1.2rem', + whiteSpace: 'pre', +})); + +// Component Definition +const MemberStatus = ({ + currentMemberData, + isRegisteredForCurrentYear, +}) => { + const isInvoiced = currentMemberData?.PaymentOption.toLowerCase() === 'invoiced'; + + const needsToPay = !currentMemberData?.AmountPaid; + + const amountToPay = currentMemberData?.MemberType === 'Active' ? 50.00 : 30.00; + + return ( + + Membership status + + + + + {!isRegisteredForCurrentYear && 'Not registered'} + + {isRegisteredForCurrentYear && needsToPay && 'Inactive member'} + + {isRegisteredForCurrentYear && !needsToPay && ( + <> + {currentMemberData?.MemberType || 'Active'} member + + )} + + )} + secondary={`for the ${currentSchoolYearLong} school year`} + /> + + + + {!isRegisteredForCurrentYear && isInvoiced && ( + <> + + Outstanding balance: + {' '} + + {currentMemberData?.MemberType === 'Active' ? '$50.00' : '$30.00'} + + + + + Payment Options + + + + + + + +
+ console.log('you did it!')} + /> +
+ + + + + +
+ +
+
+ + )} +
+ ); +}; + +MemberStatus.propTypes = propTypes; +MemberStatus.defaultProps = defaultProps; + +export default MemberStatus; diff --git a/src/pages/members/member-content.js b/src/pages/members/member-content.js index 568fd55c..c1ba8438 100644 --- a/src/pages/members/member-content.js +++ b/src/pages/members/member-content.js @@ -19,6 +19,7 @@ import EnhancedAlert from '../../components/shared/EnhancedAlert'; // Local Dependencies import MemberFileShareCard from './MemberFileShareCard'; import MemberInfo from './member-info'; +import MemberStatus from './MemberStatus'; import MemberTasks from './member-tasks'; // Sidebar Data @@ -110,9 +111,13 @@ const MemberContent = ({ + + diff --git a/src/pages/members/member-info.js b/src/pages/members/member-info.js index b6de841f..ff52bc5e 100644 --- a/src/pages/members/member-info.js +++ b/src/pages/members/member-info.js @@ -19,12 +19,9 @@ import styled from 'styled-components'; // Internal Dependencies import { doUpdateEmail } from '../../firebase/auth'; -import { emailRegex, currentSchoolYearLong } from '../../utils/helpers'; -import { options } from '../../utils/typography'; +import { emailRegex } from '../../utils/helpers'; import Card from '../../components/shared/cards/card'; import CardSubtitle from '../../components/shared/CardSubtitle'; -import PaypalButtonWrapper from '../../components/register/paypal/paypal-button-wrapper'; -import PrintInvoiceUI from './PrintInvoiceUI'; import presets from '../../utils/presets'; // Local Variables @@ -44,7 +41,6 @@ const propTypes = { ZipCode: PropTypes.string, }), memberEmail: PropTypes.string.isRequired, - isRegisteredForCurrentYear: PropTypes.bool.isRequired, setShouldRefetchUserList: PropTypes.func.isRequired, }; @@ -124,7 +120,6 @@ export const StyledStrong = styled.strong(({ theme }) => ({ // Component Definition const MemberInfo = ({ currentUser, - isRegisteredForCurrentYear, memberEmail, setShouldRefetchUserList, }) => { @@ -179,109 +174,9 @@ const MemberInfo = ({ } }, [newEmailError, newEmailValue, setShouldRefetchUserList]); - const isInvoiced = currentUser?.PaymentOption.toLowerCase() === 'invoiced'; - - const needsToPay = !currentUser?.AmountPaid; - - const amountToPay = currentUser?.MemberType === 'Active' ? 50.00 : 30.00; - return ( -
- Membership status - - - - - {!isRegisteredForCurrentYear && 'Not registered'} - - {isRegisteredForCurrentYear && needsToPay && 'Inactive member'} - - {isRegisteredForCurrentYear && !needsToPay && ( - <> - {currentUser?.MemberType || 'Active'} member - - )} - - )} - secondary={`for the ${currentSchoolYearLong} school year`} - /> - - - - {!isRegisteredForCurrentYear && isInvoiced && ( - <> - - Outstanding balance: - {' '} - - {currentUser?.MemberType === 'Active' ? '$50.00' : '$30.00'} - - - - - Payment Options - - - - - - - -
- console.log('you did it!')} - /> -
- - - - - -
- -
-
- - )} - - -
-
{currentUser && ( <> From 6b43b89284418e9e95e1d110ba4d0c61c72d020a Mon Sep 17 00:00:00 2001 From: Mike Mathew Date: Thu, 19 Jan 2023 17:22:56 -0600 Subject: [PATCH 06/13] refactor(members): put more actions into list item actions --- src/pages/members/MemberActions.js | 254 +++++++++++++++ src/pages/members/MemberStatus.js | 126 ++++---- .../{member-tasks.js => RegistrationTasks.js} | 102 +++--- src/pages/members/member-content.js | 22 +- src/pages/members/member-info.js | 292 +++--------------- 5 files changed, 426 insertions(+), 370 deletions(-) create mode 100644 src/pages/members/MemberActions.js rename src/pages/members/{member-tasks.js => RegistrationTasks.js} (70%) diff --git a/src/pages/members/MemberActions.js b/src/pages/members/MemberActions.js new file mode 100644 index 00000000..fc8046f1 --- /dev/null +++ b/src/pages/members/MemberActions.js @@ -0,0 +1,254 @@ +// External Dependencies +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogTitle from '@mui/material/DialogTitle'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction'; +import ListItemText from '@mui/material/ListItemText'; +import TextField from '@mui/material/TextField'; +import Typography from '@mui/material/Typography'; +import PropTypes from 'prop-types'; +import React, { useCallback, useState } from 'react'; +import styled from 'styled-components'; + +// Internal Dependencies +import { doUpdateEmail } from '../../firebase/auth'; +import { emailRegex } from '../../utils/helpers'; +import { options } from '../../utils/typography'; +import Card from '../../components/shared/cards/card'; +import CardSubtitle from '../../components/shared/CardSubtitle'; +import presets from '../../utils/presets'; + +// Local Variables +const propTypes = { + memberEmail: PropTypes.string.isRequired, + setShouldRefetchUserList: PropTypes.func.isRequired, +}; + +const StyledRoot = styled(Card)(({ theme }) => ({ + '.contentText': { + marginBottom: theme.spacing(2), + }, + '.emailButton': { + borderBottom: `1px solid ${theme.palette.primary.main}`, + boxShadow: 'none', + color: theme.palette.primary.main, + fontFamily: options.headerFontFamily.join(','), + fontWeight: 600, + letterSpacing: 0.5, + textDecoration: 'none', + textTransform: 'capitalize', + }, + '.innerContainer': { + paddingBottom: theme.spacing(2), + }, + '.listItemSecondaryText': { + [theme.breakpoints.down('sm')]: { + fontSize: '0.9rem', + }, + maxWidth: '80%', + }, + '.listItem': { + marginBottom: 0, + paddingBottom: 0, + }, + '.listItemText': { + [theme.breakpoints.down('xs')]: { + fontSize: '0.9rem', + maxWidth: '70%', + }, + [presets.Mobile]: { + maxWidth: '60%', + }, + [presets.Phablet]: { + maxWidth: '70%', + }, + [presets.Tablet]: { + maxWidth: '80%', + }, + fontSize: '1rem', + fontWeight: 500, + }, + marginBottom: 0, + width: '100%', +})); + +const StyledDialog = styled(Dialog)({ + '.MuiDialogActions-root': { + '.MuiButton-root': { + textTransform: 'capitalize', + }, + }, +}); + +// Component Definition +const MemberActions = ({ + memberEmail, + setShouldRefetchUserList, +}) => { + const [isChangeEmailDialogOpen, setIsChangeEmailDialogOpen] = useState(false); + const [newEmailValue, setNewEmailValue] = useState(''); + const [newEmailError, setNewEmailError] = useState(''); + + const handleOpenChangeEmailDialog = useCallback(() => { + setIsChangeEmailDialogOpen(true); + }, []); + + const handleCloseChangeEmailDialog = useCallback(() => { + setIsChangeEmailDialogOpen(false); + setNewEmailValue(''); + setNewEmailError(''); + }, []); + + const handleUpdateNewEmailValue = useCallback((event) => { + const { value } = event.target; + + if (!value) { + setNewEmailError('Email is required'); + } else if (value && emailRegex.test(value)) { + setNewEmailError(''); + } else if (value && !emailRegex.test(value)) { + setNewEmailError('Use a valid email'); + } + + setNewEmailValue(value); + }, []); + + const handleSubmitChangeEmail = useCallback(() => { + if (!newEmailError) { + doUpdateEmail(newEmailValue) + .then(() => { + setShouldRefetchUserList(true); + setIsChangeEmailDialogOpen(false); + setNewEmailValue(''); + setNewEmailError(''); + }) + .catch((err) => { + let errorMessage; + + if (err && err.message === 'EMAIL_EXISTS') { + errorMessage = 'Email unavailable.'; + } else if (err && err.message.startsWith('This operation is sensitive')) { + errorMessage = err.message; + } else errorMessage = 'There was an error updating your email. Please try again.'; + + setNewEmailError(errorMessage); + }); + } + }, [newEmailError, newEmailValue, setShouldRefetchUserList]); + + return ( + + Member actions + + + + + Current sign-in email: +
+ {memberEmail} + + )} + /> + + + + +
+ + + + Email the TMAC Executive Secretary. + + )} + /> + + + + + +
+ + + Change Email Address + + + + This email is used to sign in to the TMAC website. Make sure you have access to the new + email address for future use. + + + + + + + + + + + +
+ ); +}; + +MemberActions.propTypes = propTypes; + +export default MemberActions; diff --git a/src/pages/members/MemberStatus.js b/src/pages/members/MemberStatus.js index e90b8939..afe01ae9 100644 --- a/src/pages/members/MemberStatus.js +++ b/src/pages/members/MemberStatus.js @@ -90,7 +90,7 @@ const StyledRoot = styled(Card)(({ theme }) => ({ width: '100%', })); -export const StyledStrong = styled.strong(({ theme }) => ({ +const StyledStrong = styled.strong(({ theme }) => ({ [theme.breakpoints.down('sm')]: { fontSize: '1.1rem', }, @@ -131,74 +131,74 @@ const MemberStatus = ({ )} - )} + )} secondary={`for the ${currentSchoolYearLong} school year`} /> {!isRegisteredForCurrentYear && isInvoiced && ( - <> - - Outstanding balance: - {' '} - - {currentMemberData?.MemberType === 'Active' ? '$50.00' : '$30.00'} - - - - - Payment Options - - - - - - - -
- console.log('you did it!')} - /> -
- - - - - -
- -
-
- + <> + + Outstanding balance: + {' '} + + {currentMemberData?.MemberType === 'Active' ? '$50.00' : '$30.00'} + + + + + Payment Options + + + + + + + +
+ console.log('you did it!')} + /> +
+ + + + + +
+ +
+
+ )} ); diff --git a/src/pages/members/member-tasks.js b/src/pages/members/RegistrationTasks.js similarity index 70% rename from src/pages/members/member-tasks.js rename to src/pages/members/RegistrationTasks.js index 3688cad3..7e978c6c 100644 --- a/src/pages/members/member-tasks.js +++ b/src/pages/members/RegistrationTasks.js @@ -6,18 +6,16 @@ import ListItemSecondaryAction from '@mui/material/ListItemSecondaryAction'; import PropTypes from 'prop-types'; import React, { useRef } from 'react'; import ReactToPrint from 'react-to-print'; -import Typography from '@mui/material/Typography'; -import styled from 'styled-components'; +import styled, { useTheme } from 'styled-components'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; // Internal Dependencies import { currentSchoolYearLong } from '../../utils/helpers'; import Card from '../../components/shared/cards/card'; import CardSubtitle from '../../components/shared/CardSubtitle'; import CtaButton from '../../components/masthead/cta-button'; -import FuturaAnchor from '../../components/shared/FuturaAnchor'; import FuturaDiv from '../../components/shared/futura-div'; import Invoice from '../../components/register/invoice'; -import PrintInvoiceUI from './PrintInvoiceUI'; import RegisterButton from '../../components/register/register-button'; // Local Variables @@ -51,6 +49,12 @@ const StyledRoot = styled(Card)(({ theme }) => ({ '.buttonContainer': { marginTop: theme.spacing(2), }, + '.checkCircleIcon': { + height: 20, + marginLeft: theme.spacing(1), + transform: 'translateY(4px)', + width: 20, + }, '.hidden': { display: 'none', }, @@ -77,10 +81,11 @@ const StyledRoot = styled(Card)(({ theme }) => ({ })); // Component Definition -const MemberTasks = ({ +const RegistrationTasks = ({ currentUser, isRegisteredForCurrentYear, }) => { + const theme = useTheme(); const printReceiptRef = useRef(); const receiptInfo = currentUser && ( @@ -115,25 +120,38 @@ const MemberTasks = ({ ); - const isInvoiced = currentUser?.PaymentOption.toLowerCase() === 'invoiced'; - const isPaypal = currentUser?.PaymentOption.toLowerCase() === 'paypal'; return ( - Tasks for {currentSchoolYearLong} school year + Registration tasks - + + Register + {isRegisteredForCurrentYear && ( + + )} + + )} + secondary={ + isRegisteredForCurrentYear + ? 'You are registered — thanks for being a member!' + : 'Become a member for the current school year.' +} /> {!isRegisteredForCurrentYear && ( @@ -157,51 +175,37 @@ const MemberTasks = ({ primary="Pay Membership Dues" secondary="Pay online using credit card or send payment with invoice." /> - - - - Register - - - - {isInvoiced && ( - <> - - If you need to pay via invoice, please send payment - to the TMAC Treasurer as indicated on your - invoice. - + {isRegisteredForCurrentYear && ( + + - - - )} + + + Download + + + + )} + {isPaypal && receiptInfo} - - {isRegisteredForCurrentYear && ( - <> - - If your district requires the IRS W-9 Form for TMAC, download or print a copy below. - - - - Download W-9 - - - )} ); }; -MemberTasks.propTypes = propTypes; -MemberTasks.defaultProps = defaultProps; +RegistrationTasks.propTypes = propTypes; +RegistrationTasks.defaultProps = defaultProps; -export default MemberTasks; +export default RegistrationTasks; diff --git a/src/pages/members/member-content.js b/src/pages/members/member-content.js index c1ba8438..fe4937cd 100644 --- a/src/pages/members/member-content.js +++ b/src/pages/members/member-content.js @@ -17,10 +17,11 @@ import Cards from '../../components/shared/cards'; import EnhancedAlert from '../../components/shared/EnhancedAlert'; // Local Dependencies +import MemberActions from './MemberActions'; import MemberFileShareCard from './MemberFileShareCard'; import MemberInfo from './member-info'; import MemberStatus from './MemberStatus'; -import MemberTasks from './member-tasks'; +import RegistrationTasks from './RegistrationTasks'; // Sidebar Data import membersSidebar from './members-links.yml'; @@ -116,16 +117,23 @@ const MemberContent = ({ isRegisteredForCurrentYear={isRegisteredForCurrentYear} /> - + {currentMemberData && ( + + )} - + +

For Members

diff --git a/src/pages/members/member-info.js b/src/pages/members/member-info.js index ff52bc5e..d8fe28cf 100644 --- a/src/pages/members/member-info.js +++ b/src/pages/members/member-info.js @@ -1,25 +1,12 @@ // External Dependencies -import { - Button, - Dialog, - DialogActions, - DialogTitle, - DialogContent, - Divider, - List, - ListItem, - ListItemText, - ListItemSecondaryAction, - TextField, - Typography, -} from '@mui/material'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemText from '@mui/material/ListItemText'; import PropTypes from 'prop-types'; -import React, { useCallback, useState } from 'react'; +import React from 'react'; import styled from 'styled-components'; // Internal Dependencies -import { doUpdateEmail } from '../../firebase/auth'; -import { emailRegex } from '../../utils/helpers'; import Card from '../../components/shared/cards/card'; import CardSubtitle from '../../components/shared/CardSubtitle'; import presets from '../../utils/presets'; @@ -39,33 +26,24 @@ const propTypes = { State: PropTypes.string, Title: PropTypes.string, ZipCode: PropTypes.string, - }), - memberEmail: PropTypes.string.isRequired, - setShouldRefetchUserList: PropTypes.func.isRequired, + }).isRequired, }; -const defaultProps = { - currentUser: null, -}; - -const StyledRoot = styled.div(({ theme }) => ({ +const StyledRoot = styled(Card)(({ theme }) => ({ '.address': { fontStyle: 'normal', }, '.contentText': { marginBottom: theme.spacing(2), }, - '.divider': { - marginBottom: theme.spacing(3), - }, '.emailContainer': { marginLeft: theme.spacing(2), }, - '.listItemSecondaryText': { - [theme.breakpoints.down('sm')]: { - fontSize: '0.9rem', - }, - maxWidth: '80%', + '.infoList, .infoListItem': { + paddingBottom: 0, + }, + '.innerContainer': { + paddingBottom: theme.spacing(2), }, '.listItemText': { [theme.breakpoints.down('xs')]: { @@ -84,235 +62,47 @@ const StyledRoot = styled.div(({ theme }) => ({ fontSize: '1rem', fontWeight: 500, }, - '.paymentActionContainer': { - display: 'flex', - justifyContent: 'flex-end', - }, - '.paymentList': { - marginBottom: theme.spacing(1), - }, - '.paymentListItem': { - '&:not(:first-child)': { - marginTop: theme.spacing(1), - }, - marginBottom: 0, - }, - '.subheader': { - marginTop: theme.spacing(3), - }, - '.subtitle': { - fontWeight: 600, - }, - '.textField': { - width: '75%', - }, + marginBottom: 0, width: '100%', })); -export const StyledStrong = styled.strong(({ theme }) => ({ - [theme.breakpoints.down('sm')]: { - fontSize: '1.1rem', - }, - fontSize: '1.2rem', - whiteSpace: 'pre', -})); - // Component Definition -const MemberInfo = ({ - currentUser, - memberEmail, - setShouldRefetchUserList, -}) => { - const [isChangeEmailDialogOpen, setIsChangeEmailDialogOpen] = useState(false); - const [newEmailValue, setNewEmailValue] = useState(''); - const [newEmailError, setNewEmailError] = useState(''); - - const handleOpenChangeEmailDialog = useCallback(() => { - setIsChangeEmailDialogOpen(true); - }, []); - - const handleCloseChangeEmailDialog = useCallback(() => { - setIsChangeEmailDialogOpen(false); - setNewEmailValue(''); - setNewEmailError(''); - }, []); - - const handleUpdateNewEmailValue = useCallback((event) => { - const { value } = event.target; - - if (!value) { - setNewEmailError('Email is required'); - } else if (value && emailRegex.test(value)) { - setNewEmailError(''); - } else if (value && !emailRegex.test(value)) { - setNewEmailError('Use a valid email'); - } - - setNewEmailValue(value); - }, []); - - const handleSubmitChangeEmail = useCallback(() => { - if (!newEmailError) { - doUpdateEmail(newEmailValue) - .then(() => { - setShouldRefetchUserList(true); - setIsChangeEmailDialogOpen(false); - setNewEmailValue(''); - setNewEmailError(''); - }) - .catch((err) => { - let errorMessage; - - if (err && err.message === 'EMAIL_EXISTS') { - errorMessage = 'Email unavailable.'; - } else if (err && err.message.startsWith('This operation is sensitive')) { - errorMessage = err.message; - } else errorMessage = 'There was an error updating your email. Please try again.'; - - setNewEmailError(errorMessage); - }); - } - }, [newEmailError, newEmailValue, setShouldRefetchUserList]); - +const MemberInfo = ({ currentUser }) => { return ( - -
- {currentUser && ( - <> - Member Info - - - - -
{currentUser.FirstName} {currentUser.LastName}
-
{currentUser.Title}, {currentUser.District}
- - )} - secondary={( -
-
{currentUser.Address1}
-
{currentUser.Address2}
-
- {currentUser.City}, {currentUser.State} {currentUser.ZipCode} -
-
Office: {currentUser.OfficePhone}
-
Cell: {currentUser.CellPhone}
-
{currentUser.Email}
-
- )} - secondaryTypographyProps={{ - component: 'div', - }} - /> -
-
- - - - )} -
- -
- Member actions - - - - - Current sign-in email: -
- {memberEmail} - - )} - /> - - - - -
- - - - Email the TMAC Executive Secretary. - - )} - /> - -
-
-
- - - Change Email Address - - - - This email is used to sign in to the TMAC website. Make sure you have access to the new - email address for future use. - - - Member Info + + + + +
{currentUser.FirstName} {currentUser.LastName}
+
{currentUser.Title}, {currentUser.District}
+ + )} + secondary={( +
+
{currentUser.Address1}
+
{currentUser.Address2}
+
+ {currentUser.City}, {currentUser.State} {currentUser.ZipCode} +
+
Office: {currentUser.OfficePhone}
+
Cell: {currentUser.CellPhone}
+
{currentUser.Email}
+
+ )} + secondaryTypographyProps={{ + component: 'div', + }} /> -
- - - - - - -
+ +
); }; MemberInfo.propTypes = propTypes; -MemberInfo.defaultProps = defaultProps; export default MemberInfo; From 6216f5208746fa93ddf79b0af8551d181bc7df7d Mon Sep 17 00:00:00 2001 From: Mike Mathew Date: Thu, 19 Jan 2023 17:40:40 -0600 Subject: [PATCH 07/13] refactor(db): simplify log statements --- src/firebase/db.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/firebase/db.js b/src/firebase/db.js index dbb18164..40c72bef 100644 --- a/src/firebase/db.js +++ b/src/firebase/db.js @@ -16,7 +16,7 @@ export const doCreateEntry = ( ) => { const collectionName = getFirebaseCollectionName(collection); - console.log('doCreateEntry : creating...', `${collection}_${currentSchoolYearShort}`); + console.log('creating data...', `${collection}_${currentSchoolYearShort}`); return db .collection(collectionName) @@ -41,7 +41,7 @@ export const doUpdateEntry = ( ) => { const collectionName = getFirebaseCollectionName(collection); - console.log('doUpdateEntry : updating...', collectionName); + console.log('updating data...', collectionName); return db .collection(collectionName) @@ -62,7 +62,7 @@ export const doUpdateEntry = ( export const doGetUsers = (collection, userList, callback) => { const collectionName = getFirebaseCollectionName(collection); - console.log('doGetUsers : getting...', collectionName); + console.log('getting data...', collectionName); const updatedUserList = userList; return db From b37c8f53d308ae34842cc1a6d6d3f7d477cb77d2 Mon Sep 17 00:00:00 2001 From: Mike Mathew Date: Thu, 19 Jan 2023 18:12:50 -0600 Subject: [PATCH 08/13] refactor(members): correctly show loading spinner when loading members --- src/firebase/db.js | 1 + src/pages/members/index.js | 21 ++++++++++++++++----- src/pages/members/member-content.js | 29 +++++++++-------------------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/firebase/db.js b/src/firebase/db.js index 40c72bef..d805229f 100644 --- a/src/firebase/db.js +++ b/src/firebase/db.js @@ -65,6 +65,7 @@ export const doGetUsers = (collection, userList, callback) => { console.log('getting data...', collectionName); const updatedUserList = userList; + return db .collection(collectionName) .get() diff --git a/src/pages/members/index.js b/src/pages/members/index.js index d2f16fa3..207b7286 100644 --- a/src/pages/members/index.js +++ b/src/pages/members/index.js @@ -2,7 +2,7 @@ import { Helmet } from 'react-helmet'; import { graphql } from 'gatsby'; import PropTypes from 'prop-types'; -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import styled from 'styled-components'; // Internal Dependencies @@ -43,19 +43,31 @@ const MembersHome = ({ data, }) => { const [userData, setUserData] = useState(null); + const [isLoading, setIsLoading] = useState(true); const [shouldRefetchUserList, setShouldRefetchUserList] = useState(false); + const handleUpdateUserData = useCallback((updatedUserData) => { + setUserData(updatedUserData); + setIsLoading(false); + }, []); + useEffect(() => { const userList = []; if (authUser || shouldRefetchUserList) { - doGetUsers('registration', userList, setUserData); + doGetUsers('registration', userList, handleUpdateUserData); if (shouldRefetchUserList) { setShouldRefetchUserList(false); } } - }, [authUser, shouldRefetchUserList, setShouldRefetchUserList]); + }, [ + authUser, + setUserData, + shouldRefetchUserList, + setShouldRefetchUserList, + handleUpdateUserData, + ]); const isAuthenticated = Boolean(authUser); @@ -78,6 +90,7 @@ const MembersHome = ({ data.allContentfulFileShareDescriptionTextNode.edges } currentMemberList={userData} + isLoading={isLoading} memberEmail={authUser.email} setShouldRefetchUserList={setShouldRefetchUserList} /> @@ -111,8 +124,6 @@ Members.propTypes = { const MembersWithContext = (props) => ( {(authUser) => { - console.log('MembersWithContext : authUser', authUser); - return ( { - const [isLoadingUserData, setIsLoadingUserData] = useState(true); const [currentMemberData, setCurrentMemberData] = useState(null); - useEffect(() => { - if (authUser && isLoadingUserData) { - setIsLoadingUserData(!isLoadingUserData); - } - }, [authUser, isLoadingUserData]); - useEffect(() => { if (currentMemberList?.length > 0 && !currentMemberData) { const currentMember = currentMemberList.find( @@ -77,16 +71,11 @@ const MemberContent = ({ setCurrentMemberData(currentMember); } - }, [ - authUser.email, - authUser.uid, - currentMemberData, - currentMemberList, - ]); + }, [authUser.email, authUser.uid, currentMemberData, currentMemberList, isLoading]); const isRegisteredForCurrentYear = Boolean(currentMemberData); - if (isLoadingUserData) { + if (isLoading) { return ( +

{`${isAdmin ? 'Admin ' : ''}Member Dashboard`}

@@ -169,7 +158,7 @@ const MemberContent = ({ yaml={membersSidebar} /> -
+ ); }; From 4369cc96bf10bba07087f927202c51d721cffa40 Mon Sep 17 00:00:00 2001 From: Mike Mathew Date: Thu, 19 Jan 2023 23:55:56 -0600 Subject: [PATCH 09/13] refactor(members): tweak paying from the member content page --- .../register/register-member-payment.tsx | 7 +- src/firebase/db.js | 3 + src/pages/members/MemberStatus.js | 99 ++++++++++++++++--- src/pages/members/RegistrationTasks.js | 61 ++++++++---- src/pages/members/index.js | 13 ++- src/pages/members/member-content.js | 13 ++- src/pages/members/member-info.js | 20 ++-- src/pages/members/members-links.yml | 2 +- src/utils/dateHelpers.ts | 4 + src/utils/helpers.ts | 4 + 10 files changed, 164 insertions(+), 62 deletions(-) create mode 100644 src/utils/dateHelpers.ts diff --git a/src/components/register/register-member-payment.tsx b/src/components/register/register-member-payment.tsx index c35ae941..306b10c8 100644 --- a/src/components/register/register-member-payment.tsx +++ b/src/components/register/register-member-payment.tsx @@ -23,7 +23,9 @@ import { doUpdateEntry, doUpdateInvoiceId as updateFirestoreInvoiceId, doUpdateReceiptId as updateFirestoreReceiptId, + FIRESTORE_MEMBER_COLLECTION, } from '../../firebase/db'; +import { currentDate } from '../../utils/dateHelpers'; import { currentSchoolYearLong } from '../../utils/helpers'; import { HandleCompleteMemberStepType, @@ -76,11 +78,6 @@ const StyledRoot = styled.section(({ theme }) => ({ }, })); -const currentDate = format(new Date(), 'M/d/yyyy'); - -// This will tell the Firestore database action where to put the new record -const FIRESTORE_MEMBER_COLLECTION = 'registration'; - // Component Definition const RegisterMemberPayment: FC = ({ authenticatedUserId, diff --git a/src/firebase/db.js b/src/firebase/db.js index d805229f..e99a741d 100644 --- a/src/firebase/db.js +++ b/src/firebase/db.js @@ -7,6 +7,9 @@ import { logError } from '../utils/logError'; const getFirebaseCollectionName = (collection) => `${collection}_${currentSchoolYearShort}`; +// This will tell the Firestore database action where to put the new record +export const FIRESTORE_MEMBER_COLLECTION = 'registration'; + // Create/Update user entry in Firestore export const doCreateEntry = ( form, diff --git a/src/pages/members/MemberStatus.js b/src/pages/members/MemberStatus.js index afe01ae9..264957ee 100644 --- a/src/pages/members/MemberStatus.js +++ b/src/pages/members/MemberStatus.js @@ -1,17 +1,28 @@ // External Dependencies +// import { navigate } from 'gatsby'; +import Box from '@mui/material/Box'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemText from '@mui/material/ListItemText'; import Typography from '@mui/material/Typography'; import PropTypes from 'prop-types'; -import React from 'react'; +import React, { useCallback } from 'react'; import styled from 'styled-components'; // Internal Dependencies -import { currentSchoolYearLong } from '../../utils/helpers'; +import { + doUpdateEntry, + FIRESTORE_MEMBER_COLLECTION, +} from '../../firebase/db'; +import { currentDate } from '../../utils/dateHelpers'; +import { + currentSchoolYearEnding, + currentSchoolYearLong, +} from '../../utils/helpers'; import { options } from '../../utils/typography'; import Card from '../../components/shared/cards/card'; import CardSubtitle from '../../components/shared/CardSubtitle'; +import EnhancedAlert from '../../components/shared/EnhancedAlert'; import PaypalButtonWrapper from '../../components/register/paypal/paypal-button-wrapper'; import PrintInvoiceUI from './PrintInvoiceUI'; import presets from '../../utils/presets'; @@ -40,6 +51,9 @@ const defaultProps = { }; const StyledRoot = styled(Card)(({ theme }) => ({ + '.balanceText': { + marginTop: theme.spacing(3), + }, '.contentText': { marginBottom: theme.spacing(2), }, @@ -109,6 +123,33 @@ const MemberStatus = ({ const amountToPay = currentMemberData?.MemberType === 'Active' ? 50.00 : 30.00; + const handleSuccessfulPayment = useCallback((payment) => { + const updatedMemberData = { + ...currentMemberData, + AmountPaid: currentMemberData?.MemberType === 'Active' ? 50 : 30, + PaypalPayerID: payment?.payerID, + PaypalPaymentID: payment?.paymentID, + PaymentOption: payment?.paymentID ? 'Paypal' : 'Invoiced', + invoiceDate: currentDate, + invoiceId: currentMemberData.invoiceId, + receiptDate: currentMemberData.receiptId ? currentDate : '', + receiptId: currentMemberData.receiptId, + }; + + // Update the member's payment data in the Firestore database + // This shape should be the same as register-membmer-payment + // in the handleCompleteMemberPaymentStep function + doUpdateEntry( + updatedMemberData, + FIRESTORE_MEMBER_COLLECTION, + currentMemberData?.userId, + ); + + // Instead of trying to update all of the data, + // it's easier to reload the Members page + window.location.reload(); + }, [currentMemberData]); + return ( Membership status @@ -132,25 +173,51 @@ const MemberStatus = ({ )} )} - secondary={`for the ${currentSchoolYearLong} school year`} + secondary={( + <> + for the {currentSchoolYearLong} school year + {!needsToPay && ( + <> +
+ through 6/30/{currentSchoolYearEnding} + + )} + + )} /> - {!isRegisteredForCurrentYear && isInvoiced && ( - <> - - Outstanding balance: + {needsToPay && ( + + + To become an active member, please {' '} - - {currentMemberData?.MemberType === 'Active' ? '$50.00' : '$30.00'} - - + {!isRegisteredForCurrentYear && 'register and '} + {' '} + pay + dues for this school year. + + + )} + + Outstanding balance: + {' '} + + {!isRegisteredForCurrentYear && '$50.00'} + {needsToPay && currentMemberData?.MemberType === 'Active' && '$50.00'} + {currentMemberData?.MemberType === 'Retired' && '$30.00'} + {!needsToPay && '$0.00'} + + + + {isRegisteredForCurrentYear && isInvoiced && ( + <> console.log('you did it!')} + onSuccessfulPayment={handleSuccessfulPayment} />
diff --git a/src/pages/members/RegistrationTasks.js b/src/pages/members/RegistrationTasks.js index 7e978c6c..97459ecd 100644 --- a/src/pages/members/RegistrationTasks.js +++ b/src/pages/members/RegistrationTasks.js @@ -8,6 +8,7 @@ import React, { useRef } from 'react'; import ReactToPrint from 'react-to-print'; import styled, { useTheme } from 'styled-components'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import ErrorIcon from '@mui/icons-material/Error'; // Internal Dependencies import { currentSchoolYearLong } from '../../utils/helpers'; @@ -20,7 +21,7 @@ import RegisterButton from '../../components/register/register-button'; // Local Variables const propTypes = { - currentUser: PropTypes.shape({ + currentMemberData: PropTypes.shape({ Address1: PropTypes.string, Address2: PropTypes.string, AmountPaid: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), @@ -42,14 +43,14 @@ const propTypes = { }; const defaultProps = { - currentUser: null, + currentMemberData: null, }; const StyledRoot = styled(Card)(({ theme }) => ({ '.buttonContainer': { marginTop: theme.spacing(2), }, - '.checkCircleIcon': { + '.listItemIcon': { height: 20, marginLeft: theme.spacing(1), transform: 'translateY(4px)', @@ -82,13 +83,31 @@ const StyledRoot = styled(Card)(({ theme }) => ({ // Component Definition const RegistrationTasks = ({ - currentUser, + currentMemberData, isRegisteredForCurrentYear, }) => { const theme = useTheme(); const printReceiptRef = useRef(); - const receiptInfo = currentUser && ( + const needsToPay = !currentMemberData?.AmountPaid; + + const successIconElement = ( + + ); + + const warningIconElement = ( + + ); + + const receiptInfo = currentMemberData && (

Thank you for joining TMAC for the {currentSchoolYearLong} school year! @@ -109,18 +128,18 @@ const RegistrationTasks = ({

); - const isPaypal = currentUser?.PaymentOption.toLowerCase() === 'paypal'; + const isPaypal = currentMemberData?.PaymentOption.toLowerCase() === 'paypal'; return ( @@ -138,13 +157,7 @@ const RegistrationTasks = ({ primary={( <> Register - {isRegisteredForCurrentYear && ( - - )} + {isRegisteredForCurrentYear && successIconElement} )} secondary={ @@ -172,8 +185,18 @@ const RegistrationTasks = ({ primary: 'listItemText', secondary: 'listItemSecondaryText', }} - primary="Pay Membership Dues" - secondary="Pay online using credit card or send payment with invoice." + primary={( + <> + Pay Membership Dues + {isRegisteredForCurrentYear && !needsToPay && successIconElement} + {isRegisteredForCurrentYear && needsToPay && warningIconElement} + + )} + secondary={ + isRegisteredForCurrentYear && !needsToPay + ? 'You have already paid your dues for the current school year.' + : 'Pay online using credit card or send payment with invoice.' +} /> diff --git a/src/pages/members/index.js b/src/pages/members/index.js index 207b7286..779fd3af 100644 --- a/src/pages/members/index.js +++ b/src/pages/members/index.js @@ -46,12 +46,12 @@ const MembersHome = ({ const [isLoading, setIsLoading] = useState(true); const [shouldRefetchUserList, setShouldRefetchUserList] = useState(false); - const handleUpdateUserData = useCallback((updatedUserData) => { - setUserData(updatedUserData); - setIsLoading(false); - }, []); - useEffect(() => { + const handleUpdateUserData = (updatedUserData) => { + setUserData(updatedUserData); + setIsLoading(false); + }; + const userList = []; if (authUser || shouldRefetchUserList) { @@ -64,9 +64,8 @@ const MembersHome = ({ }, [ authUser, setUserData, - shouldRefetchUserList, setShouldRefetchUserList, - handleUpdateUserData, + shouldRefetchUserList, ]); const isAuthenticated = Boolean(authUser); diff --git a/src/pages/members/member-content.js b/src/pages/members/member-content.js index cb4feb6f..e2f6de7e 100644 --- a/src/pages/members/member-content.js +++ b/src/pages/members/member-content.js @@ -71,7 +71,13 @@ const MemberContent = ({ setCurrentMemberData(currentMember); } - }, [authUser.email, authUser.uid, currentMemberData, currentMemberList, isLoading]); + }, [ + authUser.email, + authUser.uid, + currentMemberData, + currentMemberList, + isLoading, + ]); const isRegisteredForCurrentYear = Boolean(currentMemberData); @@ -108,14 +114,13 @@ const MemberContent = ({ {currentMemberData && ( )} diff --git a/src/pages/members/member-info.js b/src/pages/members/member-info.js index d8fe28cf..65465c19 100644 --- a/src/pages/members/member-info.js +++ b/src/pages/members/member-info.js @@ -13,7 +13,7 @@ import presets from '../../utils/presets'; // Local Variables const propTypes = { - currentUser: PropTypes.shape({ + currentMemberData: PropTypes.shape({ Address1: PropTypes.string, Address2: PropTypes.string, CellPhone: PropTypes.string, @@ -67,7 +67,7 @@ const StyledRoot = styled(Card)(({ theme }) => ({ })); // Component Definition -const MemberInfo = ({ currentUser }) => { +const MemberInfo = ({ currentMemberData }) => { return ( Member Info @@ -77,20 +77,20 @@ const MemberInfo = ({ currentUser }) => { -
{currentUser.FirstName} {currentUser.LastName}
-
{currentUser.Title}, {currentUser.District}
+
{currentMemberData.FirstName} {currentMemberData.LastName}
+
{currentMemberData.Title}, {currentMemberData.District}
)} secondary={(
-
{currentUser.Address1}
-
{currentUser.Address2}
+
{currentMemberData.Address1}
+
{currentMemberData.Address2}
- {currentUser.City}, {currentUser.State} {currentUser.ZipCode} + {currentMemberData.City}, {currentMemberData.State} {currentMemberData.ZipCode}
-
Office: {currentUser.OfficePhone}
-
Cell: {currentUser.CellPhone}
-
{currentUser.Email}
+
Office: {currentMemberData.OfficePhone}
+
Cell: {currentMemberData.CellPhone}
+
{currentMemberData.Email}
)} secondaryTypographyProps={{ diff --git a/src/pages/members/members-links.yml b/src/pages/members/members-links.yml index f95163b9..1d8eb513 100644 --- a/src/pages/members/members-links.yml +++ b/src/pages/members/members-links.yml @@ -4,5 +4,5 @@ link: /members/ - title: Member List link: /members/member-list/ - - title: Join TMAC + - title: Register for 2022-2023 link: /members/join/ diff --git a/src/utils/dateHelpers.ts b/src/utils/dateHelpers.ts new file mode 100644 index 00000000..e19ccf08 --- /dev/null +++ b/src/utils/dateHelpers.ts @@ -0,0 +1,4 @@ +// External Dependencies +import format from 'date-fns/format'; + +export const currentDate = format(new Date(), 'M/d/yyyy'); diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index a975c3d0..34c59711 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -54,3 +54,7 @@ export const currentSchoolYearShort = isTodayAfterJune30th export const currentSchoolYearLong = isTodayAfterJune30th ? `${currentYearLong}-${Number(currentYearLong) + 1}` : `${Number(currentYearLong) - 1}-${currentYearLong}`; + +export const currentSchoolYearEnding = isTodayAfterJune30th + ? `${Number(currentYearLong) + 1}` + : currentYearLong; From 393c5202a19755341d862b97155c06b85e00c447 Mon Sep 17 00:00:00 2001 From: Mike Mathew Date: Fri, 20 Jan 2023 00:02:06 -0600 Subject: [PATCH 10/13] refactor(members): add "Paid in full" chip --- src/pages/members/MemberStatus.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/pages/members/MemberStatus.js b/src/pages/members/MemberStatus.js index 264957ee..a158f311 100644 --- a/src/pages/members/MemberStatus.js +++ b/src/pages/members/MemberStatus.js @@ -1,6 +1,6 @@ // External Dependencies -// import { navigate } from 'gatsby'; import Box from '@mui/material/Box'; +import Chip from '@mui/material/Chip'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemText from '@mui/material/ListItemText'; @@ -53,6 +53,8 @@ const defaultProps = { const StyledRoot = styled(Card)(({ theme }) => ({ '.balanceText': { marginTop: theme.spacing(3), + display: 'flex', + alignItems: 'center', }, '.contentText': { marginBottom: theme.spacing(2), @@ -109,6 +111,7 @@ const StyledStrong = styled.strong(({ theme }) => ({ fontSize: '1.1rem', }, fontSize: '1.2rem', + margin: theme.spacing(0, 1), whiteSpace: 'pre', })); @@ -207,13 +210,22 @@ const MemberStatus = ({ variant="body2" > Outstanding balance: - {' '} {!isRegisteredForCurrentYear && '$50.00'} {needsToPay && currentMemberData?.MemberType === 'Active' && '$50.00'} {currentMemberData?.MemberType === 'Retired' && '$30.00'} {!needsToPay && '$0.00'} + {!needsToPay && ( + <> + {' '} + + + )} {isRegisteredForCurrentYear && isInvoiced && ( From 8993e7b18ec83806dbc78b3e5cbc43837a51593e Mon Sep 17 00:00:00 2001 From: Mike Mathew Date: Fri, 20 Jan 2023 00:03:33 -0600 Subject: [PATCH 11/13] chore: bump version to 2.17.0 --- package.json | 2 +- src/pages/members/RegistrationTasks.js | 4 ++-- src/pages/members/member-list.js | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index eda7f365..45409f0f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "tmac-website", "description": "Website for the Texas Music Administrators Conference", - "version": "2.16.0", + "version": "2.17.0", "repository": { "type": "git", "url": "https://github.com/m2mathew/tmac-website" diff --git a/src/pages/members/RegistrationTasks.js b/src/pages/members/RegistrationTasks.js index 97459ecd..85785f29 100644 --- a/src/pages/members/RegistrationTasks.js +++ b/src/pages/members/RegistrationTasks.js @@ -164,7 +164,7 @@ const RegistrationTasks = ({ isRegisteredForCurrentYear ? 'You are registered — thanks for being a member!' : 'Become a member for the current school year.' -} + } /> {!isRegisteredForCurrentYear && ( @@ -196,7 +196,7 @@ const RegistrationTasks = ({ isRegisteredForCurrentYear && !needsToPay ? 'You have already paid your dues for the current school year.' : 'Pay online using credit card or send payment with invoice.' -} + } /> diff --git a/src/pages/members/member-list.js b/src/pages/members/member-list.js index 4a4f5982..33ef35a3 100644 --- a/src/pages/members/member-list.js +++ b/src/pages/members/member-list.js @@ -50,8 +50,6 @@ const MemberListContent = ({ }) => { const [userData, setUserData] = useState([]); - console.log('MemberListContent : userData', userData); - const handleUpdateUserList = (userList) => { setUserData(userList); }; From cb30d422924cf75ceb484a0d3c30efa7112e06f7 Mon Sep 17 00:00:00 2001 From: Mike Mathew Date: Fri, 20 Jan 2023 00:17:09 -0600 Subject: [PATCH 12/13] fix(members): only reload the window if the window object is available --- src/pages/members/MemberStatus.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/members/MemberStatus.js b/src/pages/members/MemberStatus.js index a158f311..d4bf4859 100644 --- a/src/pages/members/MemberStatus.js +++ b/src/pages/members/MemberStatus.js @@ -150,7 +150,9 @@ const MemberStatus = ({ // Instead of trying to update all of the data, // it's easier to reload the Members page - window.location.reload(); + if (window !== 'undefined') { + window.location.reload(); + } }, [currentMemberData]); return ( From 4126fbeba8fbff7d97871909b95b154a807effd2 Mon Sep 17 00:00:00 2001 From: Mike Mathew Date: Fri, 20 Jan 2023 00:21:36 -0600 Subject: [PATCH 13/13] fix(members): update some potential null pointer exceptions --- src/pages/members/MemberStatus.js | 2 +- src/pages/members/member-content.js | 10 ++-------- src/pages/members/member-info.js | 4 ++++ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/pages/members/MemberStatus.js b/src/pages/members/MemberStatus.js index d4bf4859..fd54e723 100644 --- a/src/pages/members/MemberStatus.js +++ b/src/pages/members/MemberStatus.js @@ -150,7 +150,7 @@ const MemberStatus = ({ // Instead of trying to update all of the data, // it's easier to reload the Members page - if (window !== 'undefined') { + if (typeof window !== 'undefined') { window.location.reload(); } }, [currentMemberData]); diff --git a/src/pages/members/member-content.js b/src/pages/members/member-content.js index e2f6de7e..9a05944d 100644 --- a/src/pages/members/member-content.js +++ b/src/pages/members/member-content.js @@ -61,7 +61,7 @@ const MemberContent = ({ const [currentMemberData, setCurrentMemberData] = useState(null); useEffect(() => { - if (currentMemberList?.length > 0 && !currentMemberData) { + if (authUser && currentMemberList?.length > 0 && !currentMemberData) { const currentMember = currentMemberList.find( // We used to use authUser.uid as the unique key in the Firestore // Now we use authUser.email @@ -71,13 +71,7 @@ const MemberContent = ({ setCurrentMemberData(currentMember); } - }, [ - authUser.email, - authUser.uid, - currentMemberData, - currentMemberList, - isLoading, - ]); + }, [authUser, currentMemberData, currentMemberList, isLoading]); const isRegisteredForCurrentYear = Boolean(currentMemberData); diff --git a/src/pages/members/member-info.js b/src/pages/members/member-info.js index 65465c19..3224ba5a 100644 --- a/src/pages/members/member-info.js +++ b/src/pages/members/member-info.js @@ -68,6 +68,10 @@ const StyledRoot = styled(Card)(({ theme }) => ({ // Component Definition const MemberInfo = ({ currentMemberData }) => { + if (!currentMemberData) { + return null; + } + return ( Member Info