Skip to content

Commit

Permalink
Merge branch 'main' into as/responsive-navbar-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
asibs authored Mar 29, 2024
2 parents c826152 + 59f5f9b commit f1ac4e5
Show file tree
Hide file tree
Showing 9 changed files with 517 additions and 178 deletions.
251 changes: 251 additions & 0 deletions app/constituencies/[slug]/SignupShare.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
"use client";

import {
Form,
Button,
ButtonGroup,
Spinner,
InputGroup,
} from "react-bootstrap";

import {
FaShare,
FaPuzzlePiece,
FaCopy,
FaHandHoldingHeart,
} from "react-icons/fa6";
import { submitANForm } from "@/utils/AnApiSubmission";
import { rubik } from "@/utils/Fonts";
import { useRef, useState, useEffect } from "react";
import ConstituencyLookup from "@/components/forms/constituencyLookup";

const emailErrorMessage = (code: EmailErrorCode) => {
switch (code) {
case "EMAIL_INVALID":
return "Please add a valid email address.";
case "SERVER_ERROR":
return "Something went wrong signing you up. Please try again?";
default:
return "";
}
};

type FormData = {
emailOptIn: boolean;
email: string;
};

const initialFormState: FormData = {
emailOptIn: false,
email: "",
};

// Variables used to track state of the ConstituencyLookup components

export default function SignupShare({
constituencyData,
}: {
constituencyData: ConstituencyData;
}) {
const [subscribed, setSubscribed] = useState<string | null | false>(false);
const [formState, setFormState] = useState<FormData>(initialFormState);
const [emailError, setEmailError] = useState<EmailErrorCode | null>(null);
const validPostcode = useRef("");
const [constituency, setConstituency] = useState<Constituency | null>(null);
const [constituencyApiLoading, setConstituencyApiLoading] = useState(false);

// Variables used to track state of the email opt-in components
const formRef = useRef<HTMLFormElement | null>(null);

// Track whether the user is already subscribed via localStorage
useEffect(() => {
//string = subscription Date.now()
//null = not subscribed on client
//false = on server
setSubscribed(window.localStorage.getItem("fwd-subscribed"));
}, []);

const submitForm = async () => {
if (
constituency &&
formState.email &&
formRef.current &&
!formRef.current.email.validity.typeMismatch
) {
//TODO set source codes from current url params.
const anResponse = await submitANForm(
formState.email,
validPostcode.current,
constituency,
process.env.NEXT_PUBLIC_AN_POSTCODE_FORM || "",
["stop the tories", "movement forward", "election reminders", "join"],
"", // source codes,
);

if (anResponse.ok) {
window.localStorage.setItem("fwd-subscribed", Date.now().toString());
setSubscribed("Subscribed");
} else {
setEmailError("SERVER_ERROR"); //AN doesn't give error codes on failure
}
}

// VALIDATION
// Invalid email
if (
formState.emailOptIn &&
(!formState.email ||
(formRef.current && formRef.current.email.validity.typeMismatch))
) {
setEmailError("EMAIL_INVALID");
}

// // no postcode or invalid postcode or constituency/address not selected
// if (
// !validPostcode.current ||
// !apiResponse ||
// !apiResponse.constituencies ||
// apiResponse.constituencies.length == 0
// ) {
// // User hasn't input anything or invalid postcode
// setPostError("POSTCODE_INVALID");
// return;
// }

// //not selected constituency or address
// if (
// apiResponse.constituencies.length > 1 &&
// formState.constituencyIndex === false
// ) {
// setPostError("UNCLEAR_CONSTITUENCY");
// }
};

if (!subscribed) {
return (
<Form
className="form-search"
ref={formRef}
action={submitForm}
noValidate
>
<h3>Join the movement forward</h3>
<ul>
<li>
<strong>Be counted</strong>, I&apos;m voting tactically!
</li>
<li>Get a voting plan</li>
<li>Get reminders and actions</li>
</ul>
{/* Renders the postcode box, makes API calls, and if necessary shows an address/constituency picker */}
<ConstituencyLookup
validPostcode={validPostcode}
constituency={constituency}
setConstituency={setConstituency}
loading={constituencyApiLoading}
setLoading={setConstituencyApiLoading}
filterConstituencySlug={constituencyData.constituencyIdentifiers.slug}
/>

<div className="my-3">
<>
<InputGroup hasValidation className="my-3">
<Form.Control
name="email"
size="lg"
type="email"
placeholder="Your Email"
value={formState.email}
isInvalid={!!emailError}
onChange={(e) => {
setFormState({ ...formState, email: e.target.value });
if (!e.target.validity.typeMismatch) {
setEmailError(null);
}
}}
className="invalid-text-greyed"
/>
<Form.Control.Feedback
className="fw-bold fst-italic px-2 pt-1 text-white"
type="invalid"
>
{emailError ? emailErrorMessage(emailError) : ""}
</Form.Control.Feedback>
</InputGroup>
<p className="small">
You&apos;re opting in to receive emails. We store your email
address, postcode, and constituency, so we can send you exactly
the information you need.
</p>
</>
</div>

<div className="d-flex justify-content-between mt-3">
<Button
variant="light"
size="lg"
type="submit"
disabled={!constituency}
aria-disabled={!constituency}
style={{ width: "66%" }}
>
{constituencyApiLoading && (
<>
<Spinner
as="span"
animation="border"
size="sm"
role="status"
area-hidden="true"
/>
<span className="visually-hidden">Loading...</span>{" "}
</>
)}
<span className={`${rubik.className} fw-bold`}>Join</span>
</Button>
<a
href="https://themovementforward.com/privacy/"
target="_blank"
rel="noreferrer"
className="btn btn-link btn-sm"
role="button"
>
<span className={`${rubik.className} fw-bold`}>Privacy Policy</span>
</a>
</div>
</Form>
);
} else {
return (
<div className="form-search">
<h3>Grow this movement</h3>
<p>You&apos;re in! Now let&apos;s build our numbers</p>
<ButtonGroup size="lg" vertical className="w-100 mb-0">
{/* TODO share link */}
<Button href="#" variant="light">
<FaShare /> Share with friends &amp; family
</Button>
<Button
variant="light"
onClick={() =>
window.navigator.clipboard.writeText(window.location.href)
}
>
<FaCopy />
Copy link to this page
</Button>
<Button
href="https://themovementforward.com/volunteer/"
variant="light"
>
<FaPuzzlePiece /> Volunteer
</Button>

<Button href="/donate" variant="light">
<FaHandHoldingHeart /> Support our crowdfunder
</Button>
</ButtonGroup>
</div>
);
}
}
37 changes: 4 additions & 33 deletions app/constituencies/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Col, Container, Row, ButtonGroup, Button } from "react-bootstrap";
import { Col, Container, Row } from "react-bootstrap";
import Link from "next/link";
import Header from "@/components/Header";
import ActionBox from "@/components/info_box/ActionBox";
import ImpliedChart from "@/components/info_box/ImpliedChart";
import MRPChart from "@/components/info_box/MRPChart";
import PlanToVoteBox from "@/components/info_box/PlanToVoteBox";
Expand All @@ -12,13 +11,7 @@ import {
getConstituencySlugs,
} from "@/utils/constituencyData";
import { notFound } from "next/navigation";
import {
FaShare,
FaPuzzlePiece,
FaCopy,
FaHandHoldingHeart,
} from "react-icons/fa6";
import PostcodeLookup from "@/components/constituency_lookup/ConstituencyLookup";
import SignupShare from "./SignupShare";

