Skip to content

Commit

Permalink
Show elections page and allow nominations to be supported/unsupported
Browse files Browse the repository at this point in the history
Signed-off-by: miam-miam100 <[email protected]>
  • Loading branch information
miam-miam committed Mar 1, 2024
1 parent a3d27b1 commit 6b54dd6
Show file tree
Hide file tree
Showing 9 changed files with 643 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/app/(pages)/elections/index.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@import "../../_css/common";

.login {
margin-bottom: var(--block-padding);
}

.params {
margin-top: var(--base);
}
77 changes: 77 additions & 0 deletions src/app/(pages)/elections/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React, { Fragment } from 'react'
import { Metadata } from 'next'
import { notFound } from 'next/navigation'
import qs from 'qs'

import { Election, Position } from '../../../payload/payload-types'
import { fetchSettings } from '../../_api/fetchGlobals'
import { Gutter } from '../../_components/Gutter'
import { CMSLink } from '../../_components/Link'
import { Positions } from '../../_components/Positions'
import { mergeOpenGraph } from '../../_utilities/mergeOpenGraph'

import classes from './index.module.scss'

function formatDate(dateString: string) {
const date = new Date(Date.parse(dateString))
return new Intl.DateTimeFormat('en-GB', {
dateStyle: 'long',
timeStyle: 'short',
}).format(date)
}

export default async function Elections() {
let elections: (Omit<Election, 'positions'> & { positions: Position[] })[] | null = null

const searchQuery = qs.stringify(
{
depth: 2,
sort: '-nominationStart',
},
{ encode: false },
)

try {
const req = await fetch(`${process.env.NEXT_PUBLIC_SERVER_URL}/api/elections?${searchQuery}`)

const json = await req.json()

const { docs } = json as { docs: (Election & { positions: Position[] })[] }

elections = docs
} catch (err) {
console.warn(err) // eslint-disable-line no-console
}

if (!elections) {
notFound()
}

return (
<Gutter className={classes.logout}>
{elections.map((election, index) => {
const { id, name, nominationStart, nominationEnd, votingStart, votingEnd, positions } =
election
return (
<Fragment key={id}>
<h1>{name}</h1>
<p>Nomination start: {formatDate(nominationStart)}</p>
<p>Nomination end: {formatDate(nominationEnd)}</p>
<p>Voting start: {formatDate(votingStart)}</p>
<p>Voting end: {formatDate(votingEnd)}</p>
<Positions positions={positions} electionId={id} />
</Fragment>
)
})}
</Gutter>
)
}

export const metadata: Metadata = {
title: 'Elections',
description: 'An overview of all upcoming and current elections',
openGraph: mergeOpenGraph({
title: 'Elections',
url: '/elections',
}),
}
106 changes: 106 additions & 0 deletions src/app/_components/Nominations/index.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
@import '../../_css/common';

.card {
border: 1px var(--theme-elevation-200) solid;
border-radius: 4px;
height: 100%;
display: flex;
flex-direction: column;
}

.vertical {
flex-direction: column;
}

.horizontal {
flex-direction: row;

&:local() {
.mediaWrapper {
width: 150px;

@include mid-break {
width: 100%;
}
}
}

@include mid-break {
flex-direction: column;
}
}

.content {
padding: var(--base);
flex-grow: 1;
display: flex;
flex-direction: column;
gap: calc(var(--base) / 2);

@include small-break {
padding: calc(var(--base) / 2);
gap: calc(var(--base) / 4);
}
}

.title {
margin: 0;
}

.titleLink {
text-decoration: none;
}

.centerAlign {
align-items: center;
}

.body {
flex-grow: 1;
}

.leader {
@extend %label;
display: flex;
gap: var(--base);
}

.description {
margin: 0;
}

.hideImageOnMobile {
@include mid-break {
display: none;
}
}

.mediaWrapper {
text-decoration: none;
display: block;
position: relative;
aspect-ratio: 16 / 9;
}

.image {
object-fit: cover;
}

.placeholder {
background-color: var(--theme-elevation-50);
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}

.actions {
display: flex;
align-items: center;

@include mid-break {
flex-direction: column;
align-items: flex-start;
}
}
79 changes: 79 additions & 0 deletions src/app/_components/Nominations/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, { Fragment, useEffect, useState } from 'react'
import Link from 'next/link'
import qs from 'qs'

import {
Election,
Media,
Nomination,
Position,
Sponsor,
User,
} from '../../../payload/payload-types'
import { Button } from '../Button'
import { Chevron } from '../Chevron'
import { Gutter } from '../Gutter'
import { CMSLinkType } from '../Link'
import { LinkList } from '../LinkList'
import { Media as MediaComp } from '../Media'
import RichText from '../RichText'
import { SupportNomination } from '../SupportNomination'

import classes from './index.module.scss'

export const Nominations: React.FC<{
positionId?: string
electionId?: string
}> = props => {
const { positionId, electionId } = props

let [nominations, setNominations] = useState<Nomination[] | null>(null)

useEffect(() => {
const getPositions = async (): Promise<Nomination[]> => {
const searchQuery = qs.stringify(
{
depth: 1,
where: {
and: [
{
droppedOut: { equals: false },
},
{ election: { equals: electionId } },
{ position: { equals: positionId } },
],
},
},
{ encode: false },
)

try {
const req = await fetch(
`${process.env.NEXT_PUBLIC_SERVER_URL}/api/nominations?${searchQuery}`,
)

const json = await req.json()

const { docs } = json as { docs: Nomination[] }
return docs
} catch (err) {
console.warn(err) // eslint-disable-line no-console
}
}
getPositions().then(setNominations)
}, [positionId, electionId])
return (
<div>
{nominations?.map((nomination, index) => {
const { id, supporters, populatedNominees, nickname } = nomination
const nomineeNames = populatedNominees.map(n => n.name).join(' ')
return (
<Fragment key={id}>
<h4>{nickname ?? nomineeNames}</h4>
<SupportNomination nominationId={id} supporters={supporters} />
</Fragment>
)
})}
</div>
)
}
106 changes: 106 additions & 0 deletions src/app/_components/Positions/index.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
@import '../../_css/common';

.card {
border: 1px var(--theme-elevation-200) solid;
border-radius: 4px;
height: 100%;
display: flex;
flex-direction: column;
}

.vertical {
flex-direction: column;
}

.horizontal {
flex-direction: row;

&:local() {
.mediaWrapper {
width: 150px;

@include mid-break {
width: 100%;
}
}
}

@include mid-break {
flex-direction: column;
}
}

.content {
padding: var(--base);
flex-grow: 1;
display: flex;
flex-direction: column;
gap: calc(var(--base) / 2);

@include small-break {
padding: calc(var(--base) / 2);
gap: calc(var(--base) / 4);
}
}

.title {
margin: 0;
}

.titleLink {
text-decoration: none;
}

.centerAlign {
align-items: center;
}

.body {
flex-grow: 1;
}

.leader {
@extend %label;
display: flex;
gap: var(--base);
}

.description {
margin: 0;
}

.hideImageOnMobile {
@include mid-break {
display: none;
}
}

.mediaWrapper {
text-decoration: none;
display: block;
position: relative;
aspect-ratio: 16 / 9;
}

.image {
object-fit: cover;
}

.placeholder {
background-color: var(--theme-elevation-50);
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}

.actions {
display: flex;
align-items: center;

@include mid-break {
flex-direction: column;
align-items: flex-start;
}
}
Loading

0 comments on commit 6b54dd6

Please sign in to comment.