Skip to content

Commit

Permalink
Merge pull request #208 from swsrkty/add-limted-participation
Browse files Browse the repository at this point in the history
feat: one-on-one poll
  • Loading branch information
anandbaburajan authored Apr 2, 2024
2 parents e21f780 + e3bcf25 commit 115348f
Show file tree
Hide file tree
Showing 22 changed files with 408 additions and 118 deletions.
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,17 @@

Samay is a free and open source meeting poll tool. Quickly find a time which works for everyone without the back-and-forth texts/emails!

- Create a meeting poll by choosing the time slots based on your availability.
- Copy and share the poll link with the participants to let them mark their availability.
- Find the most popular times and see who's free with "yes" votes - or who can be - with "if need be" votes, and book the meeting!
> #### Create a poll
>
> Select the time slots (click and drag) based on your availability, and optionally enter the title, description and location. The default poll type is "group" — to find a common time which works for everyone. If you want to have one-on-one meetings (parent-teacher meetings for example), select the "one-on-one" poll type.
>
> #### Share the poll
>
> Copy and share the poll link with the participants to let them mark their availability. In group polls, participants can either vote [yes] or [if need be] . In one-on-one polls, participants can select their one preferred time. No login required. No time zone confusion since Samay automatically shows participants times in their local time zone.
>
> #### Book the meeting
>
> In group polls, find the most popular times and see who's free with [yes] votes - or who can be - with [if need be] votes, book the meeting and share the final time with the participants! In one-on-one polls, find who has chosen which time slot for a one-on-one with you!
Create a poll now at [Samay.app](https://samay.app/)!

Expand Down
43 changes: 31 additions & 12 deletions pages/how-to.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,15 @@ const HowTo = (): JSX.Element => {
1. Create a poll
</Card.Title>
<Card.Text className="how-to-card desc">
Quickly create a meeting poll by choosing the time slots based
on your availability, and optionally enter the title,
description and location.
Select the time slots (click and drag) based on your
availability, and optionally enter the title, description and
location.
<br />
<br />
The default poll type is "group" — to find a common time which
works for everyone. If you want to have one-on-one meetings
(parent-teacher meetings for example), select the "one-on-one"
poll type.
</Card.Text>
</Card.Body>
</Card>
Expand All @@ -80,12 +86,20 @@ const HowTo = (): JSX.Element => {
</Card.Title>
<Card.Text className="how-to-card desc">
Copy and share the poll link with the participants to let them
mark their availability using &nbsp;
<CheckCircleFill className="how-to-card icon-yes" /> (yes) or{" "}
<CircleFill className="how-to-card icon-if-need-be" /> (if
need be) votes. No login required. No time zone confusion
since Samay automatically shows participants times in their
local time zone.
mark their availability.
<br />
<br />
In group polls, participants can either vote [yes]{" "}
<CheckCircleFill className="how-to-card icon-yes" /> or [if
need be]{" "}
<CircleFill className="how-to-card icon-if-need-be" />. In
one-on-one polls, participants can select their one preferred
time.
<br />
<br />
No login required. No time zone confusion since Samay
automatically shows participants times in their local time
zone.
</Card.Text>
</Card.Body>
</Card>
Expand All @@ -97,9 +111,14 @@ const HowTo = (): JSX.Element => {
3. Book the meeting
</Card.Title>
<Card.Text className="how-to-card desc">
Find the most popular times and see who's free with "yes"
votes - or who can be - with "if need be" votes, and book the
meeting!
In group polls, find the most popular times and see who's free
with [yes] votes - or who can be - with [if need be] votes,
book the meeting and share the final time with the
participants!
<br />
<br />
In one-on-one polls, find who has chosen which time slot for a
one-on-one with you!
</Card.Text>
</Card.Body>
</Card>
Expand Down
29 changes: 26 additions & 3 deletions pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const Home = (): JSX.Element => {
pollDescription: "",
});

const [pollType, setPollType] = useState("group");

const { pollTitle, pollLocation, pollDescription } = pollDetails;

const [pollTimes, setTimes] = useState<Time[]>([]);
Expand All @@ -46,6 +48,12 @@ const Home = (): JSX.Element => {
});
};

const handlePollTypeChange = (
e: React.ChangeEvent<HTMLSelectElement>
): void => {
setPollType(e.target.value);
};

const handleSubmit = async (
e: React.MouseEvent<HTMLInputElement>
): Promise<void> => {
Expand All @@ -63,6 +71,7 @@ const Home = (): JSX.Element => {
title: pollTitle,
description: pollDescription,
location: pollLocation,
type: pollType,
secret: encryptedSecret,
times: pollTimes,
};
Expand Down Expand Up @@ -164,7 +173,7 @@ const Home = (): JSX.Element => {
</Jumbotron>
<Jumbotron className="new-poll-jumbo">
<Row>
<Col sm>
<Col sm className="samay-form-col">
<Form.Control
className="form-text"
type="text"
Expand All @@ -174,7 +183,7 @@ const Home = (): JSX.Element => {
onChange={handlePollDetailsChange}
/>
</Col>
<Col sm>
<Col sm className="samay-form-col">
<Form.Control
className="form-text"
type="text"
Expand All @@ -184,7 +193,7 @@ const Home = (): JSX.Element => {
onChange={handlePollDetailsChange}
/>
</Col>
<Col sm>
<Col sm className="samay-form-col">
<Form.Control
className="form-text"
type="text"
Expand All @@ -194,6 +203,20 @@ const Home = (): JSX.Element => {
onChange={handlePollDetailsChange}
/>
</Col>
<Col sm className="samay-form-col">
<Form.Group className="form-group">
<Form.Control
as="select"
className="form-select"
name="pollType"
defaultValue="group"
onChange={handlePollTypeChange}
>
<option value="group">Group poll</option>
<option value="oneonone">One-on-one poll</option>
</Form.Control>
</Form.Group>
</Col>
<Col sm="auto">
<Button
className="global-primary-button"
Expand Down
19 changes: 15 additions & 4 deletions pages/poll/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ const Poll = (props: {
times: [],
});

let showVoteRecorded = false;
let showVoteRecordedGroup = false;
let showVoteRecordedOneOnOne = false;
let votedTimeOneOnOne = null;

const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
const { value } = e.target;
Expand Down Expand Up @@ -70,7 +72,14 @@ const Poll = (props: {

if (Object.keys(poll)[0] === pollID && pollFromDB.open) {
pageSection = <></>;
showVoteRecorded = true;
if (!pollFromDB.type || pollFromDB.type === "group") {
showVoteRecordedGroup = true;
} else {
votedTimeOneOnOne = JSON.parse(
Object.values(poll)[0].split("#")[1]
);
showVoteRecordedOneOnOne = true;
}
break;
} else if (pollFromDB.open) {
pageSection = (
Expand Down Expand Up @@ -136,7 +145,7 @@ const Poll = (props: {
return (
<>
<Head>
<title>Samay — Mark your availability</title>
<title>Samay — mark your availability</title>
<link rel="shortcut icon" href="/favicon.svg" />
<meta charSet="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
Expand All @@ -148,7 +157,9 @@ const Poll = (props: {
<VoterPollInfo
poll={pollFromDB}
showFinalTime={!pollFromDB.open}
showVoteRecorded={showVoteRecorded}
showVoteRecordedGroup={showVoteRecordedGroup}
showVoteRecordedOneOnOne={showVoteRecordedOneOnOne}
votedTimeOneOnOne={votedTimeOneOnOne}
/>
</Jumbotron>
{pageSection}
Expand Down
4 changes: 2 additions & 2 deletions pages/poll/[id]/[secret].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import Layout from "../../../src/components/Layout";
import AdminPollInfo from "../../../src/components/poll/AdminPollInfo";
import PollTableAdmin from "../../../src/components/poll/PollTableAdmin";
import SubmitFinalTime from "../../../src/components/poll/SubmitFinalTime";
import DeletePoll from "../../../src/components/poll/DeletePoll";
import { Time, TimeFromDB, PollFromDB } from "../../../src/models/poll";
import { decrypt } from "../../../src/helpers";

Expand All @@ -29,7 +28,7 @@ const Poll = (props: {
return (
<>
<Head>
<title>Samay — Finalise time</title>
<title>Samay — finalise time</title>
<link rel="shortcut icon" href="/favicon.svg" />
<meta charSet="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
Expand All @@ -54,6 +53,7 @@ const Poll = (props: {
finalTime={finalTime}
pollID={pollID}
secret={secret}
poll={pollFromDB}
/>
)}
</>
Expand Down
12 changes: 5 additions & 7 deletions pages/privacy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const Privacy = (): JSX.Element => {
<Container className="global-container">
<Jumbotron className="privacy-jumbo">
<h1>Privacy Policy</h1>
<p>Last updated: Feb 18, 2023</p>
<p>Last updated: Apr 2, 2024</p>
<hr />
<br />
<p>
Expand Down Expand Up @@ -127,15 +127,13 @@ const Privacy = (): JSX.Element => {
</p>
<h3>Where do we store the data?</h3>
<p>
We use MongoDB's{" "}
<a href="https://www.mongodb.com/legal/privacy-policy">Atlas</a>{" "}
cloud database service to store all the events and your
availability information.
We use MongoDB's Atlas cloud database service to store all the
events and your availability information.
</p>
<h3>How long do we store your data?</h3>
<p>
We will delete every poll 1 month after the event associated
with the poll is over.
We will keep your polls indefinitely. You have the option to
delete your polls from the recent polls page.
</p>
<h3>Cookies</h3>
<p>
Expand Down
7 changes: 5 additions & 2 deletions pages/recent-polls.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Card, Container, Button } from "react-bootstrap";
import Modal from "react-bootstrap/Modal";
import { Grid, BoxArrowUpRight, Trash } from "react-bootstrap-icons";
import { Grid, Trash } from "react-bootstrap-icons";
import Router from "next/router";
import { useState } from "react";
import Head from "next/head";
Expand Down Expand Up @@ -145,7 +145,10 @@ const RecentPolls = (): JSX.Element => {
<Card className="your-polls-poll-card">
<Card.Body>
<Card.Title>
{Object.values(poll)[0] || "Untitled"}
{Object.values(poll)[0].includes("#{") &&
(Object.values(poll)[0].split("#{")[0] || "Untitled")}
{!Object.values(poll)[0].includes("#{") &&
(Object.values(poll)[0] || "Untitled")}
<div className="card-options">
<Button
className="trash-button"
Expand Down
3 changes: 2 additions & 1 deletion src/components/copyText/CopyTextMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ const CopyTextMain = (props: {
pollURL: string;
pollTitle: string;
pollLocation: string;
pollType: string;
finalTime: Time | undefined;
}): JSX.Element => {
const { pollURL, pollTitle, pollLocation, finalTime } = props;
const { pollURL, pollTitle, pollLocation, pollType, finalTime } = props;

const finalPollTitle = pollTitle || "Untitled";
const finalPollLocation = pollLocation ? `at ${pollLocation}` : "";
Expand Down
4 changes: 3 additions & 1 deletion src/components/copyText/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ const CopyText = (props: {
pollID: string;
pollTitle: string;
pollLocation: string;
pollType: string;
finalTime: Time | undefined;
}): JSX.Element => {
const { pollID, pollTitle, pollLocation, finalTime } = props;
const { pollID, pollTitle, pollLocation, pollType, finalTime } = props;
const pollURL = `${NEXT_PUBLIC_BASE_URL}/poll/${pollID}`;

return (
Expand All @@ -25,6 +26,7 @@ const CopyText = (props: {
pollURL={pollURL}
pollTitle={pollTitle}
pollLocation={pollLocation}
pollType={pollType}
finalTime={finalTime}
/>
</Form>
Expand Down
19 changes: 13 additions & 6 deletions src/components/poll/AdminPollInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@ const AdminPollInfo = (props: {
const { poll, showFinalTime, showCopyBox } = props;
return (
<div>
<Badge
pill
variant={poll.open ? "success" : "secondary"}
className={poll.open ? "poll-badge-open" : "poll-badge-closed"}
>
{poll.open ? "Open" : "Closed"}
{(!poll.type || poll.type === "group") && (
<Badge
pill
variant={poll.open ? "success" : "secondary"}
className={poll.open ? "poll-badge-open" : "poll-badge-closed"}
>
{poll.open ? "Open" : "Closed"}
</Badge>
)}
<Badge pill variant="secondary" className="poll-badge-polltype">
{!poll.type || poll.type === "group" ? "Group" : "One-on-one"}
</Badge>
{poll.title && (
<span
Expand Down Expand Up @@ -78,6 +83,7 @@ const AdminPollInfo = (props: {
pollTitle={poll.title}
pollID={poll._id}
pollLocation={poll.location}
pollType={poll.type}
finalTime={poll.finalTime}
/>
</span>
Expand All @@ -92,6 +98,7 @@ const AdminPollInfo = (props: {
pollTitle={poll.title}
pollID={poll._id}
pollLocation={poll.location}
pollType={poll.type}
finalTime={poll.finalTime}
/>{" "}
with the participants
Expand Down
5 changes: 3 additions & 2 deletions src/components/poll/MarkTimes.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Dispatch, useState } from "react";
import { CheckCircleFill, CircleFill } from "react-bootstrap-icons";
import { Time, Vote } from "../../models/poll";
import { Time, Vote, PollFromDB } from "../../models/poll";

const MarkTimes = (props: {
times: Time[];
newVote: Vote;
poll: PollFromDB;
setNewVote: Dispatch<Vote>;
}): JSX.Element => {
const { times, newVote, setNewVote } = props;
const { times, newVote, poll, setNewVote } = props;

const [timeBoxStatus, setTimeBoxStatus] = useState<Record<number, number>>(
times.reduce((obj, cur) => ({ ...obj, [cur.start]: 0 }), {})
Expand Down
Loading

0 comments on commit 115348f

Please sign in to comment.