Skip to content

Commit

Permalink
Merge branch 'main' into jms/constituency-page-ui
Browse files Browse the repository at this point in the history
  • Loading branch information
jms301 authored Mar 26, 2024
2 parents 39f60af + 68c946f commit ca28883
Show file tree
Hide file tree
Showing 17 changed files with 1,071 additions and 221 deletions.
266 changes: 236 additions & 30 deletions app/constituencies/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,46 @@ import { Col, Container, Row } from "react-bootstrap";
import Link from "next/link";
import Header from "@/components/Header";
import { partyColorFromSlug, partyNameFromSlug } from "@/utils/Party";
import { getConstituenciesData, majority } from "@/utils/constituencyData";
import {
getConstituenciesData,
majority,
sortOnMajority,
} from "@/utils/constituencyData";

export default async function ConstituencySummaryPage() {
// Get all constituencies EXCEPT those where we're explicitly making no recommendation (NI & Speaker)
const constituenciesData: ConstituencyData[] = (
await getConstituenciesData()
).filter((c: ConstituencyData) => c.recommendation.partySlug !== "None");
const torySeats = constituenciesData
.filter(
(c: ConstituencyData) => c.impliedPreviousResult.winningParty === "Con",
)
.sort(
(a, b) =>
majority(a.impliedPreviousResult) - majority(b.impliedPreviousResult),
);

const nonTorySeats = constituenciesData
.filter(
(c: ConstituencyData) => c.impliedPreviousResult.winningParty !== "Con",
)
.sort(
(a, b) =>
majority(a.impliedPreviousResult) - majority(b.impliedPreviousResult),
);
const torySeats = constituenciesData.filter(
(c) => c.impliedPreviousResult.winningParty === "Con",
);
const torySeatsProgressiveAhead = sortOnMajority(
torySeats.filter(
(c) =>
c.recommendation.partySlug && c.pollingResults.winningParty !== "Con",
),
);
const torySeatsProgressiveBehind = sortOnMajority(
torySeats.filter(
(c) =>
c.recommendation.partySlug && c.pollingResults.winningParty === "Con",
),
);
const torySeatsNoRecommendation = sortOnMajority(
torySeats.filter((c) => !c.recommendation.partySlug),
);

const nonTorySeats = constituenciesData.filter(
(c) => c.impliedPreviousResult.winningParty !== "Con",
);
const nonTorySeatsToryCanWin = sortOnMajority(
nonTorySeats.filter((c) => !c.otherVoteData.conservativeWinUnlikely),
);
const nonTorySeatsToryCantWin = sortOnMajority(
nonTorySeats.filter((c) => c.otherVoteData.conservativeWinUnlikely),
);

return (
<>
Expand All @@ -38,17 +55,170 @@ export default async function ConstituencySummaryPage() {
<main>
<section className="section-light">
<Container>
{/* 2019 TORY SEATS WITH SLIM MAJORITIES */}
<Row className="pb-4">
<Col>
<h3>Current Tory Seats</h3>
<h3>Target Tory Seats</h3>
<p className="mb-2">
There are {torySeatsProgressiveAhead.length} Tory
Constituencies* that we have a great chance of taking back!
</p>
<p className="fst-italic">
Based on converting 2019 voting patterns to new constituency
* Based on converting 2019 voting patterns to new constituency
boundaries
</p>
</Col>
</Row>
<Row className="pb-5">
<Col
xs={12}
lg={{ offset: 1, span: 10 }}
xl={{ offset: 2, span: 8 }}
>
<Container className="text-center">
<Row xs={2}>
<Col>
<h5>We could turn this...</h5>
</Col>
<Col>
<h5>Into this...</h5>
</Col>
</Row>

{torySeatsProgressiveAhead.map((c) => (
<Row
xs={2}
className="pb-1"
key={c.constituencyIdentifiers.slug}
>
<Col>
<Link
href={`/constituencies/${c.constituencyIdentifiers.slug}`}
>
<span
className="badge rounded-pill w-100 h-100 text-wrap align-middle"
style={{
backgroundColor: partyColorFromSlug("Con"),
}}
>
{c.constituencyIdentifiers.name}
</span>
</Link>
</Col>
<Col>
<Link
href={`/constituencies/${c.constituencyIdentifiers.slug}`}
>
<span
className="badge rounded-pill w-100 h-100 text-wrap align-middle"
style={{
backgroundColor: partyColorFromSlug(
c.recommendation.partySlug,
),
}}
>
{c.recommendation.partySlug
? partyNameFromSlug(c.recommendation.partySlug)
: "TBC"}
</span>
</Link>
</Col>
</Row>
))}
</Container>
</Col>
</Row>

{/* 2019 TORY SEATS WITH BIG MAJORITIES */}
<Row className="pb-4">
<Col>
<h3>Tough Tory Seats</h3>
<p className="mb-2">
There are {torySeatsProgressiveBehind.length} Tory
Constituencies* that we need to work even harder to take back!
</p>
<p className="fst-italic">
* Based on converting 2019 voting patterns to new constituency
boundaries
</p>
</Col>
</Row>
<Row className="pb-5">
<Col
xs={12}
lg={{ offset: 1, span: 10 }}
xl={{ offset: 2, span: 8 }}
>
<Container className="text-center">
<Row xs={2}>
<Col>
<h5>We could turn this...</h5>
</Col>
<Col>
<h5>Into this...</h5>
</Col>
</Row>

{torySeatsProgressiveBehind.map((c) => (
<Row
xs={2}
className="pb-1"
key={c.constituencyIdentifiers.slug}
>
<Col>
<Link
href={`/constituencies/${c.constituencyIdentifiers.slug}`}
>
<span
className="badge rounded-pill w-100 h-100 text-wrap align-middle"
style={{
backgroundColor: partyColorFromSlug("Con"),
}}
>
{c.constituencyIdentifiers.name}
</span>
</Link>
</Col>
<Col>
<Link
href={`/constituencies/${c.constituencyIdentifiers.slug}`}
>
<span
className="badge rounded-pill w-100 h-100 text-wrap align-middle"
style={{
backgroundColor: partyColorFromSlug(
c.recommendation.partySlug,
),
}}
>
{c.recommendation.partySlug
? partyNameFromSlug(c.recommendation.partySlug)
: "TBC"}
</span>
</Link>
</Col>
</Row>
))}
</Container>
</Col>
</Row>

<Row>
{/* 2019 TORY SEATS WITH NO RECOMMENDATION YET */}
<Row className="pb-4">
<Col>
<h3>Check Back Later</h3>
<p className="mb-2">
There are {torySeatsNoRecommendation.length} Tory
Constituencies* where we&apos;re still working out the
tactical vote...
</p>
<p className="fst-italic">
* Based on converting 2019 voting patterns to new constituency
boundaries
</p>
</Col>
</Row>
<Row className="pb-5">
<Col
xs={12}
lg={{ offset: 1, span: 10 }}
Expand All @@ -64,7 +234,7 @@ export default async function ConstituencySummaryPage() {
</Col>
</Row>

{torySeats.map((c) => (
{torySeatsNoRecommendation.map((c) => (
<Row
xs={2}
className="pb-1"
Expand Down Expand Up @@ -112,22 +282,58 @@ export default async function ConstituencySummaryPage() {

<section className="section-light">
<Container>
<Row className="pb-4">
{/* NON-TORY SEATS, CAN BE WON BY A TORY */}
<Row className="pb-3">
<Col>
<h3>Non-Tory Seats</h3>
<h3>At Risk Non-Tory Seats</h3>
<p className="mb-2">
There are {nonTorySeatsToryCanWin.length} non-Tory
Constituencies* where we need to vote tactically to make sure
the Tories don&apos;t win!
</p>
<p className="fst-italic">
Based on converting 2019 voting patterns to new constituency
* Based on converting 2019 voting patterns to new constituency
boundaries
</p>
</Col>
</Row>

<Row>
<h5>Let&apos;s keep them out of Tory hands</h5>
<Row xs={2} md={3} lg={4} xl={5} xxl={6} className="pb-5 g-1">
{nonTorySeatsToryCanWin.map((c) => (
<Col key={c.constituencyIdentifiers.slug}>
<Link
href={`/constituencies/${c.constituencyIdentifiers.slug}`}
>
<span
className="badge rounded-pill w-100 h-100 text-wrap align-middle"
style={{
backgroundColor: partyColorFromSlug(
c.recommendation.partySlug,
),
}}
>
{c.constituencyIdentifiers.name}
</span>
</Link>
</Col>
))}
</Row>

<Row xs={2} md={3} lg={4} xl={5} xxl={6} className="g-1">
{nonTorySeats.map((c) => (
{/* NON-TORY SEATS, CAN'T BE WON BY A TORY */}
<Row className="pb-3">
<Col>
<h3>Safe Non-Tory Seats</h3>
<p className="mb-2">
There are {nonTorySeatsToryCantWin.length} non-Tory
Constituencies* where you can safely vote with your heart
</p>
<p className="fst-italic">
* Based on converting 2019 voting patterns to new constituency
boundaries
</p>
</Col>
</Row>
<Row xs={2} md={3} lg={4} xl={5} xxl={6} className="pb-5 g-1">
{nonTorySeatsToryCantWin.map((c) => (
<Col key={c.constituencyIdentifiers.slug}>
<Link
href={`/constituencies/${c.constituencyIdentifiers.slug}`}
Expand All @@ -136,7 +342,7 @@ export default async function ConstituencySummaryPage() {
className="badge rounded-pill w-100 h-100 text-wrap align-middle"
style={{
backgroundColor: partyColorFromSlug(
c.impliedPreviousResult.biggestProgressiveParty,
c.impliedPreviousResult.winningParty,
),
}}
>
Expand Down
Loading

0 comments on commit ca28883

Please sign in to comment.