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}
+
+ )}
+ />
);
}