export const dynamicParams = false; // Don't allow params not in generateStaticParams

Expand Down Expand Up @@ -86,6 +79,7 @@ export default async function ConstituencyPage({
}
}

// NO ADVICE OVERRIDE (NI & Speaker)
if (constituencyData.recommendation.partySlug === "None") {
return (
<>
Expand Down Expand Up @@ -157,30 +151,7 @@ export default async function ConstituencyPage({
</Row>
<Row xs={1} lg={3}>
<Col md={7} className="pb-3">
<div className="form-search">
<h3>Grow this movement</h3>
<p>You&apos;re in! Now let&apos;s build our numbers</p>
<ButtonGroup size="lg" vertical className="w-100 mb-0">
{/* TODO share link and clipboard copy */}
<Button href="#" variant="light">
<FaShare /> Share with friends &amp; family
</Button>
<Button href="#" variant="light">
<FaCopy />
Copy link to this page
</Button>
<Button
href="https://themovementforward.com/volunteer/"
variant="light"
>
<FaPuzzlePiece /> Volunteer
</Button>

<Button href="/donate" variant="light">
<FaHandHoldingHeart /> Support our crowdfunder
</Button>
</ButtonGroup>
</div>
<SignupShare constituencyData={constituencyData} />
</Col>
<Col md={7} className="pb-3">
<p style={{ fontSize: "26px" }}>
Expand Down
Loading

0 comments on commit f1ac4e5

Please sign in to comment.