diff --git a/.env.local b/.env.local index 27d0224d..cfb8016d 100644 --- a/.env.local +++ b/.env.local @@ -1,9 +1,9 @@ NEXT_PUBLIC_APPINSIGHTS_INSTRUMENTATIONKEY=2f1d6a9c-1b88-4f3d-bba1-64b11ffd2362 -NEXT_PUBLIC_GET_SUBMISSIONS_URL=https://dddperth-functions-test.azurewebsites.net/api/GetSubmissions -NEXT_PUBLIC_SUBMIT_VOTE_URL=https://dddperth-functions-test.azurewebsites.net/api/SubmitVote -NEXT_PUBLIC_GET_AGENDA_URL=https://dddperth-functions-test.azurewebsites.net/api/GetAgenda -NEXT_PUBLIC_SUBMIT_FEEDBACK_URL=https://dddperth-functions-test.azurewebsites.net/api/SubmitFeedback +NEXT_PUBLIC_GET_SUBMISSIONS_URL=https://dddmelb-2024.azurewebsites.net/api/GetSubmissions +NEXT_PUBLIC_SUBMIT_VOTE_URL=https://dddmelb-2024.azurewebsites.net/api/SubmitVote +NEXT_PUBLIC_GET_AGENDA_URL=https://dddmelb-2024.azurewebsites.net/api/GetAgenda +NEXT_PUBLIC_SUBMIT_FEEDBACK_URL=https://dddmelb-2024.azurewebsites.net/api/SubmitFeedback NEXT_PUBLIC_TESTING_MODE=false NEXT_PUBLIC_BASE_URL=https://www.dddmelbourne.com -NEXT_PUBLIC_ELO_PAIR=https://dddperth-functions-test.azurewebsites.net/api/EloVotingGetPair -NEXT_PUBLIC_ELO_VOTE=https://dddperth-functions-test.azurewebsites.net/api/EloVotingSubmitPair +NEXT_PUBLIC_ELO_PAIR=https://dddmelb-2024.azurewebsites.net/api/EloVotingGetPair +NEXT_PUBLIC_ELO_VOTE=https://dddmelb-2024.azurewebsites.net/api/EloVotingSubmitPair diff --git a/.github/workflows/azure-static-web-apps-happy-river-0aceb3600.yml b/.github/workflows/azure-static-web-apps-happy-river-0aceb3600.yml index d48ec91c..058ae799 100644 --- a/.github/workflows/azure-static-web-apps-happy-river-0aceb3600.yml +++ b/.github/workflows/azure-static-web-apps-happy-river-0aceb3600.yml @@ -6,8 +6,6 @@ on: - main pull_request: types: [opened, synchronize, reopened, closed] - branches: - - melb-2024 jobs: build_and_deploy_job: diff --git a/components/Voting/EloVote.styled.tsx b/components/Voting/EloVote.styled.tsx index 10895834..d96855eb 100644 --- a/components/Voting/EloVote.styled.tsx +++ b/components/Voting/EloVote.styled.tsx @@ -24,6 +24,7 @@ export const StyledEloVoteContainer = styled('div') marginInlineStart: 'auto', marginInlineEnd: 'auto', maxBlockSize: '65vh', + minHeight: '70vh', }), ({ variant }) => ({ [breakpointMax('md')]: { diff --git a/components/Voting/landing.styled.tsx b/components/Voting/landing.styled.tsx index ca66c246..50400130 100644 --- a/components/Voting/landing.styled.tsx +++ b/components/Voting/landing.styled.tsx @@ -2,6 +2,8 @@ import styled from '@emotion/styled' import { Button } from 'components/global/Button/Button' import { Text } from 'components/global/text' import { calcRem } from 'components/utils/styles/calcRem' +import { DialogContent } from '@reach/dialog' +import { breakpointMax } from '../utils/styles/breakpoints' export const StyledLandingContainer = styled('div')(() => ({ inlineSize: '100%', @@ -29,4 +31,31 @@ export const StyledButton = styled(Button)({ display: 'block', marginInlineStart: 'auto', marginInlineEnd: 'auto', + marginTop: calcRem(20), +}) + +export const StyledOverlayButtons = styled('div')(() => ({ + padding: calcRem(20), + '*:first-of-type': { + marginRight: calcRem(20), + }, +})) + +export const StyledForm = styled('form')(() => ({ + padding: `${calcRem(20)} 0`, + label: { + display: 'inline-block', + width: 100, + }, + input: '100%', +})) + +export const StyledFormRow = styled('div')({ + marginBottom: calcRem(30), +}) + +export const StyledDialogContent = styled(DialogContent)({ + [breakpointMax('sm')]: { + width: '100%', + }, }) diff --git a/config/actions.ts b/config/actions.ts index 05282b15..f42d8539 100644 --- a/config/actions.ts +++ b/config/actions.ts @@ -11,14 +11,6 @@ export default function getConferenceActions(conference: Conference, dates: Date }) } - if (dates.VotingOpen) { - actions.push({ - Category: 'voting', - Title: 'Vote for agenda', - Url: '/vote', - }) - } - if (dates.RegistrationOpen) { actions.push({ Category: 'tickets', @@ -27,6 +19,14 @@ export default function getConferenceActions(conference: Conference, dates: Date }) } + if (dates.VotingOpen) { + actions.push({ + Category: 'voting', + Title: 'Vote for agenda', + Url: '/vote', + }) + } + if (dates.RegistrationOpen) { actions.push({ Category: 'training', diff --git a/config/conference.ts b/config/conference.ts index bb01de63..c1c4041d 100644 --- a/config/conference.ts +++ b/config/conference.ts @@ -28,7 +28,7 @@ const registrationOpenWave2From = zonedTimeToUtc('2023-10-15T08:00:00', tz) const registrationOpenUntil = null const presentationSubmissionsOpenFrom = zonedTimeToUtc('2023-09-01T08:00:00', tz) const presentationSubmissionsOpenUntil = zonedTimeToUtc('2023-11-01T23:59:59', tz) -const votingOpenFrom = zonedTimeToUtc('2023-11-08T17:00:00', tz) +const votingOpenFrom = zonedTimeToUtc('2023-11-08T00:00:00', tz) const votingOpenUntil = zonedTimeToUtc('2023-11-20T23:59:59', tz) const agendaPublishedFrom = zonedTimeToUtc('2023-12-01T17:00:00', tz) const feedbackOpenFrom = toDate(date) diff --git a/pages/vote/elo.tsx b/pages/vote/elo.tsx index 22f194d9..b1350814 100644 --- a/pages/vote/elo.tsx +++ b/pages/vote/elo.tsx @@ -52,6 +52,8 @@ async function postPair(winningSessionId: string, losingSessionId: string, isDra LoserSessionId: losingSessionId, IsDraw: isDraw, VoterSessionId: getSessionId(), + VoterTicket: Cookies.get('vote-ticket'), + VoterLastname: Cookies.get('vote-lastname'), } try { @@ -66,11 +68,29 @@ async function postPair(winningSessionId: string, losingSessionId: string, isDra } } -export default function Elo({ sessions, votingSessionId, userDefinedLayout = 'stacked' }: EloProps): JSX.Element { +export default function Elo({ sessions, votingSessionId, userDefinedLayout = 'expanded' }: EloProps): JSX.Element { const { conference } = useConfig() const [sessionPair, setSessionPair] = useState(sessions) const [nextPair, setNextPair] = useState(undefined) const [layoutVariant, setLayoutVariant] = useState(userDefinedLayout) + const [loading, setLoading] = useState(false) + + const spinner = + '\n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' + + ' \n' useEffect(() => { // the cookie is used for reloads, it is read in `getServerSideProps` @@ -87,8 +107,10 @@ export default function Elo({ sessions, votingSessionId, userDefinedLayout = 'st useEffect(() => { async function getPair() { + setLoading(true) const data = await fetchPair(votingSessionId) setNextPair(data) + setLoading(false) } if (typeof nextPair === 'undefined') { @@ -97,6 +119,7 @@ export default function Elo({ sessions, votingSessionId, userDefinedLayout = 'st }, [next, nextPair, votingSessionId]) async function sessionChoiceHandler(winningSession: EloSession, losingSession: EloSession, isDraw = false) { + setLoading(true) await postPair(winningSession.Id, losingSession.Id, isDraw) logEvent('voting', 'vote', { variant: layoutVariant, @@ -127,7 +150,6 @@ export default function Elo({ sessions, votingSessionId, userDefinedLayout = 'st onSessionChoice={sessionChoiceHandler} layout={layoutVariant} /> - It's a Draw! @@ -160,6 +184,7 @@ export default function Elo({ sessions, votingSessionId, userDefinedLayout = 'st window.scrollTo(0, 0) sessionChoiceHandler(sessionPair.SubmissionB, sessionPair.SubmissionA, false) }} + disabled={loading} > Option 2 @@ -173,6 +198,11 @@ export default function Elo({ sessions, votingSessionId, userDefinedLayout = 'st /> Change layout? {layoutVariant === 'stacked' ? 'Expand abstracts' : 'Stack talks'} +
{loading && }
+ + Keep voting for as many talks as you like. You can leave and come back any time until the closing date and + your votes will be saved. +
) @@ -189,7 +219,7 @@ export const getServerSideProps: GetServerSideProps = async (context) const validLayouts: LayoutVariant[] = ['expanded', 'stacked'] const userDefinedLayout: LayoutVariant = [...(validLayouts as string[])].includes(layoutCookie) ? (layoutCookie as LayoutVariant) - : 'stacked' + : 'expanded' if (!dates.VotingOpen) { return { notFound: true } diff --git a/pages/vote/landing.tsx b/pages/vote/landing.tsx index 1881596c..23995409 100644 --- a/pages/vote/landing.tsx +++ b/pages/vote/landing.tsx @@ -3,11 +3,25 @@ import { Main } from 'layouts/main' import { GetServerSideProps } from 'next' import { getCommonServerSideProps } from 'components/utils/getCommonServerSideProps' import { Text } from 'components/global/text' -import { useRouter } from 'next/router' import { PRIVACY_ACCEPTED } from '../../components/Voting/VoteConst' import Cookies from 'js-cookie' -import { StyledButton, StyledHeader, StyledIntro, StyledLandingContainer } from '../../components/Voting/landing.styled' +import { + StyledButton, + StyledDialogContent, + StyledForm, + StyledFormRow, + StyledHeader, + StyledIntro, + StyledLandingContainer, + StyledOverlayButtons, +} from '../../components/Voting/landing.styled' import { formatInTimeZone } from 'date-fns-tz' +import React, { FormEvent, Fragment } from 'react' +import { DialogOverlay } from '@reach/dialog' +import { Button, ButtonAnchor } from '../../components/global/Button/Button' +import '@reach/dialog/styles.css' +import Link from 'next/link' +import { useRouter } from 'next/router' type VoteLandingProps = { instance: string @@ -16,46 +30,113 @@ type VoteLandingProps = { const BUTTON_LABEL = 'Start Voting!' -export default function VoteLanding({ instance, votingFinished }: VoteLandingProps): JSX.Element { +// export default function VoteLanding({ instance, votingFinished }: VoteLandingProps): JSX.Element { +export default function VoteLanding({ instance }: VoteLandingProps): JSX.Element { const { conference } = useConfig() const router = useRouter() - function onClickHandler() { + function onSubmitForm(e: FormEvent) { + e.preventDefault() + const formData = new FormData(e.target as HTMLFormElement) + Cookies.set(PRIVACY_ACCEPTED, 'true', { expires: 90 }) + Cookies.set('vote-ticket', formData.get('ticket'), { expires: 90 }) + Cookies.set('vote-lastname', formData.get('lastname'), { expires: 90 }) router.push(`/vote/voting`) } - return ( -
- - {`${instance} Conference Voting`} - Here's how voting works: - - You'll be presented with a couple of talk options. Have a read of the abstract and simply select the talk - which sounds the best to you based on your interests. If you really can't pick between the two, simply choose - "It's a draw!". - - - Once you've made your selection, two new options will appear. You can continue to vote on the options - presented for as long as you like - every vote will count towards formulating the best agenda possible for - this year. - - - Voting closes on {votingFinished}, so you have between now and then to have your say. You can leave and come - back any time until the closing day to get your votes in. - - Happy Voting! + const [showDialog, setShowDialog] = React.useState(true) + const [showBuyTicket, setShowBuyTicket] = React.useState(false) + const [showBoughtTicket, setShowBoughtTicket] = React.useState(false) + const close = () => setShowDialog(false) - - By selecting '{BUTTON_LABEL}' I have read and accepted the{' '} - DDDPerth Privacy statement. - + return ( + + + + {!showBuyTicket && !showBoughtTicket && ( + +

Have you bought your ticket to DDD Melbourne?

- - {BUTTON_LABEL} - -
-
+ + + + + + )} + {showBuyTicket && ( + + + Did you know that ticket holder votes count more? You can buy your ticket{' '} + here for only {conference.TicketPrice}. + + + + Get Ticket + + + + + )} + {showBoughtTicket && ( + + Wonderful! Enjoy voting! :) + + + )} + + +
+ + {`${instance} Conference Voting`} + Here's how voting works: +
    +
  1. Enter your {conference.Name} ticket number and last name, then press ‘Start voting!’.
  2. +
  3. + No ticket? Just press ‘Start voting!’ to begin. (Buying a ticket helps your + vote count more!) +
  4. +
  5. + You’ll see two talks next. Read the session information and pick your favourite of the two. If you can’t + decide, choose “It’s a draw!” +
  6. +
  7. Once you’ve made your selection, you’ll get two new talks to pick from.
  8. +
  9. + Keep voting for as many talks as you like. You can leave and come back any time until the closing date and + your votes will be saved. +
  10. +
+

+ + Voting closes on{' '} + {formatInTimeZone(conference.VotingOpenUntil, conference.TimeZone, "iiii, d MMMM 'at' hh:mma")} + +

+ + + + + + + + + + Ticket number example + + {BUTTON_LABEL} + + +
+
+ ) } diff --git a/public/static/voting/ticket-example.png b/public/static/voting/ticket-example.png new file mode 100644 index 00000000..1b9d932b Binary files /dev/null and b/public/static/voting/ticket-example.png differ