diff --git a/src/app.html b/src/app.html index 63cff7a..97c3539 100644 --- a/src/app.html +++ b/src/app.html @@ -2,7 +2,10 @@ - + => { + let response = undefined; + try { + response = await this.fetch(this.baseUrl + this.endpoint + endpoint, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + ...headers + }, + body: JSON.stringify(data) + }); + } catch (error) { + throw new APIError(response?.status, response?.statusText, (error as Error).message); + } + + return this.parseResponse(response, parseReturn); + }; } diff --git a/src/lib/api/events.ts b/src/lib/api/events.ts index e7f9596..b66a9f8 100644 --- a/src/lib/api/events.ts +++ b/src/lib/api/events.ts @@ -6,10 +6,13 @@ export type EventCreateRequestData = { electoral_system: string; }; +export type EventStatus = 'RE' | 'VO' | 'CL'; + export type EventResponseData = { id: number; name: string; choices: string[]; + status: EventStatus; electoral_system: string; share_token: string; show_results: boolean; @@ -68,13 +71,27 @@ export class EventsAPI extends BaseAPI { 'X-API-Key': shareToken }) as Promise; }; + + updateStatus = async (eventID: number, token: string, status: EventStatus) => { + return this.patch( + `/${eventID}/update-status`, + { status }, + { + 'X-API-Key': token + } + ); + }; } export class BallotAPI extends BaseAPI { endpoint: string = '/vote/ballot'; submitBallot = async (ballotID: number, token: string, submission: unknown) => { - return this.post(`/${ballotID}/submit`, { vote: submission }, { 'X-API-Key': token }); + return this.post( + `/${ballotID}/submit`, + { vote: submission }, + { 'X-API-Key': token } + ) as Promise; }; getBallot = async (ballotID: number, token: string) => { diff --git a/src/lib/components/EventForm.svelte b/src/lib/components/EventForm.svelte index 2683a62..4d96ca0 100644 --- a/src/lib/components/EventForm.svelte +++ b/src/lib/components/EventForm.svelte @@ -45,7 +45,7 @@ storage.saveEvent(response.id, response.name, response.host_token); - await goto(resolve(`/event/${response.id}/host/invitation/`)); + await goto(resolve(`/event/${response.id}`)); }; diff --git a/src/lib/components/VotingWrapper.svelte b/src/lib/components/VotingWrapper.svelte index 522f682..b09c354 100644 --- a/src/lib/components/VotingWrapper.svelte +++ b/src/lib/components/VotingWrapper.svelte @@ -3,7 +3,7 @@ import { setContext } from 'svelte'; import votingSystems from '$lib/voting-system/config'; import type { SubmissionContext } from '$lib/types'; - import { BallotAPI, type EventResponseData } from '$lib/api/events'; + import { BallotAPI, type BallotResponseData, type EventResponseData } from '$lib/api/events'; import { ExclamationCircleOutline } from 'flowbite-svelte-icons'; const { @@ -15,7 +15,7 @@ ballotID: number; event: EventResponseData; token: string; - onSubmitVote: () => void; + onSubmitVote: (value: BallotResponseData) => void; } = $props(); const config = $derived(votingSystems.find((value) => value.id === event.electoral_system)); const submissionContext: SubmissionContext = $state({ @@ -28,9 +28,8 @@ const submitVote = async () => { const ballotAPI = new BallotAPI(); - - await ballotAPI.submitBallot(ballotID, token, submissionContext.submission); - onSubmitVote(); + const ballot = await ballotAPI.submitBallot(ballotID, token, submissionContext.submission); + onSubmitVote(ballot); }; diff --git a/src/lib/voting-system/components/plurality/PluralityResults.svelte b/src/lib/voting-system/components/plurality/PluralityResults.svelte index bbdfc59..d536ace 100644 --- a/src/lib/voting-system/components/plurality/PluralityResults.svelte +++ b/src/lib/voting-system/components/plurality/PluralityResults.svelte @@ -13,7 +13,7 @@ ); -{#each votes as vote (vote.choice)} +{#each votes.sort((a, b) => b.count - a.count) as vote (vote.choice)}

{vote.choice}: {vote.count}

diff --git a/src/lib/voting-system/components/plurality/PluralityVoting.svelte b/src/lib/voting-system/components/plurality/PluralityVoting.svelte index 630d13b..5fd5f9f 100644 --- a/src/lib/voting-system/components/plurality/PluralityVoting.svelte +++ b/src/lib/voting-system/components/plurality/PluralityVoting.svelte @@ -15,7 +15,7 @@ }); -

Select your top pick.

+

Select your top pick.

{#each event.choices as choice (choice)} diff --git a/src/lib/voting-system/components/ranked-choice/RankedChoiceResults.svelte b/src/lib/voting-system/components/ranked-choice/RankedChoiceResults.svelte index 21c235c..a7c7d29 100644 --- a/src/lib/voting-system/components/ranked-choice/RankedChoiceResults.svelte +++ b/src/lib/voting-system/components/ranked-choice/RankedChoiceResults.svelte @@ -2,6 +2,7 @@ import type { ResultComponentProps } from '$lib/voting-system/types'; import type { BallotResponseData, EventResponseData } from '$lib/api/events'; import type { RankedSubmission } from './types'; + import { P } from 'flowbite-svelte'; let { event, ballots }: ResultComponentProps = $props(); @@ -80,19 +81,19 @@ }); -

+

{#if overallWinner === '__TIE_FLAG__'} It's a tie! {:else} {overallWinner} wins! {/if} -

+

-
+
{#each roundData as voteCount, i (i)} -
-
Round {i + 1}
-
+
+
Round {i + 1}
+
{#each Object.keys(voteCount) as candidate (candidate)}
{candidate}: {voteCount[candidate]}
{/each} @@ -100,30 +101,3 @@
{/each}
- - diff --git a/src/lib/voting-system/components/ranked-choice/RankedChoiceVoting.svelte b/src/lib/voting-system/components/ranked-choice/RankedChoiceVoting.svelte index eaa2d69..b3d7a05 100644 --- a/src/lib/voting-system/components/ranked-choice/RankedChoiceVoting.svelte +++ b/src/lib/voting-system/components/ranked-choice/RankedChoiceVoting.svelte @@ -5,6 +5,7 @@ import type { SubmissionContext } from '$lib/types'; import type { VotingComponentProps } from '$lib/voting-system/types'; import type { RankedSubmission } from './types'; + import { P } from 'flowbite-svelte'; let { event }: VotingComponentProps = $props(); @@ -34,16 +35,20 @@ }); -

Drag your preferred options to the top.

+

Drag your preferred options to the top.

{#each items as item, index (item.id)} -
-
+
+
{index + 1}.
@@ -52,24 +57,3 @@
{/each}
- - diff --git a/src/lib/voting-system/components/star/StarResults.svelte b/src/lib/voting-system/components/star/StarResults.svelte index a11923e..e3e2921 100644 --- a/src/lib/voting-system/components/star/StarResults.svelte +++ b/src/lib/voting-system/components/star/StarResults.svelte @@ -202,19 +202,19 @@ {#each totalRatings.sort(sortRatings) as vote (vote.choice)} -
+

{vote.choice} - {vote.total}

{#if firstPlace && vote.choice === firstPlace.winner} -

(Winner) {firstPlace.reasoning}

+

(Winner)

{/if} {#if secondPlace && vote.choice === secondPlace.winner} -

(Second Place) {secondPlace.reasoning}

+

(Second Place)

{/if} {#if thirdPlace && vote.choice === thirdPlace.winner} -

(Third Place) {thirdPlace.reasoning}

+

(Third Place)

{/if}
{/each} diff --git a/src/lib/voting-system/components/star/StarVoting.svelte b/src/lib/voting-system/components/star/StarVoting.svelte index 7fef5c8..8951c17 100644 --- a/src/lib/voting-system/components/star/StarVoting.svelte +++ b/src/lib/voting-system/components/star/StarVoting.svelte @@ -26,9 +26,7 @@ }); -

- Rate each item from 0-5 stars. You may rate multiple items with the same number of stars. -

+

Rate each item from 0-5 stars. You may rate multiple items with the same number of stars.

{#each event.choices as choice (choice)} @@ -37,14 +35,6 @@

{choice}

- {#if ratingObj.rating > 0} - - {/if} {#each Array(5), index (index)} {/each} + {#if ratingObj.rating > 0} + + {/if}
{/if} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 375c4ba..e8980b4 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -27,14 +27,12 @@ (loaded = true)}> {#if !loaded} -
+

Loading...

{:else} -
-
- {@render children?.()} -
+
+ {@render children?.()}
{/if} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 6468ff7..2c43d90 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -3,15 +3,17 @@ import { Heading, P, Hr } from 'flowbite-svelte'; -Vote The Bowl +
+ Vote The Bowl -

- Make your cook off voting easy! Create an event and invite others to vote. You don't need an - account just start by creating an event below and sharing the registration link or QR-code. You - can choose the voting system that best suits your needs (more on the way). -

+

+ Make your cook off voting easy! Create an event and invite others to vote. You don't need an + account just start by creating an event below and sharing the registration link or QR-code. You + can choose the voting system that best suits your needs (more on the way). +

-
+
-Create an Event - + Create an Event + +
diff --git a/src/routes/event/[id=eventID]/+page.svelte b/src/routes/event/[id=eventID]/+page.svelte new file mode 100644 index 0000000..a9c433a --- /dev/null +++ b/src/routes/event/[id=eventID]/+page.svelte @@ -0,0 +1,50 @@ + + +
+ {#if event?.status == 'RE'} + + {:else if event?.status == 'VO'} + + {:else if event?.status == 'CL'} + + {:else} + Loading... + {/if} +
diff --git a/src/routes/event/[id=eventID]/ballot/[ballotID=eventID]/+layout.svelte b/src/routes/event/[id=eventID]/ballot/[ballotID=eventID]/+layout.svelte deleted file mode 100644 index 8adfa00..0000000 --- a/src/routes/event/[id=eventID]/ballot/[ballotID=eventID]/+layout.svelte +++ /dev/null @@ -1,53 +0,0 @@ - - -{@render children()} diff --git a/src/routes/event/[id=eventID]/ballot/[ballotID=eventID]/+page.svelte b/src/routes/event/[id=eventID]/ballot/[ballotID=eventID]/+page.svelte new file mode 100644 index 0000000..2b0c036 --- /dev/null +++ b/src/routes/event/[id=eventID]/ballot/[ballotID=eventID]/+page.svelte @@ -0,0 +1,58 @@ + + +
+ {#if ballot && event?.status == 'RE'} + {event.name} +

+ Thanks for registering for {event.name}. Sit tight and wait for the event to start. +

+ {:else if ballot && event?.status == 'VO'} + + {:else if ballot && event?.status == 'CL'} + + {:else} + Loading... + {/if} +
diff --git a/src/routes/event/[id=eventID]/ballot/[ballotID=eventID]/components/results.svelte b/src/routes/event/[id=eventID]/ballot/[ballotID=eventID]/components/results.svelte new file mode 100644 index 0000000..a4173bf --- /dev/null +++ b/src/routes/event/[id=eventID]/ballot/[ballotID=eventID]/components/results.svelte @@ -0,0 +1,25 @@ + + +{event.name} +Results +

+ This event has concluded. Thank you for participating {ballot.voter_name}! +

+{#if event.show_results} + +{/if} diff --git a/src/routes/event/[id=eventID]/ballot/[ballotID=eventID]/components/voting.svelte b/src/routes/event/[id=eventID]/ballot/[ballotID=eventID]/components/voting.svelte new file mode 100644 index 0000000..75a034c --- /dev/null +++ b/src/routes/event/[id=eventID]/ballot/[ballotID=eventID]/components/voting.svelte @@ -0,0 +1,33 @@ + + +{event.name} +{ballot.voter_name}'s Ballot +
+ {#if ballot.submitted !== null} +

Thank you for submitting your vote!

+ {:else} + + {/if} +
diff --git a/src/routes/event/[id=eventID]/ballot/[ballotID=eventID]/vote/+page.svelte b/src/routes/event/[id=eventID]/ballot/[ballotID=eventID]/vote/+page.svelte deleted file mode 100644 index f9d80e2..0000000 --- a/src/routes/event/[id=eventID]/ballot/[ballotID=eventID]/vote/+page.svelte +++ /dev/null @@ -1,53 +0,0 @@ - - -Voting - {name} -
- {#if !eventContext.event} -

Loading event data...

- {:else if submitted} - {#if eventContext.event?.show_results === true} - - {:else} -

You have already submitted your vote. Thank you!

- {/if} - {:else} - - {/if} -
diff --git a/src/routes/event/[id=eventID]/components/registration.svelte b/src/routes/event/[id=eventID]/components/registration.svelte new file mode 100644 index 0000000..3aeec73 --- /dev/null +++ b/src/routes/event/[id=eventID]/components/registration.svelte @@ -0,0 +1,78 @@ + + +
+ Registration ({event.name}) + + + + Registered Voters ({ballotCount}) + {#if ballots == null} +

Loading...

+ {:else if ballots.length == 0} +

No voters have registered yet.

+ {:else} +
    + {#each ballots as ballot (ballot.id)} +
  • {ballot.voter_name}
  • + {/each} +
+ {/if} +
+ + diff --git a/src/routes/event/[id=eventID]/components/results.svelte b/src/routes/event/[id=eventID]/components/results.svelte new file mode 100644 index 0000000..d6e3ce2 --- /dev/null +++ b/src/routes/event/[id=eventID]/components/results.svelte @@ -0,0 +1,42 @@ + + +
+ Results ({event.name}) + + + {#if config} + {#if ballots} + + {/if} + {:else} + Config Error! + {/if} +
diff --git a/src/routes/event/[id=eventID]/components/voting.svelte b/src/routes/event/[id=eventID]/components/voting.svelte new file mode 100644 index 0000000..d57cd22 --- /dev/null +++ b/src/routes/event/[id=eventID]/components/voting.svelte @@ -0,0 +1,55 @@ + + +Voting ({event.name}) + +{#if unsubmittedBallots !== undefined && submittedBallots !== undefined} + Active ({unsubmittedBallots.length}) +
    + {#each unsubmittedBallots as ballot (ballot.id)} +
  • {ballot.voter_name}
  • + {/each} +
+ Submitted ({submittedBallots.length}) +
    + {#each submittedBallots as ballot (ballot.id)} +
  • {ballot.voter_name}
  • + {/each} +
+ + {#if submittedBallots.length === 0 && unsubmittedBallots.length === 0} +

No ballots available.

+ {/if} +{:else} +

Loading ballots...

+{/if} + +
+ + +
diff --git a/src/routes/event/[id=eventID]/host/+layout.svelte b/src/routes/event/[id=eventID]/host/+layout.svelte deleted file mode 100644 index 3f3f78e..0000000 --- a/src/routes/event/[id=eventID]/host/+layout.svelte +++ /dev/null @@ -1,84 +0,0 @@ - - -
- {@render children?.()} -
- - - - - - - - - { - confirmationModalOpen = true; - }} - > - - - - - -
- -

- Are you sure you want to close voting to this event? -

-
- - -
-
-
diff --git a/src/routes/event/[id=eventID]/host/dashboard/+page.svelte b/src/routes/event/[id=eventID]/host/dashboard/+page.svelte deleted file mode 100644 index 5d33474..0000000 --- a/src/routes/event/[id=eventID]/host/dashboard/+page.svelte +++ /dev/null @@ -1,61 +0,0 @@ - - -Dashboard - -{#if unsubmittedBallots.length > 0} - Unsubmitted Ballots -
    - {#each unsubmittedBallots as ballot (ballot.id)} -
  • {ballot.voter_name}
  • - {/each} -
-{/if} - -{#if submittedBallots.length > 0} - Submitted Ballots -
    - {#each submittedBallots as ballot (ballot.id)} -
  • {ballot.voter_name}
  • - {/each} -
-{/if} - -{#if submittedBallots.length === 0 && unsubmittedBallots.length === 0} -

No ballots available.

-{/if} diff --git a/src/routes/event/[id=eventID]/host/invitation/+page.svelte b/src/routes/event/[id=eventID]/host/invitation/+page.svelte deleted file mode 100644 index 95260f0..0000000 --- a/src/routes/event/[id=eventID]/host/invitation/+page.svelte +++ /dev/null @@ -1,87 +0,0 @@ - - -Event Invitation -
- {#if hostContext.event} - - {/if} - - - -
- Registered Voters - -
- {#if hostContext.ballots.length > 0} -
    - {#each hostContext.ballots as ballot (ballot.id)} -
  • {ballot.voter_name}
  • - {/each} -
- {:else} -

No voters have registered yet.

- {/if} -
diff --git a/src/routes/event/[id=eventID]/host/results/+page@[id=eventID].svelte b/src/routes/event/[id=eventID]/host/results/+page@[id=eventID].svelte deleted file mode 100644 index 515089a..0000000 --- a/src/routes/event/[id=eventID]/host/results/+page@[id=eventID].svelte +++ /dev/null @@ -1,62 +0,0 @@ - - -Results -
- {#if eventContext.event} - - - - - - {/if} -
diff --git a/src/routes/event/[id=eventID]/host/type.ts b/src/routes/event/[id=eventID]/host/type.ts deleted file mode 100644 index 16dc9eb..0000000 --- a/src/routes/event/[id=eventID]/host/type.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { BallotResponseData, EventResponseData } from '$lib/api/events'; - -export type HostContext = { ballots: BallotResponseData[]; event: EventResponseData | null }; diff --git a/src/routes/register/+page.svelte b/src/routes/register/+page.svelte index c633ab4..a8de8df 100644 --- a/src/routes/register/+page.svelte +++ b/src/routes/register/+page.svelte @@ -35,7 +35,7 @@ } storage.saveBallot(response.ballot_id, data.id, response.ballot_token); - await goto(resolve(`/event/${data.id}/ballot/${response.ballot_id}/vote`)); + await goto(resolve(`/event/${data.id}/ballot/${response.ballot_id}/`)); }; onMount(async () => { @@ -46,7 +46,7 @@ try { const ballotID = storage.getBallotIDFromEventID(data.id); - await goto(resolve(`/event/${data.id}/ballot/${ballotID}/vote/`)); + await goto(resolve(`/event/${data.id}/ballot/${ballotID}/`)); } catch (e) { if (!(e instanceof ErrorBallotWithEventIDNotFound)) { throw e; @@ -58,33 +58,36 @@ }); -{#if loading === false} - Register for {data.name} -
- {#if error} - - {error} - - {/if} -
- - { - error = null; - }} - required - /> -
+
+ {#if loading === false} + {data.name} + Register + + {#if error} + + {error} + + {/if} +
+ + { + error = null; + }} + required + /> +
-
- -
- -{:else if loading === true} - Loading... -{/if} +
+ +
+ + {:else if loading === true} + Loading... + {/if} +