Skip to content

Commit

Permalink
Resolves issue #3, #6, and more (unlisted) (#10)
Browse files Browse the repository at this point in the history
* rename create to createNewRoom for clarity

* rename create to createNewRoom for clarity

* improve UI to support mobile

* modify little things

* modify README.md
  • Loading branch information
fruitiecutiepie authored Sep 19, 2023
1 parent 0a7a291 commit 348928b
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 116 deletions.
8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ RUN rm -rf src/web/package.json node_modules src/server/node_modules && \

# Hack for greatly reducing the size of the image. For some reason I couldn't find
# a cleaner way to do this.
RUN \
rm ./node_modules/.prisma/client/libquery_engine-linux-musl.so.node && \
rm ./node_modules/prisma/libquery_engine-linux-musl.so.node && \
rm ./node_modules/@prisma/engines/libquery_engine-linux-musl.so.node
# RUN \
# rm ./node_modules/.prisma/client/libquery_engine-linux-musl.so.node && \
# rm ./node_modules/prisma/libquery_engine-linux-musl.so.node && \
# rm ./node_modules/@prisma/engines/libquery_engine-linux-musl.so.node

FROM node:16.17.0-alpine

Expand Down
73 changes: 71 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,79 @@
# Club Voting System
# ClubVotingSystem

The Club Voting system was developed in the runup to the 2022 AGM for the Programmers' Society. Since then it has been continually updated and improved.
## About
ClubVotingSystem lets you do real-time voting in a safe and controlled environment. It was initially developed in 2022 for the Annual General Meeting (AGM) for UTS Programmers' Society.

## Features
- [x] Responsive UI
- [x] Realtime vote counting and display
- [x] User facing dashboard
- [x] Admin dashboard
- [x] Anonymous voting

## Use Cases
1. Committee election in Annual & Special General Meetings (AGMs & SGMs)
2. Voting where memberships are required

## Limitation
You can only vote & ask one question at a time.

