-
-
Loading WeConnect...
+
+
Loading WeConnect...
Thank you for being part of WeVote!
diff --git a/src/js/common/components/Search/SearchBar2024.jsx b/src/js/common/components/Search/SearchBar2024.jsx
new file mode 100644
index 0000000..e6313ef
--- /dev/null
+++ b/src/js/common/components/Search/SearchBar2024.jsx
@@ -0,0 +1,114 @@
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
+import styled from 'styled-components';
+import { blurTextFieldAndroid, focusTextFieldAndroid } from '../../utils/cordovaUtils';
+import { renderLog } from '../../utils/logging';
+import SearchBase from './SearchBase';
+
+/* eslint-disable jsx-a11y/control-has-associated-label */
+class SearchBar2024 extends Component {
+ constructor (props) {
+ super(props);
+
+ this.state = {
+ searchString: '',
+ };
+
+ this.handleKeyPress = this.handleKeyPress.bind(this);
+ this.updateResults = this.updateResults.bind(this);
+ this.clearQuery = this.clearQuery.bind(this);
+ }
+
+ componentDidMount () {
+ if (this.props.clearSearchTextNow) {
+ if (this.props.clearFunction) {
+ this.props.clearFunction();
+ }
+ const { searchString } = this.state;
+ if (searchString) {
+ this.setState({
+ searchString: '',
+ });
+ }
+ }
+ }
+
+ componentDidUpdate (prevProps) {
+ if (this.props.clearSearchTextNow !== prevProps.clearSearchTextNow) {
+ if (this.props.clearSearchTextNow) {
+ if (this.props.clearFunction) {
+ this.props.clearFunction();
+ }
+ const { searchString } = this.state;
+ if (searchString) {
+ this.setState({
+ searchString: '',
+ });
+ }
+ }
+ }
+ }
+
+ componentWillUnmount () {
+ if (this.timer) {
+ clearTimeout(this.timer);
+ this.timer = null;
+ }
+ }
+
+ handleKeyPress () {
+ if (this.timer) clearTimeout(this.timer);
+ this.timer = setTimeout(() => {
+ this.props.searchFunction(this.state.searchString);
+ }, this.props.searchUpdateDelayTime);
+ }
+
+ clearQuery () {
+ this.props.clearFunction();
+ this.setState({ searchString: '' });
+ }
+
+ updateResults (event) {
+ const searchString = event.target.value;
+ this.setState({
+ searchString,
+ });
+ }
+
+ // check limit of 50 characters
+ render () {
+ renderLog('SearchBar2024'); // Set LOG_RENDER_EVENTS to log all renders
+ const { placeholder } = this.props;
+ const { searchString } = this.state;
+ return (
+
+ focusTextFieldAndroid('SearchBar2024')}
+ onBlur={blurTextFieldAndroid}
+ onClear={this.clearQuery}
+ />
+
+ );
+ }
+}
+SearchBar2024.propTypes = {
+ clearFunction: PropTypes.func.isRequired,
+ clearSearchTextNow: PropTypes.bool,
+ placeholder: PropTypes.string,
+ searchFunction: PropTypes.func.isRequired,
+ searchUpdateDelayTime: PropTypes.number.isRequired,
+};
+
+const SearchBar2024Wrapper = styled('div')`
+ width: 100%;
+ overflow: hidden;
+ position: relative;
+ padding: 4px;
+`;
+
+export default SearchBar2024;
diff --git a/src/js/common/components/Search/SearchBase.jsx b/src/js/common/components/Search/SearchBase.jsx
new file mode 100644
index 0000000..422fc46
--- /dev/null
+++ b/src/js/common/components/Search/SearchBase.jsx
@@ -0,0 +1,120 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import styled from 'styled-components';
+import colors from '../Style/Colors';
+import normalizedImagePath from '../../utils/normalizedImagePath';
+
+const closeIcon = normalizedImagePath('../../../img/global/icons/cross.svg');
+const searchIcon = normalizedImagePath('../../../img/global/icons/search.svg');
+
+class SearchBase extends React.Component {
+ constructor (props) {
+ super(props);
+ this.state = { searchText: '' };
+ }
+
+ handleInputChange = (event) => {
+ this.setState({ searchText: event.target.value }, () => {
+ if (this.props.onChange) {
+ this.props.onChange(event);
+ }
+ if (this.props.onKeyDown) {
+ this.props.onKeyDown(event);
+ }
+ if (this.props.onFocus) {
+ this.props.onFocus(event);
+ }
+ });
+ }
+
+ handleClear = () => {
+ this.setState({ searchText: '' }, () => {
+ if (this.props.onClear) {
+ this.props.onClear();
+ }
+ });
+ }
+
+ render () {
+ return (
+
+ {!this.state.searchText && }
+
+ {this.state.searchText && }
+
+ );
+ }
+}
+SearchBase.propTypes = {
+ placeholder: PropTypes.string,
+ onChange: PropTypes.func,
+ onKeyDown: PropTypes.func,
+ onFocus: PropTypes.func,
+ onBlur: PropTypes.func,
+ onClear: PropTypes.func,
+};
+
+const SearchBaseWrapper = styled('div')`
+ position: relative;
+ display: inline-block;
+ width: 100%;
+`;
+
+const SearchIcon = styled('div')`
+ position: absolute;
+ top: 50%;
+ right: 10px;
+ transform: translateY(-50%);
+ color: gray;
+ background-image: url(${searchIcon});
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: contain;
+ width: 24px;
+ height: 24px;
+`;
+
+const ClearButton = styled('div')`
+ position: absolute;
+ right: 12px;
+ top: 50%;
+ transform: translateY(-50%);
+ background-image: url(${closeIcon});
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: contain;
+ width: 18px;
+ height: 18px;
+ cursor: pointer;
+`;
+
+const SearchInput = styled('input')`
+ &::-webkit-search-decoration,
+ &::-webkit-search-cancel-button,
+ &::-webkit-search-results-button,
+ &::-webkit-search-results-decoration {
+ display: none;
+ }
+
+ border: 1px solid rgb(206, 212, 218);
+ height: 38px;
+ width: 100%;
+ border-radius: 0.25rem;
+ padding-right: 40px;
+ padding-left: 12px;
+
+
+ &:focus-visible {
+ border: none;
+ outline: ${colors.primary} solid 2px !important;
+ }
+`;
+
+export default SearchBase;
diff --git a/src/js/common/components/Style/Colors.js b/src/js/common/components/Style/Colors.js
new file mode 100644
index 0000000..60255b7
--- /dev/null
+++ b/src/js/common/components/Style/Colors.js
@@ -0,0 +1,17 @@
+// Please also see DesignTokenColors.js
+const colors = {
+ primary: '#0834CD',
+ primary2024: '#053C6D',
+ // primary50: '#E6F3FF', // Moved to DesignTokenColors
+ primaryHover: '#09288A',
+ secondaryHover: '#F5F7FD',
+ darkGrey: '#454F69',
+ middleGrey: '#5E5E5B',
+ grey: '#AEB2BE',
+ lightGrey: '#E5E6EA',
+ ultraLightGrey: '#FAFAFA',
+ white: '#ffffff',
+ green: '#007800',
+};
+
+export default colors;
diff --git a/src/js/components/Drawers/AddPersonDrawer.jsx b/src/js/components/Drawers/AddPersonDrawer.jsx
new file mode 100644
index 0000000..f2045af
--- /dev/null
+++ b/src/js/components/Drawers/AddPersonDrawer.jsx
@@ -0,0 +1,97 @@
+import React from 'react';
+import styled from 'styled-components';
+import PropTypes from 'prop-types';
+import { withStyles } from '@mui/styles';
+import DrawerTemplateA from './DrawerTemplateA';
+import AppObservableStore, { messageService } from '../../stores/AppObservableStore';
+import PersonStore from '../../stores/PersonStore';
+// import TeamActions from '../actions/TeamActions';
+import TeamStore from '../../stores/TeamStore';
+import SearchBar2024 from '../../common/components/Search/SearchBar2024';
+import { renderLog } from '../../common/utils/logging';
+
+
+const AddPersonDrawer = ({ classes }) => { // classes, teamId
+ renderLog('AddPersonDrawer'); // Set LOG_RENDER_EVENTS to log all renders
+ const [mainContentJsx, setMainContentJsx] = React.useState(<>>);
+ const [headerTitleJsx, setHeaderTitleJsx] = React.useState(<>>);
+ const [headerFixedJsx, setHeaderFixedJsx] = React.useState(<>>);
+ const [searchText, setSearchText] = React.useState('');
+ const [teamId, setTeamId] = React.useState(-1);
+
+ const onAppObservableStoreChange = () => {
+ setTeamId(AppObservableStore.getGlobalVariableState('addPersonDrawerTeamId'));
+ };
+
+ const onRetrieveTeamChange = () => {
+ };
+
+ const onPersonStoreChange = () => {
+ onRetrieveTeamChange();
+ };
+
+ const onTeamStoreChange = () => {
+ onRetrieveTeamChange();
+ };
+
+ const searchFunction = (incomingSearchText) => {
+ setSearchText(incomingSearchText);
+ };
+
+ const clearFunction = () => {
+ setSearchText('');
+ }
+
+ React.useEffect(() => {
+ const appStateSubscription = messageService.getMessage().subscribe(() => onAppObservableStoreChange());
+ onAppObservableStoreChange();
+ const personStoreListener = PersonStore.addListener(onPersonStoreChange);
+ onPersonStoreChange();
+ const teamStoreListener = TeamStore.addListener(onTeamStoreChange);
+ onTeamStoreChange();
+
+ setHeaderTitleJsx(<>Add Team Member>);
+ setMainContentJsx(
+
+
+
+
+
+ );
+
+ return () => {
+ appStateSubscription.unsubscribe();
+ personStoreListener.remove();
+ teamStoreListener.remove();
+ };
+ }, []);
+
+ return (
+
+ );
+};
+AddPersonDrawer.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+const styles = () => ({
+});
+
+const AddPersonDrawerWrapper = styled('div')`
+`;
+
+const SearchBarWrapper = styled('div')`
+ margin-bottom: 16px;
+`;
+
+export default withStyles(styles)(AddPersonDrawer);
diff --git a/src/js/components/Drawers/DrawerTemplateA.jsx b/src/js/components/Drawers/DrawerTemplateA.jsx
new file mode 100644
index 0000000..6dc711b
--- /dev/null
+++ b/src/js/components/Drawers/DrawerTemplateA.jsx
@@ -0,0 +1,190 @@
+import { Close } from '@mui/icons-material'; // Info
+import { Drawer, IconButton } from '@mui/material';
+import React from 'react';
+import styled from 'styled-components';
+import PropTypes from 'prop-types';
+import { withStyles } from '@mui/styles';
+import AppObservableStore, { messageService } from '../../stores/AppObservableStore';
+import { DrawerHeaderAnimateDownInnerContainer, DrawerHeaderAnimateDownOuterContainer, DrawerTitle, DrawerHeaderWrapper } from '../Style/drawerLayoutStyles';
+import { cordovaDrawerTopMargin } from '../../utils/cordovaOffsets';
+import { hasIPhoneNotch } from '../../common/utils/cordovaUtils';
+import { renderLog } from '../../common/utils/logging';
+
+
+const DrawerTemplateA = ({ classes, drawerOpenGlobalVariableName, headerFixedJsx, headerTitleJsx, mainContentJsx }) => { // classes, teamId
+ renderLog('DrawerTemplateA'); // Set LOG_RENDER_EVENTS to log all renders
+ const [drawerOpen, setDrawerOpen] = React.useState(false);
+ const [scrolledDown, setScrolledDown] = React.useState(false);
+
+ const handleScrolledDownDrawer = (evt) => {
+ const { scrollTop } = evt.target;
+ if (scrollTop > 200 && !AppObservableStore.getScrolledDownDrawer()) {
+ AppObservableStore.setScrolledDownDrawer(true);
+ }
+ if (scrollTop < 200 && AppObservableStore.getScrolledDownDrawer()) {
+ AppObservableStore.setScrolledDownDrawer(false);
+ }
+ };
+
+ const onAppObservableStoreChange = () => {
+ setScrolledDown(AppObservableStore.getScrolledDownDrawer());
+ setDrawerOpen(AppObservableStore.getGlobalVariableState(drawerOpenGlobalVariableName));
+ };
+
+ React.useEffect(() => {
+ const appStateSubscription = messageService.getMessage().subscribe(() => onAppObservableStoreChange());
+ onAppObservableStoreChange();
+
+ setTimeout(() => {
+ const drawer = document.querySelector('.MuiDrawer-paper');
+ if (drawer) {
+ drawer.addEventListener('scroll', handleScrolledDownDrawer);
+ } else {
+ console.log('Drawer element NOT found make timeout longer.');
+ }
+ }, 100);
+
+ return () => {
+ appStateSubscription.unsubscribe();
+ };
+ }, []);
+
+ return (
+
AppObservableStore.setGlobalVariableState(drawerOpenGlobalVariableName, false)}
+ open={drawerOpen}
+ >
+
+
+ {headerTitleJsx}
+
+
+ AppObservableStore.setGlobalVariableState(drawerOpenGlobalVariableName, false)}
+ size="large"
+ >
+
+
+
+
+
+
+
+
+ {mainContentJsx}
+
+
+ );
+};
+DrawerTemplateA.propTypes = {
+ classes: PropTypes.object.isRequired,
+ drawerOpenGlobalVariableName: PropTypes.string,
+ mainContentJsx: PropTypes.object,
+ headerTitleJsx: PropTypes.object,
+ headerFixedJsx: PropTypes.object,
+};
+
+const styles = () => ({
+ drawer: {
+ marginTop: cordovaDrawerTopMargin(),
+ maxWidth: '550px !important',
+ '& *': {
+ maxWidth: '550px !important',
+ },
+ '@media(max-width: 576px)': {
+ maxWidth: '360px !important',
+ '& *': {
+ maxWidth: '360px !important',
+ },
+ },
+ },
+ dialogPaper: {
+ display: 'block',
+ marginTop: hasIPhoneNotch() ? 68 : 48,
+ minWidth: '100%',
+ maxWidth: '100%',
+ width: '100%',
+ minHeight: '100%',
+ maxHeight: '100%',
+ height: '100%',
+ margin: '0 auto',
+ '@media (min-width: 577px)': {
+ maxWidth: '550px',
+ width: '90%',
+ height: 'fit-content',
+ margin: '0 auto',
+ minWidth: 0,
+ minHeight: 0,
+ transitionDuration: '.25s',
+ },
+ '@media (max-width: 576px)': {
+ maxWidth: '360px',
+ },
+ },
+ dialogContent: {
+ padding: '24px 24px 36px 24px',
+ background: 'white',
+ height: 'fit-content',
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ justifyContent: 'center',
+ '@media(max-width: 576px)': {
+ justifyContent: 'flex-start !important',
+ },
+ },
+ backButton: {
+ // marginBottom: 6,
+ // marginLeft: -8,
+ paddingTop: 0,
+ paddingBottom: 0,
+ },
+ backButtonIcon: {
+ fontSize: 14,
+ fontWeight: 'bold',
+ },
+ closeButton: {
+ marginRight: 'auto',
+ padding: 6,
+ },
+ closeButtonAbsolute: {
+ position: 'absolute',
+ right: 14,
+ top: 14,
+ },
+ closeIcon: {
+ color: '#999',
+ width: 24,
+ height: 24,
+ },
+ informationIcon: {
+ color: '#999',
+ width: 16,
+ height: 16,
+ marginTop: '-3px',
+ marginRight: 4,
+ },
+});
+
+const DrawerTemplateAWrapper = styled('div')`
+ margin: 0 15px;
+ min-width: 300px;
+`;
+
+const CloseDrawerIconWrapper = styled('div')`
+ display: flex;
+ justify-content: flex-end;
+`;
+
+export default withStyles(styles)(DrawerTemplateA);
diff --git a/src/js/components/Drawers/Drawers.jsx b/src/js/components/Drawers/Drawers.jsx
new file mode 100644
index 0000000..130aab1
--- /dev/null
+++ b/src/js/components/Drawers/Drawers.jsx
@@ -0,0 +1,44 @@
+import React, { Suspense } from 'react';
+import styled from 'styled-components';
+import PropTypes from 'prop-types';
+import { withStyles } from '@mui/styles';
+import AddPersonDrawer from './AddPersonDrawer';
+import { messageService } from '../../stores/AppObservableStore';
+import { renderLog } from '../../common/utils/logging';
+
+
+const Drawers = () => { // classes, teamId
+ renderLog('Drawers'); // Set LOG_RENDER_EVENTS to log all renders
+
+ const onAppObservableStoreChange = () => {
+ };
+
+ React.useEffect(() => {
+ const appStateSubscription = messageService.getMessage().subscribe(() => onAppObservableStoreChange());
+ onAppObservableStoreChange();
+
+ return () => {
+ appStateSubscription.unsubscribe();
+ };
+ }, []);
+
+ return (
+
>}>
+
+
+ );
+};
+Drawers.propTypes = {
+ match: PropTypes.object,
+};
+
+const styles = () => ({
+});
+
+const SearchBarWrapper = styled('div')`
+ // margin-top: 14px;
+ // margin-bottom: 8px;
+ width: 100%;
+`;
+
+export default withStyles(styles)(Drawers);
diff --git a/src/js/components/Navigation/Footer.jsx b/src/js/components/Navigation/Footer.jsx
index 32d2db9..e7ad128 100644
--- a/src/js/components/Navigation/Footer.jsx
+++ b/src/js/components/Navigation/Footer.jsx
@@ -3,6 +3,7 @@ import React, { Component, Suspense } from 'react';
import styled from 'styled-components';
import { normalizedHref } from '../../common/utils/hrefUtils';
import { isWebApp } from '../../common/utils/isCordovaOrWebApp';
+import { renderLog } from '../../common/utils/logging';
import { handleResize } from '../../common/utils/isMobileScreenSize';
import AppObservableStore, { messageService } from '../../stores/AppObservableStore';
import { getApplicationViewBooleans } from '../../utils/applicationUtils';
@@ -122,6 +123,7 @@ class Footer extends Component {
}
render () {
+ renderLog('Footer'); // Set LOG_RENDER_EVENTS to log all renders
const { /* doShowHeader, doShowFooter, */ showFooterBar, showFooterMain } = this.state;
// console.log('Footer render showFooterMain:', showFooterMain);
return (
diff --git a/src/js/components/Style/drawerLayoutStyles.js b/src/js/components/Style/drawerLayoutStyles.js
new file mode 100644
index 0000000..1a1b3d6
--- /dev/null
+++ b/src/js/components/Style/drawerLayoutStyles.js
@@ -0,0 +1,69 @@
+import styled, { css, keyframes } from 'styled-components';
+import standardBoxShadow from '../../common/components/Style/standardBoxShadow';
+import { cordovaBallotFilterTopMargin } from '../../utils/cordovaOffsets';
+
+const slideIn = keyframes`
+ from {
+ transform: translateY(-100%);
+ }
+ to {
+ transform: translateY(0);
+ }
+`;
+
+export const DrawerHeaderAnimateDownOuterContainer = styled.div.attrs(({ scrolledDown }) => ({
+ style: {
+ display: scrolledDown ? 'block' : 'hidden',
+ },
+}))`
+ width: 100%;
+ background-color: #fff;
+ overflow: hidden;
+ position: fixed;
+ z-index: 9000;
+ right: 0;
+ transform: translateY(${({ scrolledDown }) => (scrolledDown ? '0' : '-100%')});
+ transition: transform 0.3s ease-in-out;
+ visibility: ${({ scrolledDown }) => (scrolledDown ? 'visible' : 'hidden')};
+ opacity: ${({ scrolledDown }) => (scrolledDown ? 1 : 0)};
+
+ ${({ scrolledDown }) => scrolledDown &&
+ css`
+ animation: ${slideIn} 0.3s ease-out;
+ border-bottom: 1px solid #aaa;
+ box-shadow: ${standardBoxShadow('wide')};
+ `}
+`;
+export const DrawerHeaderAnimateDownInnerContainer = styled('div')`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ padding: 10px;
+`;
+
+export const DrawerHeaderContentContainer = styled('div')(({ theme }) => (`
+ margin: ${() => cordovaBallotFilterTopMargin()} auto 0 auto;
+ position: relative;
+ max-width: 960px;
+ width: 100%;
+ z-index: 0;
+ ${theme.breakpoints.down('sm')} {
+ min-height: 10px;
+ //margin: 0 10px;
+ }
+`));
+
+export const DrawerTitle = styled('div')`
+ font-weight: bold;
+ margin: 0;
+ text-align: left;
+ padding-left: 16px;
+`;
+
+export const DrawerHeaderWrapper = styled('div')`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-right: 0;
+ min-height: 28px;
+`;
diff --git a/src/js/components/Style/muiStyleOverrides.js b/src/js/components/Style/muiStyleOverrides.js
index 6c94e29..90ef424 100644
--- a/src/js/components/Style/muiStyleOverrides.js
+++ b/src/js/components/Style/muiStyleOverrides.js
@@ -40,6 +40,16 @@ const muiStyleOverrides = {
},
},
},
+ MuiDrawer: {
+ styleOverrides: {
+ root: {
+ fontFamily: '"Poppins", "Helvetica Neue Light", "Helvetica Neue", "Helvetica", "Arial", sans-serif',
+ fontSize: 16,
+ outline: 'none !important',
+ textTransform: 'none',
+ },
+ },
+ },
MuiFormControlLabel: {
styleOverrides: {
root: {
diff --git a/src/js/pages/PageNotFound.jsx b/src/js/pages/PageNotFound.jsx
index 6122dee..58146bc 100644
--- a/src/js/pages/PageNotFound.jsx
+++ b/src/js/pages/PageNotFound.jsx
@@ -51,6 +51,18 @@ PageNotFound.propTypes = {
classes: PropTypes.object,
};
+const styles = (theme) => ({
+ ballotButtonIconRoot: {
+ marginRight: 8,
+ },
+ ballotButtonRoot: {
+ width: 250,
+ [theme.breakpoints.down('md')]: {
+ width: '100%',
+ },
+ },
+});
+
const Wrapper = styled('div')(({ theme }) => (`
${theme.breakpoints.down('md')} {
margin: 1em 0;
@@ -73,16 +85,4 @@ const EmptyBallotText = styled('p')(({ theme }) => (`
}
`));
-const styles = (theme) => ({
- ballotButtonIconRoot: {
- marginRight: 8,
- },
- ballotButtonRoot: {
- width: 250,
- [theme.breakpoints.down('md')]: {
- width: '100%',
- },
- },
-});
-
export default withStyles(styles)(PageNotFound);
diff --git a/src/js/pages/TeamMembers.jsx b/src/js/pages/TeamMembers.jsx
index 8f16829..cbd5a69 100644
--- a/src/js/pages/TeamMembers.jsx
+++ b/src/js/pages/TeamMembers.jsx
@@ -1,16 +1,20 @@
+import { Button } from '@mui/material';
import React from 'react';
import { Helmet } from 'react-helmet-async';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { withStyles } from '@mui/styles';
import AppObservableStore, { messageService } from '../stores/AppObservableStore';
+import PersonStore from '../stores/PersonStore';
import TeamActions from '../actions/TeamActions';
import TeamStore from '../stores/TeamStore';
import { PageContentContainer } from '../components/Style/pageLayoutStyles';
import webAppConfig from '../config';
+import { renderLog } from '../common/utils/logging';
-const TeamMembers = ({ match }) => { // classes, teamId
+const TeamMembers = ({ classes, match }) => { // classes, teamId
+ renderLog('TeamMembers'); // Set LOG_RENDER_EVENTS to log all renders
const [teamId, setTeamId] = React.useState(-1);
const [teamMemberList, setTeamMemberList] = React.useState([]);
const [teamMemberCount, setTeamMemberCount] = React.useState(0);
@@ -18,28 +22,43 @@ const TeamMembers = ({ match }) => { // classes, teamId
const onAppObservableStoreChange = () => {
};
- const onTeamStoreChange = () => {
- const teamMemberListTemp = TeamStore.getTeamMemberList(teamId);
+ const onRetrieveTeamChange = () => {
+ const { params } = match;
+ setTeamId(params.teamId);
+ const teamMemberListTemp = TeamStore.getTeamMemberList(params.teamId);
+ // console.log('TeamMembers onRetrieveTeamChange, params.teamId:', params.teamId, ', TeamStore.getTeamMemberList:', teamMemberListTemp);
setTeamMemberList(teamMemberListTemp);
setTeamMemberCount(teamMemberListTemp.length);
};
+ const onPersonStoreChange = () => {
+ onRetrieveTeamChange();
+ };
+
+ const onTeamStoreChange = () => {
+ onRetrieveTeamChange();
+ };
+
+ const addTeamMemberClick = () => {
+ AppObservableStore.setGlobalVariableState('addPersonDrawerOpen', true);
+ AppObservableStore.setGlobalVariableState('addPersonDrawerTeamId', true);
+ };
+
React.useEffect(() => {
- const { params: {
- teamId: teamIdIncoming,
- } } = match;
- setTeamId(teamIdIncoming);
+ const { params } = match;
- console.log('Fetching team members for:', teamIdIncoming);
const appStateSubscription = messageService.getMessage().subscribe(() => onAppObservableStoreChange());
onAppObservableStoreChange();
+ const personStoreListener = PersonStore.addListener(onPersonStoreChange);
+ onPersonStoreChange();
const teamStoreListener = TeamStore.addListener(onTeamStoreChange);
onTeamStoreChange();
- TeamActions.teamRetrieve(teamIdIncoming);
+ TeamActions.teamRetrieve(params.teamId);
return () => {
appStateSubscription.unsubscribe();
+ personStoreListener.remove();
teamStoreListener.remove();
};
}, []);
@@ -56,9 +75,24 @@ const TeamMembers = ({ match }) => { // classes, teamId
- Team Members (
- {teamMemberCount}
- )
+
+ Team Members (
+ {teamMemberCount}
+ )
+
+
+ {teamMemberList.map((teamMember) => (
+
+ {teamMember.firstName}
+
+ ))}
{pigsCanFly && (
Team Members will fly here
)}
@@ -72,13 +106,15 @@ TeamMembers.propTypes = {
match: PropTypes.object.isRequired,
};
-const styles = () => ({
- buttonDesktop: {
- padding: '2px 16px',
- borderRadius: 5,
+const styles = (theme) => ({
+ ballotButtonIconRoot: {
+ marginRight: 8,
},
- searchButton: {
- borderRadius: 50,
+ addTeamMemberButtonRoot: {
+ width: 100,
+ [theme.breakpoints.down('md')]: {
+ width: '100%',
+ },
},
});
diff --git a/src/js/stores/AppObservableStore.js b/src/js/stores/AppObservableStore.js
index 65e3d5f..3a83939 100644
--- a/src/js/stores/AppObservableStore.js
+++ b/src/js/stores/AppObservableStore.js
@@ -21,7 +21,8 @@ export const messageService = {
const nonFluxState = {
activityTidbitWeVoteIdForDrawer: '',
- blockCampaignXRedirectOnSignIn: false, // When signing in from the header, don't mark a campaign as supported
+ addPersonDrawerOpen: false,
+ addPersonDrawerTeamId: -1, // Team ID used when adding a new person
blockChallengeRedirectOnSignIn: false, // When signing in from the header, don't mark a challenge as supported
challengeParticipantNameWithHighestRankByChallengeWeVoteId: {}, // Key is challengeWeVoteId, value is name for voter with the highest rank for that challenge
challengeParticipantRankOfVoterByChallengeWeVoteId: {}, // Key is challengeWeVoteId, value is rank of voter for that challenge
@@ -42,7 +43,6 @@ const nonFluxState = {
hideWeVoteLogo: false,
hostname: '',
observableUpdateCounter: 0,
- organizationModalBallotItemWeVoteId: '',
openReplayEnabled: false,
openReplayPending: false,
openReplayTracker: undefined,
@@ -90,10 +90,6 @@ const nonFluxState = {
export default {
- blockCampaignXRedirectOnSignIn () {
- return nonFluxState.blockCampaignXRedirectOnSignIn;
- },
-
blockChallengeRedirectOnSignIn () {
return nonFluxState.blockChallengeRedirectOnSignIn;
},
@@ -158,12 +154,8 @@ export default {
return nonFluxState.currentPathname;
},
- getPositionDrawerBallotItemWeVoteId () {
- return nonFluxState.positionDrawerBallotItemWeVoteId;
- },
-
- getPositionDrawerOrganizationWeVoteId () {
- return nonFluxState.positionDrawerOrganizationWeVoteId;
+ getGlobalVariableState (globalVariableName) {
+ return nonFluxState[globalVariableName];
},
getGoogleAnalyticsEnabled () {
@@ -210,10 +202,6 @@ export default {
return nonFluxState.hostname || '';
},
- getOrganizationModalBallotItemWeVoteId () {
- return nonFluxState.organizationModalBallotItemWeVoteId;
- },
-
getPendingSnackMessage () {
return nonFluxState.pendingSnackMessage;
},
@@ -373,11 +361,6 @@ export default {
messageService.sendMessage('state updated activityTidbitWeVoteIdForDrawerAndOpen');
},
- setBlockCampaignXRedirectOnSignIn (value) {
- nonFluxState.blockCampaignXRedirectOnSignIn = value;
- messageService.sendMessage('state updated blockCampaignXRedirectOnSignIn');
- },
-
setBlockChallengeRedirectOnSignIn (value) {
nonFluxState.blockChallengeRedirectOnSignIn = value;
messageService.sendMessage('state updated blockChallengeRedirectOnSignIn');
@@ -411,6 +394,11 @@ export default {
messageService.sendMessage('state updated getStartedMode');
},
+ setGlobalVariableState (globalVariableName, newState) {
+ nonFluxState[globalVariableName] = newState;
+ messageService.sendMessage(`state updated ${globalVariableName}`);
+ },
+
setHideOrganizationModalBallotItemInfo (hide) {
nonFluxState.hideOrganizationModalBallotItemInfo = hide;
messageService.sendMessage('state updated hideOrganizationModalBallotItemInfo');
@@ -466,26 +454,11 @@ export default {
messageService.sendMessage('state updated openReplayVoterWeVoteId');
},
- setOrganizationModalBallotItemWeVoteId (ballotItemWeVoteId) {
- nonFluxState.organizationModalBallotItemWeVoteId = ballotItemWeVoteId;
- messageService.sendMessage('state updated organizationModalBallotItemWeVoteId');
- },
-
setPendingSnackMessage (message, severity) {
nonFluxState.pendingSnackMessage = message;
nonFluxState.pendingSnackSeverity = severity;
},
- setPositionDrawerBallotItemWeVoteId (ballotItemWeVoteId) {
- nonFluxState.positionDrawerBallotItemWeVoteId = ballotItemWeVoteId;
- messageService.sendMessage('state updated positionDrawerBallotItemWeVoteId');
- },
-
- setPositionDrawerOrganizationWeVoteId (organizationWeVoteId) {
- nonFluxState.positionDrawerOrganizationWeVoteId = organizationWeVoteId;
- messageService.sendMessage('state updated positionDrawerOrganizationWeVoteId');
- },
-
setRecommendedCampaignListFirstRetrieveInitiated (value) {
nonFluxState.recommendedCampaignListFirstRetrieveInitiated = value;
messageService.sendMessage('state updated recommendedCampaignListFirstRetrieveInitiated');
diff --git a/src/js/stores/PersonStore.js b/src/js/stores/PersonStore.js
index 35bf3f3..b10ef47 100644
--- a/src/js/stores/PersonStore.js
+++ b/src/js/stores/PersonStore.js
@@ -1,6 +1,7 @@
import { ReduceStore } from 'flux/utils';
import Dispatcher from '../common/dispatcher/Dispatcher';
import Cookies from '../common/utils/js-cookie/Cookies';
+import arrayContains from '../common/utils/arrayContains';
class PersonStore extends ReduceStore {
getInitialState () {
@@ -44,6 +45,7 @@ class PersonStore extends ReduceStore {
getPersonById (personId) {
const { allCachedPeopleDict } = this.getState();
+ // console.log('PersonStore getPersonById:', personId, ', allCachedPeopleDict:', allCachedPeopleDict);
return allCachedPeopleDict[personId] || {};
}
@@ -57,10 +59,39 @@ class PersonStore extends ReduceStore {
}
reduce (state, action) {
+ let { allCachedPeopleDict } = state;
+ let revisedState = state;
+ let teamId = -1;
+ let teamMemberList = [];
+
switch (action.type) {
- case 'clearEmailAddressStatus':
- // console.log('VoterStore clearEmailAddressStatus');
- return { ...state, emailAddressStatus: {} };
+ case 'team-retrieve':
+ if (!action.res.success) {
+ console.log('PersonStore ', action.type, ' FAILED action.res:', action.res);
+ return state;
+ }
+ teamId = action.res.teamId || -1;
+ teamMemberList = action.res.teamMemberList || [];
+ revisedState = state;
+
+ // console.log('PersonStore team-retrieve start allCachedPeopleDict:', allCachedPeopleDict);
+ if (!allCachedPeopleDict) {
+ allCachedPeopleDict = {};
+ }
+ if (teamId > 0) {
+ teamMemberList.forEach((person) => {
+ // console.log('PersonStore team-retrieve adding person:', person);
+ if (person && (person.id >= 0) && !arrayContains(person.id, allCachedPeopleDict)) {
+ allCachedPeopleDict[person.id] = person;
+ }
+ });
+ // console.log('allCachedPeopleDict:', allCachedPeopleDict);
+ revisedState = {
+ ...revisedState,
+ allCachedPeopleDict,
+ };
+ }
+ return revisedState;
default:
return state;
diff --git a/src/js/stores/TeamStore.js b/src/js/stores/TeamStore.js
index 80a457e..921729d 100644
--- a/src/js/stores/TeamStore.js
+++ b/src/js/stores/TeamStore.js
@@ -33,6 +33,7 @@ class TeamStore extends ReduceStore {
const teamMemberList = [];
for (let i = 0; i < personIdList.length; i++) {
const person = PersonStore.getPersonById(personIdList[i]);
+ // console.log('TeamStore getTeamMemberList person:', person);
if (person) {
teamMemberList.push(person);
}
@@ -70,7 +71,7 @@ class TeamStore extends ReduceStore {
}
});
allCachedTeamMembersDict[teamId] = teamMemberIdList;
- console.log('allCachedTeamMembersDict:', allCachedTeamMembersDict);
+ // console.log('allCachedTeamMembersDict:', allCachedTeamMembersDict);
// console.log('allCachedOrganizationsDict:', allCachedOrganizationsDict);
revisedState = {
...revisedState,