Privacy Policy
- Last updated: Feb 18, 2023
+ Last updated: Apr 2, 2024
@@ -127,15 +127,13 @@ const Privacy = (): JSX.Element => {
Where do we store the data?
- We use MongoDB's{" "}
- Atlas {" "}
- 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.
How long do we store your data?
- 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.
Cookies
diff --git a/pages/recent-polls.tsx b/pages/recent-polls.tsx
index b2a8be7..be06c18 100644
--- a/pages/recent-polls.tsx
+++ b/pages/recent-polls.tsx
@@ -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";
@@ -145,7 +145,10 @@ const RecentPolls = (): JSX.Element => {
- {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")}
{
- const { pollURL, pollTitle, pollLocation, finalTime } = props;
+ const { pollURL, pollTitle, pollLocation, pollType, finalTime } = props;
const finalPollTitle = pollTitle || "Untitled";
const finalPollLocation = pollLocation ? `at ${pollLocation}` : "";
diff --git a/src/components/copyText/index.tsx b/src/components/copyText/index.tsx
index fb18e58..ce5f6c7 100644
--- a/src/components/copyText/index.tsx
+++ b/src/components/copyText/index.tsx
@@ -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 (
@@ -25,6 +26,7 @@ const CopyText = (props: {
pollURL={pollURL}
pollTitle={pollTitle}
pollLocation={pollLocation}
+ pollType={pollType}
finalTime={finalTime}
/>
diff --git a/src/components/poll/AdminPollInfo.tsx b/src/components/poll/AdminPollInfo.tsx
index 51179b3..4036d3d 100644
--- a/src/components/poll/AdminPollInfo.tsx
+++ b/src/components/poll/AdminPollInfo.tsx
@@ -22,12 +22,17 @@ const AdminPollInfo = (props: {
const { poll, showFinalTime, showCopyBox } = props;
return (
-
- {poll.open ? "Open" : "Closed"}
+ {(!poll.type || poll.type === "group") && (
+
+ {poll.open ? "Open" : "Closed"}
+
+ )}
+
+ {!poll.type || poll.type === "group" ? "Group" : "One-on-one"}
{poll.title && (
@@ -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
diff --git a/src/components/poll/MarkTimes.tsx b/src/components/poll/MarkTimes.tsx
index 1eb16e7..35f3af1 100644
--- a/src/components/poll/MarkTimes.tsx
+++ b/src/components/poll/MarkTimes.tsx
@@ -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;
}): JSX.Element => {
- const { times, newVote, setNewVote } = props;
+ const { times, newVote, poll, setNewVote } = props;
const [timeBoxStatus, setTimeBoxStatus] = useState>(
times.reduce((obj, cur) => ({ ...obj, [cur.start]: 0 }), {})
diff --git a/src/components/poll/MarkTimesOneOnOne.tsx b/src/components/poll/MarkTimesOneOnOne.tsx
new file mode 100644
index 0000000..8643146f
--- /dev/null
+++ b/src/components/poll/MarkTimesOneOnOne.tsx
@@ -0,0 +1,54 @@
+import { Form } from "react-bootstrap";
+import { Dispatch } from "react";
+import { Time, Vote, PollFromDB } from "../../models/poll";
+import { isTimePresentInPollTimes } from "../../helpers";
+
+const MarkTimesOneOnOne = (props: {
+ times: Time[];
+ newVote: Vote;
+ poll: PollFromDB;
+ setNewVote: Dispatch;
+}): JSX.Element => {
+ const { times, newVote, poll, setNewVote } = props;
+
+ let availableTimes = [];
+ let VotedTimes = poll.votes.map((vote) => vote.times[0]);
+
+ poll.times.map((time) => {
+ if (!isTimePresentInPollTimes(time, VotedTimes)) {
+ availableTimes.push(time);
+ }
+ });
+
+ const handleMarkTimeRadioButton = (
+ e: React.ChangeEvent
+ ): void => {
+ if (e.target !== e.currentTarget) return;
+ const { dataset, checked } = e.target;
+ const time: Time = dataset.value ? JSON.parse(dataset.value) : {};
+ let newTimes = [];
+
+ if (checked) {
+ newTimes.push(time);
+ setNewVote({ name: newVote.name, times: newTimes });
+ }
+ };
+
+ return (
+
+ {availableTimes.map((time) => (
+
+
+
+ ))}
+
+ );
+};
+
+export default MarkTimesOneOnOne;
diff --git a/src/components/poll/PollTableAdmin.tsx b/src/components/poll/PollTableAdmin.tsx
index eaf3391..171a4f2 100644
--- a/src/components/poll/PollTableAdmin.tsx
+++ b/src/components/poll/PollTableAdmin.tsx
@@ -43,9 +43,10 @@ const PollTableAdmin = (props: {
- {pollFromDB.open && (
-
- )}
+ {pollFromDB.open &&
+ (!pollFromDB.type || pollFromDB.type === "group") && (
+
+ )}
{pollFromDB.votes?.length}
diff --git a/src/components/poll/PollTableVoter.tsx b/src/components/poll/PollTableVoter.tsx
index d3aa43e..1bd0f7e 100644
--- a/src/components/poll/PollTableVoter.tsx
+++ b/src/components/poll/PollTableVoter.tsx
@@ -3,8 +3,10 @@ import { Table } from "react-bootstrap";
import dayjs from "dayjs";
import localizedFormat from "dayjs/plugin/localizedFormat";
import MarkTimes from "./MarkTimes";
+import MarkTimesOneOnOne from "./MarkTimesOneOnOne";
import PollDateTime from "./PollDateTime";
import { Time, PollFromDB, Vote } from "../../models/poll";
+import { isTimePresentInPollTimes } from "../../helpers";
dayjs.extend(localizedFormat);
@@ -16,23 +18,49 @@ const PollTableVoter = (props: {
}): JSX.Element => {
const { pollFromDB, sortedTimes, newVote, setNewVote } = props;
+ let availableTimes = [];
+ let votedTimes = pollFromDB.votes.map((vote) => vote.times[0]);
+
+ pollFromDB.times.map((time) => {
+ if (!isTimePresentInPollTimes(time, votedTimes)) {
+ availableTimes.push(time);
+ }
+ });
+
return (
- {sortedTimes.map((time) => (
-
-
-
- ))}
+ {(!pollFromDB.type || pollFromDB.type === "group") &&
+ sortedTimes.map((time) => (
+
+
+
+ ))}
+ {pollFromDB.type === "oneonone" &&
+ availableTimes.map((time) => (
+
+
+
+ ))}
- {pollFromDB.open && (
-
+ )}
+ {pollFromDB.open && pollFromDB.type === "oneonone" && (
+
)}
diff --git a/src/components/poll/SubmitFinalTime.tsx b/src/components/poll/SubmitFinalTime.tsx
index e382a0f..75c4ad2 100644
--- a/src/components/poll/SubmitFinalTime.tsx
+++ b/src/components/poll/SubmitFinalTime.tsx
@@ -4,15 +4,16 @@ import Router from "next/router";
import { ToastContainer, toast } from "react-toastify";
import toastOptions from "../../helpers/toastOptions";
import { markFinalTime } from "../../utils/api/server";
-import { Time } from "../../models/poll";
+import { Time, PollFromDB } from "../../models/poll";
import { encrypt } from "../../helpers";
const SubmitFinalTime = (props: {
finalTime: Time | undefined;
pollID: string;
secret: string;
+ poll: PollFromDB;
}): JSX.Element => {
- const { finalTime, pollID, secret } = props;
+ const { finalTime, pollID, secret, poll } = props;
const [disabled, setDisabled] = useState(false);
@@ -20,7 +21,7 @@ const SubmitFinalTime = (props: {
e: React.MouseEvent
): Promise => {
e.preventDefault();
- if (finalTime) {
+ if (finalTime && (!poll.type || poll.type === "group")) {
setDisabled(true);
try {
const voterArgs = {
@@ -44,37 +45,42 @@ const SubmitFinalTime = (props: {
toast.info("Please try again later", toastOptions);
Router.reload();
}
- } else {
+ } else if (!poll.type || poll.type === "group") {
toast.error("Please choose the final time", toastOptions);
+ } else {
+ setDisabled(true);
}
};
- return (
- <>
-
- {!disabled ? (
- `Finalise time`
- ) : (
- <>
-
- >
- )}
-
-
- >
- );
+ if (!poll.type || poll.type === "group") {
+ return (
+ <>
+
+ {!disabled ? (
+ `Finalise time`
+ ) : (
+ <>
+
+ >
+ )}
+
+
+ >
+ );
+ }
+ return ;
};
export default SubmitFinalTime;
diff --git a/src/components/poll/SubmitTimes.tsx b/src/components/poll/SubmitTimes.tsx
index f10d15c..cd56986 100644
--- a/src/components/poll/SubmitTimes.tsx
+++ b/src/components/poll/SubmitTimes.tsx
@@ -52,22 +52,49 @@ const SubmitTimes = (props: {
pollID,
};
submitTimeResponse = await markTimes(voterArgs);
+ let time = JSON.stringify(newVote.times[0]);
+
if (submitTimeResponse && submitTimeResponse.statusCode === 201) {
if (typeof window !== "undefined") {
const votedPolls = localStorage.getItem("samayVotedPolls");
if (!votedPolls) {
- const initSamayPolls = {
- polls: [
- {
- [`${pollID}`]: `${pollFromDB.title}`,
- },
- ],
- };
+ if (pollFromDB.type === "oneonone") {
+ const initSamayPolls = {
+ polls: [
+ {
+ [`${pollID}`]: `${pollFromDB.title}#${time}`,
+ },
+ ],
+ };
+
+ localStorage.setItem(
+ "samayVotedPolls",
+ JSON.stringify(initSamayPolls)
+ );
+ } else {
+ const initSamayPolls = {
+ polls: [
+ {
+ [`${pollID}`]: `${pollFromDB.title}`,
+ },
+ ],
+ };
+ localStorage.setItem(
+ "samayVotedPolls",
+ JSON.stringify(initSamayPolls)
+ );
+ }
+ } else if (pollFromDB.type === "oneonone") {
+ const votedPollsJSON = JSON.parse(votedPolls);
+
+ votedPollsJSON.polls.push({
+ [`${pollID}`]: `${pollFromDB.title}#${time}`,
+ });
localStorage.setItem(
"samayVotedPolls",
- JSON.stringify(initSamayPolls)
+ JSON.stringify(votedPollsJSON)
);
} else {
const votedPollsJSON = JSON.parse(votedPolls);
diff --git a/src/components/poll/VoterPollInfo.tsx b/src/components/poll/VoterPollInfo.tsx
index c58892e..b8ed989 100644
--- a/src/components/poll/VoterPollInfo.tsx
+++ b/src/components/poll/VoterPollInfo.tsx
@@ -8,7 +8,7 @@ import {
import dayjs from "dayjs";
import localizedFormat from "dayjs/plugin/localizedFormat";
import timezone from "dayjs/plugin/timezone";
-import { PollFromDB } from "../../models/poll";
+import { PollFromDB, Time } from "../../models/poll";
dayjs.extend(localizedFormat);
dayjs.extend(timezone);
@@ -16,18 +16,31 @@ dayjs.extend(timezone);
const PollInfo = (props: {
poll: PollFromDB;
showFinalTime: boolean;
- showVoteRecorded: boolean;
+ showVoteRecordedGroup: boolean;
+ showVoteRecordedOneOnOne: boolean;
+ votedTimeOneOnOne: Time;
}): JSX.Element => {
- const { poll, showFinalTime, showVoteRecorded } = props;
+ const {
+ poll,
+ showFinalTime,
+ showVoteRecordedGroup,
+ showVoteRecordedOneOnOne,
+ votedTimeOneOnOne,
+ } = props;
return (
-
- {poll.open ? "Open" : "Closed"}
+ {(!poll.type || poll.type === "group") && (
+
+ {poll.open ? "Open" : "Closed"}
+
+ )}
+
+ {!poll.type || poll.type === "group" ? "Group" : "One-on-one"}
{poll.title && (
)}
- {showVoteRecorded && !showFinalTime && (
+ {showVoteRecordedGroup && !showFinalTime && (
Your vote has been successfully recorded.
)}
+ {showVoteRecordedOneOnOne && votedTimeOneOnOne && !showFinalTime && (
+
+
+ Your vote for the time [{dayjs(votedTimeOneOnOne.start).format(
+ "LT"
+ )}{" "}
+ - {dayjs(votedTimeOneOnOne.end).format("LT")}] on{" "}
+ {dayjs(votedTimeOneOnOne.start).format("DD")}{" "}
+ {dayjs(votedTimeOneOnOne.start).format("MMM")} has been successfully
+ recorded.
+
+ )}
);
};
diff --git a/src/models/poll.ts b/src/models/poll.ts
index 2ef710b..d858c8b 100644
--- a/src/models/poll.ts
+++ b/src/models/poll.ts
@@ -30,6 +30,7 @@ export interface Poll {
open?: boolean;
secret: string;
location?: string;
+ type?: string;
times: Time[];
finalTime?: Time;
votes?: Vote[];
@@ -42,6 +43,7 @@ export interface PollFromDB {
open?: boolean;
secret: string;
location?: string;
+ type?: string;
times: TimeFromDB[];
finalTime?: TimeFromDB;
votes?: VoteFromDB[];
@@ -56,6 +58,7 @@ export interface PollDoc extends Document {
open?: boolean;
secret: string;
location?: string;
+ type?: string;
times: Time[];
finalTime?: Time;
votes?: Vote[];
@@ -68,6 +71,7 @@ const PollSchema: Schema = new Schema(
open: { type: Boolean, default: true },
secret: { type: String, required: true },
location: { type: String },
+ type: { type: String },
times: {
type: [{ start: Number, end: Number }],
required: true,
diff --git a/src/styles/form.scss b/src/styles/form.scss
index 9eb5378..7db9ea2 100644
--- a/src/styles/form.scss
+++ b/src/styles/form.scss
@@ -51,17 +51,48 @@
}
.form-select {
- background-color: #101827;
+ background-color: #ffffff;
color: #101827;
- border: none;
- outline: 0.05rem solid #e0e1e0;
- border-radius: 0.3rem;
+ border: 0.09rem solid #e5e7eb;
+ border-radius: 0.5rem;
font-family: "Inter";
font-size: 0.9rem;
- width: 8rem;
+ font-weight: 400;
+ margin-bottom: 1rem;
+ margin-top: 0;
+ transition: 0.2s;
+ padding-left: 0.5rem !important;
- &:focus {
- outline: 0.14rem solid #101827;
- background-color: #101827;
+ @include media-breakpoint-up(sm) {
+ margin-bottom: 0;
+ margin-top: 0;
+ }
+}
+
+.form-select:hover {
+ color: #101827;
+ background-color: #ffffff;
+ border: 0.09rem solid #b7b9bd;
+}
+
+.form-select:focus {
+ color: #101827;
+ background-color: #ffffff;
+ border: 0.1rem solid #101827;
+}
+
+.form-group {
+ margin-bottom: 0 !important;
+}
+
+.samay-form-col {
+ padding-right: 1rem;
+ padding-left: 1rem;
+
+ @include media-breakpoint-up(sm) {
+ padding-right: 0rem;
+ &.first-of-type {
+ padding-left: 2rem;
+ }
}
}
diff --git a/src/styles/navbar.scss b/src/styles/navbar.scss
index 91023c0..731851f 100644
--- a/src/styles/navbar.scss
+++ b/src/styles/navbar.scss
@@ -99,7 +99,7 @@
}
.beacon {
- background-color: #16a2b8;
+ background-color: #ff5f1f;
border-radius: 50%;
width: 0.35rem;
height: 0.35rem;
@@ -111,7 +111,7 @@
}
.beacon::before {
- background-color: #16a2b8;
+ background-color: #ff5f1f;
content: "";
top: calc(50% - 0.25rem);
left: calc(50% - 0.25rem);
@@ -141,5 +141,5 @@
}
.how-to-link-new-visitor {
- color: #16a2b8;
+ color: #ff5f1f;
}
diff --git a/src/styles/new-poll.scss b/src/styles/new-poll.scss
index 2f119d5..5a70df4 100644
--- a/src/styles/new-poll.scss
+++ b/src/styles/new-poll.scss
@@ -1,16 +1,14 @@
.new-poll-jumbo {
background-color: #fefffe;
color: #ffffff;
- padding-top: 1.5rem;
- padding-left: 1.5rem;
- padding-right: 1.5rem;
- padding-bottom: 1.5rem;
+ padding: 1rem;
margin-bottom: 5rem;
border-radius: 0.5rem;
border: 0.09rem solid #e5e7eb;
+
@include media-breakpoint-up(lg) {
margin-bottom: 2rem;
-}
+ }
}
.new-poll-timeslot-jumbo {
@@ -26,4 +24,4 @@
margin-bottom: 1.7rem;
border: 0.09rem solid #e5e7eb;
height: 37rem;
-}
\ No newline at end of file
+}
diff --git a/src/styles/poll.scss b/src/styles/poll.scss
index 0f17511..2ca6215 100644
--- a/src/styles/poll.scss
+++ b/src/styles/poll.scss
@@ -63,9 +63,11 @@
padding-bottom: 0.6em;
background-color: #f0fdf4;
color: #49de80;
+ border: 0.01rem solid #49de80;
transition: none;
font-size: 0.5rem;
letter-spacing: 0.02rem;
+ margin-right: 0.5rem;
@include media-breakpoint-up(sm) {
font-size: 0.6rem;
@@ -79,6 +81,26 @@
padding-right: 0.6em;
padding-bottom: 0.6em;
background-color: #1018272e;
+ border: 0.01rem solid #1018272e;
+ color: #101827;
+ transition: none;
+ font-size: 0.5rem;
+ letter-spacing: 0.02rem;
+ margin-right: 0.5rem;
+
+ @include media-breakpoint-up(sm) {
+ font-size: 0.6rem;
+ }
+}
+
+.poll-badge-polltype {
+ font-size: 0.6rem;
+ padding-top: 0.6em;
+ padding-left: 0.6em;
+ padding-right: 0.6em;
+ padding-bottom: 0.6em;
+ background-color: transparent;
+ border: 0.01rem solid #101827;
color: #101827;
transition: none;
font-size: 0.5rem;
@@ -343,6 +365,27 @@
accent-color: #101827;
}
+.poll-slot-checkbox-one-on-one {
+ width: 1.5rem;
+ margin: 0 auto;
+}
+
+.poll-slot-checkbox-one-on-one input[type="radio"] {
+ width: 1.5rem;
+ height: 1.5rem;
+ appearance: none;
+ border-radius: 50%;
+ background-clip: content-box;
+ border: 0.15rem solid #b6b6b6;
+ background-color: white;
+}
+
+.poll-slot-checkbox-one-on-one input[type="radio"]:checked {
+ background-color: #49de80;
+ padding: 4px;
+ border: 0.15rem solid #49de80;
+}
+
.copy-text-desktop {
display: none;