-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
707 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import Container from "@components/common/container"; | ||
import PageTitle from "@components/common/page-title"; | ||
|
||
interface Props { | ||
params: { | ||
id: string; | ||
bet: string; | ||
}; | ||
} | ||
|
||
export function generateMetadata({ params: { id, bet } }: Props) { | ||
return { | ||
title: `${id} ${bet} | Votering | Partiguiden`, | ||
description: `Hur har partiernat röstat i voteringen ${id}`, | ||
}; | ||
} | ||
|
||
export default function Vote() { | ||
// TODO: Implement | ||
return ( | ||
<main> | ||
<PageTitle></PageTitle> | ||
<Container></Container> | ||
</main> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import PageTitle from "@components/common/page-title"; | ||
import type { FilterToggle } from "@components/filter/filter-context"; | ||
import { FilterContextProvider } from "@components/filter/filter-context"; | ||
import { ScaleIcon } from "@heroicons/react/24/solid"; | ||
import { getVotes } from "@lib/api/vote/get-votes"; | ||
import { | ||
parseNumberSearchParam, | ||
parseStringArraySearchParam, | ||
parseStringSearchParam, | ||
} from "@lib/utils/search-params"; | ||
import VoteList from "./vote-list"; | ||
import Filter from "@components/filter"; | ||
import { Committee, committeeInfo } from "@lib/committes"; | ||
|
||
export const metadata = { | ||
title: "Voteringar | Partiguiden", | ||
description: "Hur har partierna röstat i voteringar? Ta reda på det här", | ||
}; | ||
|
||
function initialFilterToggles(committees: string[]): FilterToggle<Committee> { | ||
return Object.values(Committee).reduce( | ||
(prev, committee) => ({ | ||
...prev, | ||
[committee]: { | ||
title: committeeInfo[committee].desc, | ||
value: committees.includes(committee), | ||
}, | ||
}), | ||
{} as FilterToggle<Committee>, | ||
); | ||
} | ||
|
||
interface Props { | ||
searchParams: { | ||
sok?: string | string[]; | ||
sida?: string | string[]; | ||
utskott?: string | string[]; | ||
}; | ||
} | ||
|
||
export default async function Votes({ searchParams }: Props) { | ||
const page = parseNumberSearchParam(searchParams.sida) ?? 1; | ||
const search = parseStringSearchParam(searchParams.sok); | ||
const committees = parseStringArraySearchParam(searchParams.utskott); | ||
const votes = await getVotes({ search, page, committees }); | ||
|
||
const filterToggles = initialFilterToggles(committees); | ||
|
||
return ( | ||
<main> | ||
<PageTitle Icon={ScaleIcon}>Voteringar</PageTitle> | ||
|
||
<div className="mx-4 mb-4 flex gap-2 2xl:container 2xl:mx-auto"> | ||
<FilterContextProvider initialToggles={filterToggles}> | ||
<VoteList currentPage={page} votes={votes} /> | ||
<Filter /> | ||
</FilterContextProvider> | ||
</div> | ||
</main> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
"use client"; | ||
import type { FilterToggle } from "@components/filter/filter-context"; | ||
import { useFilterContext } from "@components/filter/filter-context"; | ||
import type { VoteList } from "@lib/api/vote/types"; | ||
import { routes } from "@lib/navigation"; | ||
import { useRouter } from "next/navigation"; | ||
import { useEffect } from "react"; | ||
import Vote from "./vote"; | ||
import Pagination from "@components/common/pagination"; | ||
|
||
interface QueryParameters { | ||
search: string; | ||
toggles: FilterToggle<string>; | ||
page?: number; | ||
} | ||
|
||
function buildQueryParameters({ search, toggles, page }: QueryParameters) { | ||
const query = new URLSearchParams(); | ||
const activeToggles = Object.entries(toggles) | ||
.filter(([, value]) => value.value) | ||
.map(([key]) => key); | ||
for (const toggle of activeToggles) { | ||
query.append("utskott", toggle); | ||
} | ||
if (search) { | ||
query.set("sok", search); | ||
} | ||
|
||
if (page) { | ||
query.set("sida", page.toString()); | ||
} | ||
|
||
return query; | ||
} | ||
|
||
interface Props { | ||
currentPage: number; | ||
votes: VoteList; | ||
} | ||
|
||
export default function VoteList({ votes, currentPage }: Props) { | ||
const router = useRouter(); | ||
const { search, toggles } = useFilterContext(); | ||
|
||
useEffect(() => { | ||
const debounce = setTimeout(() => { | ||
const query = buildQueryParameters({ search, toggles }); | ||
router.replace(`${routes.votes}?${query}`); | ||
}, 500); | ||
return () => { | ||
clearTimeout(debounce); | ||
}; | ||
}, [router, search, toggles]); | ||
|
||
function onChangePage(newPage: number) { | ||
const query = buildQueryParameters({ search, toggles, page: newPage }); | ||
router.push(`${routes.votes}?${query}`); | ||
} | ||
|
||
if (votes.pages === 0) { | ||
return ( | ||
<p className="flex-1 text-center text-xl sm:text-2xl"> | ||
Inga voteringar hittades | ||
</p> | ||
); | ||
} | ||
|
||
return ( | ||
<div className="grid flex-1 gap-4"> | ||
<Pagination | ||
current={currentPage} | ||
total={votes.pages} | ||
onChange={onChangePage} | ||
/> | ||
{votes.votes.map((vote) => ( | ||
<Vote key={`${vote.documentId}:${vote.proposition}`} vote={vote} /> | ||
))} | ||
<Pagination | ||
current={currentPage} | ||
total={votes.pages} | ||
onChange={onChangePage} | ||
/> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import PartyIcon from "@components/party/icon"; | ||
import type { VotingResult } from "@lib/api/vote/types"; | ||
import type { Party } from "@partiguiden/party-data/types"; | ||
|
||
import { twMerge } from "tailwind-merge"; | ||
|
||
interface ResultColumnProps { | ||
votes: Party[]; | ||
title: string; | ||
className: string; | ||
} | ||
|
||
function ResultColumn({ votes, title, className }: ResultColumnProps) { | ||
return ( | ||
<div className={twMerge("flex-1 px-2 py-4 text-center", className)}> | ||
<p>{title}</p> | ||
<div className="mt-4 flex flex-wrap justify-center gap-2 px-2"> | ||
{votes.map((party) => ( | ||
<PartyIcon | ||
party={party} | ||
key={party} | ||
className="h-8 w-8 md:h-10 md:w-10" | ||
sizes="(min-width: 768px) 32px, 40px" | ||
/> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
interface Props { | ||
votes: VotingResult; | ||
} | ||
|
||
export default function VoteResult({ votes }: Props) { | ||
if (!votes.no.length || !votes.yes.length) { | ||
return <p>Ingen voteringsdata hittades</p>; | ||
} | ||
|
||
return ( | ||
<div className="flex flex-col sm:flex-row"> | ||
<ResultColumn | ||
className={twMerge( | ||
"bg-green-200 dark:bg-green-700", | ||
votes.winner !== "yes" && "bg-slate-200 dark:bg-slate-600", | ||
)} | ||
title="JA" | ||
votes={votes.yes} | ||
/> | ||
<ResultColumn | ||
className={twMerge( | ||
"bg-red-200 dark:bg-red-800", | ||
votes.winner !== "no" && "bg-slate-200 dark:bg-slate-600", | ||
)} | ||
title="NEJ" | ||
votes={votes.no} | ||
/> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { Card, CommitteeHeader } from "@components/common/card"; | ||
import type { VoteListEntry } from "@lib/api/vote/types"; | ||
import VoteResult from "./vote-result"; | ||
import Link from "next/link"; | ||
import { routes } from "@lib/navigation"; | ||
|
||
interface Props { | ||
vote: VoteListEntry; | ||
} | ||
|
||
export default function Vote({ vote }: Props) { | ||
return ( | ||
<Link href={routes.vote(vote.documentId, vote.proposition)}> | ||
<Card className="p-0"> | ||
<CommitteeHeader committee={vote.committee} /> | ||
<div className="p-4"> | ||
<p className="text-sm text-slate-600 dark:text-slate-400"> | ||
{vote.title} | ||
</p> | ||
<p>{vote.subtitle}</p> | ||
</div> | ||
<VoteResult votes={vote.results} /> | ||
</Card> | ||
</Link> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,28 @@ | ||
import Image from "next/image"; | ||
import { partyLogo } from "@lib/assets"; | ||
import type { Party } from "@partiguiden/party-data/types"; | ||
import { twMerge } from "tailwind-merge"; | ||
|
||
interface PartyIconProps { | ||
party: Party; | ||
size?: number; | ||
className?: string; | ||
sizes?: string; | ||
} | ||
|
||
export default function PartyIcon({ | ||
party, | ||
size = 25, | ||
className, | ||
sizes = "24px", | ||
}: PartyIconProps) { | ||
return ( | ||
<Image | ||
src={partyLogo(party)} | ||
width={size} | ||
height={size} | ||
alt={`${party} logo`} | ||
className={className} | ||
/> | ||
<div className={twMerge("relative h-6 w-6", className)}> | ||
<Image | ||
src={partyLogo(party)} | ||
fill | ||
sizes={sizes} | ||
className="object-cover" | ||
alt={`${party} logga`} | ||
/> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { PARLIAMENT_BASE_URL } from "@lib/constants"; | ||
import stripJsonComments from "strip-json-comments"; | ||
import type { VoteResultsResponse } from "./types"; | ||
import parseVoteResult from "./parsers/vote-result"; | ||
|
||
export default async function getVoteResult( | ||
id: string, | ||
num: number, | ||
): Promise<VoteResultsResponse> { | ||
const response = await fetch( | ||
`${PARLIAMENT_BASE_URL}/dokumentstatus/${id}.json`, | ||
); | ||
const text = await response.text(); | ||
const data = JSON.parse(stripJsonComments(text)); | ||
|
||
return parseVoteResult(data, num); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { PARLIAMENT_BASE_URL } from "@lib/constants"; | ||
import { parseVotes } from "./parsers/votes"; | ||
|
||
interface Query { | ||
search?: string; | ||
committees: string[]; | ||
page?: number; | ||
} | ||
|
||
export async function getVotes({ search, committees, page }: Query) { | ||
const query = new URLSearchParams({ | ||
doktyp: "votering", | ||
sortorder: "desc", | ||
utformat: "json", | ||
sok: search || "", | ||
sort: search ? "rel" : "datum", | ||
p: page?.toString() || "", | ||
}); | ||
for (const committe of committees) { | ||
query.append("org", committe); | ||
} | ||
|
||
const response = await fetch( | ||
`${PARLIAMENT_BASE_URL}/dokumentlista/?${query}`, | ||
{ | ||
next: { | ||
revalidate: 60 * 60, | ||
}, | ||
}, | ||
); | ||
|
||
const data = await response.json(); | ||
|
||
return parseVotes(data); | ||
} |
Oops, something went wrong.