diff --git a/src/js/common/components/Widgets/LinkToAdminTools.jsx b/src/js/common/components/Widgets/LinkToAdminTools.jsx new file mode 100644 index 000000000..dfa54ea2b --- /dev/null +++ b/src/js/common/components/Widgets/LinkToAdminTools.jsx @@ -0,0 +1,67 @@ +import PropTypes from 'prop-types'; +import React, { Component, Suspense } from 'react'; +import styled from 'styled-components'; +import { isCordova } from '../../utils/isCordovaOrWebApp'; +import VoterStore from '../../../stores/VoterStore'; + +const OpenExternalWebSite = React.lazy(() => import(/* webpackChunkName: 'OpenExternalWebSite' */ './OpenExternalWebSite')); + +class LinkToAdminTools extends Component { + constructor (props) { + super(props); + this.state = { + voter: null, + }; + } + + componentDidMount () { + this.onVoterStoreChange(); + this.voterStoreListener = VoterStore.addListener(this.onVoterStoreChange.bind(this)); + } + + componentWillUnmount () { + this.voterStoreListener.remove(); + } + + onVoterStoreChange = () => { + this.setState({ + voter: VoterStore.getVoter(), + }); + }; + + render () { + const { adminToolsUrl, linkId, linkTextNode } = this.props; + const { voter } = this.state; + return ( + + {/* Show links to this candidate in the admin tools */} + {(voter && (voter.is_admin || voter.is_verified_volunteer)) && ( + + Admin only: + }> + edit} + /> + + + )} + + ); + } +} +LinkToAdminTools.propTypes = { + linkId: PropTypes.string, + linkTextNode: PropTypes.node, + adminToolsUrl: PropTypes.string.isRequired, +}; + +const LinkToAdminToolsWrapper = styled('div')` + margin-top: ${() => (isCordova() ? '100px' : null)}; + padding-bottom: ${() => (isCordova() ? '800px' : null)}; +`; + +export default LinkToAdminTools; diff --git a/src/js/common/pages/Politician/PoliticianDetailsPage.jsx b/src/js/common/pages/Politician/PoliticianDetailsPage.jsx index 61891b86a..9e2f9ae29 100644 --- a/src/js/common/pages/Politician/PoliticianDetailsPage.jsx +++ b/src/js/common/pages/Politician/PoliticianDetailsPage.jsx @@ -19,6 +19,7 @@ import { EditIndicator, ElectionInPast, IndicatorButtonWrapper, IndicatorRow } f import { CandidateCampaignListDesktop, CandidateCampaignListMobile, CandidateCampaignWrapper, OfficeHeldNameDesktop, OfficeHeldNameMobile, PoliticianImageDesktop, PoliticianImageDesktopPlaceholder, PoliticianImageMobile, PoliticianImageMobilePlaceholder, PoliticianNameDesktop, PoliticianNameMobile, PoliticianNameOuterWrapperDesktop } from '../../components/Style/PoliticianDetailsStyles'; import { PageWrapper } from '../../components/Style/stepDisplayStyles'; import DelayedLoad from '../../components/Widgets/DelayedLoad'; +import LinkToAdminTools from '../../components/Widgets/LinkToAdminTools'; import OfficeHeldNameText from '../../components/Widgets/OfficeHeldNameText'; import SearchOnGoogle from '../../components/Widgets/SearchOnGoogle'; import ViewOnBallotpedia from '../../components/Widgets/ViewOnBallotpedia'; @@ -491,6 +492,7 @@ class PoliticianDetailsPage extends Component { voterCanEditThisPolitician, voterSupportsThisPolitician, wikipediaUrl, // youtubeUrl, } = this.state; + const campaignAdminEditUrl = `${webAppConfig.WE_VOTE_SERVER_ROOT_URL}campaign/${linkedCampaignXWeVoteId}/summary`; if (politicianDataNotFound) { return ( @@ -985,6 +987,12 @@ class PoliticianDetailsPage extends Component { {positionListTeaserHtml} {commentListTeaserHtml} + {/* Show links to this campaign in the admin tools */} + edit campaign} + />  }> diff --git a/src/js/pages/Ballot/Candidate.jsx b/src/js/pages/Ballot/Candidate.jsx index 1799d8e7a..327b376d5 100644 --- a/src/js/pages/Ballot/Candidate.jsx +++ b/src/js/pages/Ballot/Candidate.jsx @@ -19,6 +19,7 @@ import { renderLog } from '../../common/utils/logging'; import { convertToInteger } from '../../common/utils/textFormat'; import toTitleCase from '../../common/utils/toTitleCase'; import CandidateStickyHeader from '../../components/Ballot/CandidateStickyHeader'; +import LinkToAdminTools from '../../common/components/Widgets/LinkToAdminTools'; import { PageContentContainer } from '../../components/Style/pageLayoutStyles'; import SearchOnGoogle from '../../common/components/Widgets/SearchOnGoogle'; import SnackNotifier from '../../common/components/Widgets/SnackNotifier'; @@ -33,7 +34,6 @@ import VoterStore from '../../stores/VoterStore'; const CampaignSupportThermometer = React.lazy(() => import(/* webpackChunkName: 'CampaignSupportThermometer' */ '../../common/components/CampaignSupport/CampaignSupportThermometer')); const CandidateItem = React.lazy(() => import(/* webpackChunkName: 'CandidateItem' */ '../../components/Ballot/CandidateItem')); const DelayedLoad = React.lazy(() => import(/* webpackChunkName: 'DelayedLoad' */ '../../common/components/Widgets/DelayedLoad')); -const OpenExternalWebSite = React.lazy(() => import(/* webpackChunkName: 'OpenExternalWebSite' */ '../../common/components/Widgets/OpenExternalWebSite')); const PositionList = React.lazy(() => import(/* webpackChunkName: 'PositionList' */ '../../components/Ballot/PositionList')); const ShareButtonDesktopTablet = React.lazy(() => import(/* webpackChunkName: 'ShareButtonDesktopTablet' */ '../../components/Share/ShareButtonDesktopTablet')); const ViewUpcomingBallotButton = React.lazy(() => import(/* webpackChunkName: 'ViewUpcomingBallotButton' */ '../../components/Ready/ViewUpcomingBallotButton')); @@ -252,7 +252,7 @@ class Candidate extends Component { onVoterGuideStoreChange () { // console.log('Candidate onVoterGuideStoreChange'); - // Trigger an update of the candidate so we can get an updated position_list + // Trigger an update of the candidate, so we can get an updated position_list // CandidateActions.candidateRetrieve(this.state.candidateWeVoteId); // CandidateActions.positionListForBallotItemPublic(this.state.candidateWeVoteId); } @@ -315,7 +315,6 @@ class Candidate extends Component { const candidateName = toTitleCase(candidate.ballot_item_display_name); const titleText = `${candidateName} - WeVote`; const descriptionText = `Information about ${candidateName}, candidate for ${candidate.contest_office_name}`; - const voter = VoterStore.getVoter(); const candidateAdminEditUrl = `${webAppConfig.WE_VOTE_SERVER_ROOT_URL}c/${candidate.id}/edit/?google_civic_election_id=${VoterStore.electionId()}&state_code=`; // TODO When we remove expandIssuesByDefault from CandidateItem, the page is pushed very wide. This needs to be fixed. @@ -419,26 +418,17 @@ class Candidate extends Component { */}
{/* Show links to this candidate in the admin tools */} - { (voter.is_admin || voter.is_verified_volunteer) && ( - - Admin only: - }> - - edit - {' '} - {candidateName} - - )} - /> -
- - )} + + edit + {' '} + {candidateName} + + )} + /> ); diff --git a/src/js/pages/Ballot/Measure.jsx b/src/js/pages/Ballot/Measure.jsx index 01c891de2..fe9389d28 100644 --- a/src/js/pages/Ballot/Measure.jsx +++ b/src/js/pages/Ballot/Measure.jsx @@ -17,6 +17,7 @@ import toTitleCase from '../../common/utils/toTitleCase'; import MeasureStickyHeader from '../../components/Ballot/MeasureStickyHeader'; import { PageContentContainer } from '../../components/Style/pageLayoutStyles'; import EndorsementCard from '../../components/Widgets/EndorsementCard'; +import LinkToAdminTools from '../../common/components/Widgets/LinkToAdminTools'; import SearchOnGoogle from '../../common/components/Widgets/SearchOnGoogle'; import SnackNotifier from '../../common/components/Widgets/SnackNotifier'; import ViewOnBallotpedia from '../../common/components/Widgets/ViewOnBallotpedia'; @@ -30,7 +31,6 @@ import { cordovaBallotFilterTopMargin } from '../../utils/cordovaOffsets'; const DelayedLoad = React.lazy(() => import(/* webpackChunkName: 'DelayedLoad' */ '../../common/components/Widgets/DelayedLoad')); const MeasureItem = React.lazy(() => import(/* webpackChunkName: 'MeasureItem' */ '../../components/Ballot/MeasureItem')); -const OpenExternalWebSite = React.lazy(() => import(/* webpackChunkName: 'OpenExternalWebSite' */ '../../common/components/Widgets/OpenExternalWebSite')); const PositionList = React.lazy(() => import(/* webpackChunkName: 'PositionList' */ '../../components/Ballot/PositionList')); const ShareButtonDesktopTablet = React.lazy(() => import(/* webpackChunkName: 'ShareButtonDesktopTablet' */ '../../components/Share/ShareButtonDesktopTablet')); @@ -296,8 +296,6 @@ class Measure extends Component { const measureName = toTitleCase(ballotItemDisplayName); const titleText = `${measureName} - WeVote`; const descriptionText = `Information about ${measureName}`; - const voter = VoterStore.getVoter(); - const { is_admin: isAdmin, is_verified_volunteer: isVerifiedVolunteer } = voter; const measureAdminEditUrl = `${webAppConfig.WE_VOTE_SERVER_ROOT_URL}m/${measureId}/edit/?google_civic_election_id=${VoterStore.electionId()}&state_code=`; return ( @@ -371,27 +369,18 @@ class Measure extends Component { text={`Are there endorsements for ${measureName} that you expected to see?`} />
- {/* Show links to this candidate in the admin tools */} - { (isAdmin || isVerifiedVolunteer) && ( - - Admin only: - }> - - edit - {' '} - {measureName} - - )} - /> - - - )} + {/* Show links to this measure in the admin tools */} + + edit + {' '} + {measureName} + + )} + /> diff --git a/src/js/pages/VoterGuide/OrganizationVoterGuideCandidate.jsx b/src/js/pages/VoterGuide/OrganizationVoterGuideCandidate.jsx index c65aa27f4..97bff0ed9 100644 --- a/src/js/pages/VoterGuide/OrganizationVoterGuideCandidate.jsx +++ b/src/js/pages/VoterGuide/OrganizationVoterGuideCandidate.jsx @@ -17,6 +17,7 @@ import toTitleCase from '../../common/utils/toTitleCase'; import OrganizationVoterGuideCandidateItem from '../../components/VoterGuide/OrganizationVoterGuideCandidateItem'; import { PageContentContainer } from '../../components/Style/pageLayoutStyles'; import EndorsementCard from '../../components/Widgets/EndorsementCard'; +import LinkToAdminTools from '../../common/components/Widgets/LinkToAdminTools'; import SnackNotifier from '../../common/components/Widgets/SnackNotifier'; import ThisIsMeAction from '../../components/Widgets/ThisIsMeAction'; import webAppConfig from '../../config'; @@ -25,7 +26,6 @@ import CandidateStore from '../../stores/CandidateStore'; import VoterGuideStore from '../../stores/VoterGuideStore'; import VoterStore from '../../stores/VoterStore'; -const OpenExternalWebSite = React.lazy(() => import(/* webpackChunkName: 'OpenExternalWebSite' */ '../../common/components/Widgets/OpenExternalWebSite')); const PositionList = React.lazy(() => import(/* webpackChunkName: 'PositionList' */ '../../components/Ballot/PositionList')); const ViewUpcomingBallotButton = React.lazy(() => import(/* webpackChunkName: 'ViewUpcomingBallotButton' */ '../../components/Ready/ViewUpcomingBallotButton')); @@ -114,7 +114,7 @@ class OrganizationVoterGuideCandidate extends Component { onVoterGuideStoreChange () { // console.log('Candidate onVoterGuideStoreChange'); const { candidateWeVoteId } = this.state; - // When the voterGuidesToFollowForLatestBallotItem changes, trigger an update of the candidate so we can get an updated position_list + // When the voterGuidesToFollowForLatestBallotItem changes, trigger an update of the candidate, so we can get an updated position_list // CandidateActions.candidateRetrieve(candidateWeVoteId); CandidateActions.positionListForBallotItemPublic(candidateWeVoteId); CandidateActions.positionListForBallotItemFromFriends(candidateWeVoteId); @@ -153,7 +153,6 @@ class OrganizationVoterGuideCandidate extends Component { const candidateName = toTitleCase(candidate.ballot_item_display_name); const titleText = `${candidateName} - WeVote`; const descriptionText = `Information about ${candidateName}, candidate for ${candidate.contest_office_name}`; - const voter = VoterStore.getVoter(); const candidateAdminEditUrl = `${webAppConfig.WE_VOTE_SERVER_ROOT_URL}c/${candidate.id}/edit/?google_civic_election_id=${VoterStore.electionId()}&state_code=`; return ( @@ -235,26 +234,17 @@ class OrganizationVoterGuideCandidate extends Component {
{/* Show links to this candidate in the admin tools */} - { (voter.is_admin || voter.is_verified_volunteer) && ( - - Admin only: - }> - - edit - {' '} - {candidateName} - - )} - /> - - - )} + + edit + {' '} + {candidateName} + + )} + /> ); }