## Get Started
### Prerequisites
Install [Docker](https://www.docker.com/)

### Steps
1. Clone the repo
```
git clone https://github.com/ProgSoc/ClubVotingSystem.git
cd ClubVotingSystem
```

2. Install dependencies
```
yarn install
```

3. Run database on Docker
```
docker-compose up -d
```

4. Run client & server on localhost
```
yarn web dev
yarn server dev
```

## How to Contribute
1. Pick your favourite issue (or make one)
2. Make a comment saying you'll work on it
3. Wait for the issue to be assigned to you
4. Fork the repo
5. Commit your changes with the issue number (e.g., issue #2)
6. Make a pull request
7. Wait for approval
1. If approved: You're done!
2. Else: See comments

## Project Structure
```
project-root/
├── .gitignore
├── .dockerignore
├── Dockerfile
├── docker-compose.yml
├── package.json
├── ...
└── src/
├── server/
| ├── src/
│ ├── package.json
│ └── ...
└── web/
├── src/
├── index.html
├── package.json
├── tailwind.config.cjs
└── ...
```
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ services:
- 8080:8080
environment:
PORT: 8080
DATABASE_URL: postgres://postgres:psqlpass@postgre:5432/voting
DATABASE_URL: postgres://postgres:psqlpass@postgres:5432/voting
# volumes:

postgre:
postgres:
image: postgres
restart: unless-stopped
environment:
Expand Down
2 changes: 1 addition & 1 deletion src/server/src/routers/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { withRoomVoterFunctions } from '../room/interaction/user';
import { publicProcedure, router } from '../trpc';

export const roomRouter = router({
create: publicProcedure
createNewRoom: publicProcedure
.input(
z.object({
name: z.string().min(1),
Expand Down
2 changes: 1 addition & 1 deletion src/web/src/components/styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const PageContainer = ({ children, className }: BaseProps) => (
);

export const Heading = ({ children, className }: BaseProps) => (
<h1 className={twMerge('text-4xl font-bold', className)}>{children}</h1>
<h1 className={twMerge('text-2xl md:text-4xl font-bold', className)}>{children}</h1>
);

export const Question = ({ children, className }: BaseProps) => (
Expand Down
6 changes: 3 additions & 3 deletions src/web/src/pages/CreateRoomPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function CreateRoomPage() {
const [pageName, setPageName] = useState('');
const navigate = useNavigate();

const mutation = trpc.room.create.useMutation();
const mutation = trpc.room.createNewRoom.useMutation();

const onSubmit = async () => {
const result = await mutation.mutateAsync({ name: pageName.trim() });
Expand All @@ -24,15 +24,15 @@ export function CreateRoomPage() {
return (
<CenteredPageContainer className="gap-4">
<Heading>Create a new room</Heading>
<fieldset disabled={disabled} className="gap-2 w-full flex sm:flex-row flex-col justify-center items-center">
<fieldset disabled={disabled} className="gap-2 w-full flex flex-col justify-center items-center">
<input
className="input input-bordered w-full sm:w-96"
type="text"
value={pageName}
onChange={(e) => setPageName(e.target.value)}
/>
<Button
className="btn btn-primary"
className="btn btn-primary m-3"
disabled={invalid || disabled}
onClick={onSubmit}
isLoading={mutation.isLoading}
Expand Down
60 changes: 36 additions & 24 deletions src/web/src/pages/room/BoardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,11 @@ import { trpc } from 'utils/trpc';

export function BoardPage(props: { roomId: string; room: RoomPublicInfo }) {
return (
<CenteredPageContainer className="gap-4">
<div className="lg:hidden text-error">This page is not mobile friendly lol</div>
<Heading className="text-accent">{props.room.name}</Heading>
<div className="w-full flex flex-row items-center">
<div className="w-1/2 flex flex-row-reverse text-right pr-8">
<JoinPanel room={props.room} />
</div>
<div className="w-1/2 flex flex-row text-left pl-8">
<StatusPanel room={props.room} />
</div>
<CenteredPageContainer>
<Heading className="text-accent mb-8">{props.room.name}</Heading>
<div className="flex flex-col lg:flex-row items-center gap-24">
<JoinPanel room={props.room} />
<StatusPanel room={props.room} />
</div>
</CenteredPageContainer>
);
Expand All @@ -29,21 +24,31 @@ function JoinPanel(props: { room: RoomPublicInfo; className?: string }) {
const joinLink = location.origin + routeBuilders.shortJoin({ shortId: props.room.shortId });
const boardLink = location.origin + routeBuilders.shortView({ shortId: props.room.shortId });
return (
<div className={twMerge('flex flex-col items-end gap-4 bg-base-300 p-8 rounded-2xl shadow-lg', props.className)}>
<Heading>Join voting Room</Heading>
<QRCodeRender content={joinLink} />
{joinLink && (
<p>
<a target="_blank" rel="noreferrer" href={joinLink} className="text-3xl underline text-info font-mono">
{joinLink}
</a>
</p>
)}
<div className="gap-2">
<Heading className="text-2xl">View board</Heading>
<div
className={twMerge('flex flex-col items-center md:bg-base-300 shadow-lg px-8 py-10 md:p-10 pt-1 md:pt-5 rounded-2xl', props.className)}
>
<div
className="flex flex-col gap-5 py-10 items-center"
>
<Heading
className="text-3xl md:text-4xl pb-3"
>Join voting room</Heading>
<QRCodeRender content={joinLink} />
{joinLink && (
<p>
<a target="_blank" rel="noreferrer" href={boardLink} className="text-xl underline text-info font-mono">
<a target="_blank" rel="noreferrer" href={joinLink} className="text-sm md:text-xl underline text-info font-mono">
{joinLink}
</a>
</p>
)}
</div>
<div
className="flex flex-col gap-2 items-center"
>
<Heading className="text-2xl md:text-3xl">View board</Heading>
{joinLink && (
<p>
<a target="_blank" rel="noreferrer" href={boardLink} className="text-sm md:text-xl underline text-info font-mono">
{boardLink}
</a>
</p>
Expand Down Expand Up @@ -73,7 +78,14 @@ function StatusPanel(props: { room: RoomPublicInfo }) {
}

return BoardState.match(state, {
blank: () => <Heading>Waiting for a question...</Heading>,
blank: () => (
<div
className="flex flex-col"
>
<Heading>Waiting for a question...</Heading>

</div>
),

showingQuestion: (state) => (
<div className="flex flex-col gap-4">
Expand Down
62 changes: 33 additions & 29 deletions src/web/src/pages/room/JoinWaitingRoomPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,36 +54,40 @@ export function JoinWaitingRoomPage(props: { roomId: string }) {
{(form) => (
<Form>
<div className="gap-4 w-full flex flex-col justify-center items-center">
<Field
className="input input-bordered w-full sm:w-96"
placeholder="Student Email (firstname.lastname)"
type="email"
name="studentEmail"
value={form.values.studentEmail}
onChange={form.handleChange}
/>
<div className="flex items-center justify-center gap-4">
<label className="flex items-center gap-2">
<Field type="radio" name="location" value={UserLocation.InPerson} className="radio radio-primary" />
<span className="label-text">{locationEnumLabel[UserLocation.InPerson]}</span>
</label>
<label className="flex items-center gap-2">
<Field type="radio" name="location" value={UserLocation.Online} className="radio radio-primary" />
<span className="label-text">{locationEnumLabel[UserLocation.Online]}</span>
</label>
<label className="flex items-center gap-2">
<Field type="radio" name="location" value={UserLocation.Proxy} className="radio radio-primary" />
<span className="label-text">{locationEnumLabel[UserLocation.Proxy]}</span>
</label>
</div>
<Button
className="btn-primary w-32"
type="submit"
disabled={disabled || Object.values(form.errors).length > 0}
isLoading={mutation.isLoading}
<div
className="flex flex-col gap-4"
>
Join
</Button>
<Field
className="input input-bordered w-full sm:w-96 text-sm md:text-base"
placeholder="Student Email (firstname.lastname)"
type="email"
name="studentEmail"
value={form.values.studentEmail}
onChange={form.handleChange}
/>
<div className="flex items-start justify-center gap-4">
<label className="flex items-center gap-2">
<Field type="radio" name="location" value={UserLocation.InPerson} className="radio radio-primary" />
<span className="label-text text-xs md:text-sm">{locationEnumLabel[UserLocation.InPerson]}</span>
</label>
<label className="flex items-center gap-2">
<Field type="radio" name="location" value={UserLocation.Online} className="radio radio-primary" />
<span className="label-text text-xs md:text-sm">{locationEnumLabel[UserLocation.Online]}</span>
</label>
<label className="flex items-center gap-2">
<Field type="radio" name="location" value={UserLocation.Proxy} className="radio radio-primary" />
<span className="label-text text-xs md:text-sm">{locationEnumLabel[UserLocation.Proxy]}</span>
</label>
</div>
<Button
className="btn-primary w-28 self-center m-3"
type="submit"
disabled={disabled || Object.values(form.errors).length > 0}
isLoading={mutation.isLoading}
>
Join
</Button>
</div>
</div>
</Form>
)}
Expand Down
24 changes: 14 additions & 10 deletions src/web/src/pages/room/admin/QuestionSettingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ function SetQuestion({ data }: { data: QuestionSettingData }) {
};

return (
<>
<div
className="flex flex-col lg:flex-row items-center gap-24"
>
<Formik<FormValues>
initialValues={{
question: '',
Expand All @@ -170,10 +172,12 @@ function SetQuestion({ data }: { data: QuestionSettingData }) {
};

return (
<Form>
<Form
className="flex flex-col m-8 md:bg-base-300 shadow-lg md:p-10 md:pt-5 rounded-2xl"
>
<fieldset disabled={submitting} className="gap-4 w-full flex flex-col justify-center items-center">
<Field
className="input input-bordered w-full sm:w-96"
className="input input-bordered input-primary w-full text-center self-start my-8"
placeholder="Question"
name="question"
value={form.values.question}
Expand Down Expand Up @@ -231,7 +235,7 @@ function SetQuestion({ data }: { data: QuestionSettingData }) {
</div>

<Button
className="w-32 btn-primary"
className="w-32 btn-primary m-5"
type="submit"
disabled={submitting || Object.values(form.errors).length > 0}
isLoading={submitting}
Expand All @@ -244,15 +248,15 @@ function SetQuestion({ data }: { data: QuestionSettingData }) {
}}
</Formik>
{data.previousResults && (
<div className="mt-8 flex flex-col items-center">
<Heading className="mb-2">Previous results</Heading>
<Question>{data.previousResults.question}</Question>
<div className="mt-2">
<ResultsViewer results={data.previousResults.results} />
<div>
<Heading>Previous results</Heading>
<div className="mt-8 flex flex-col gap-8 items-center">
<Question>{data.previousResults.question}</Question>
<ResultsViewer results={data.previousResults.results} />
</div>
</div>
)}
</>
</div>
);
}

Expand Down
12 changes: 9 additions & 3 deletions src/web/src/pages/room/admin/RoomInfoPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,19 @@ export function RoomInfoPage(props: { roomId: string; room: RoomPublicInfo; admi

<div className="flex flex-col items-center gap-4">
<div>
<Heading>View board</Heading>
<a target="_blank" rel="noreferrer" href={boardLink} className="text-2xl underline text-info font-mono">
<Heading
className="text-2xl md:text-3xl m-3"
>View board</Heading>
<a target="_blank" rel="noreferrer" href={boardLink} className="text-sm md:text-xl underline text-info font-mono">
{boardLink}
</a>
</div>
<a target="_blank" rel="noreferrer" href={routeBuilders.viewRoomResults({ roomId: props.room.id })}>
<Button>View Results</Button>
<Button
className="btn btn-accent m-3"
>
View Results
</Button>
</a>
</div>
</PageContainer>
Expand Down
Loading

0 comments on commit 348928b

Please sign in to comment.