first commit

This commit is contained in:
2023-09-10 21:02:36 +03:00
commit 565e9c9804
34 changed files with 19186 additions and 0 deletions
+76
View File
@@ -0,0 +1,76 @@
import { useState, useEffect, useRef, useContext } from "react";
import classNames from "classnames";
const Answers = (props) => {
const { poll, user, socket, id, pid } = props;
const [winner, setWinner] = useState(0);
const token = localStorage.getItem(id)
const onVote = async (answer) => {
const data = { user:user, answer:answer, pid:pid, token:token }
socket.emit("vote", data);
};
useEffect(() => {
var res = poll.users.reduce(function (obj, v) {
obj[v.vote] = (obj[v.vote] || 0) + 1;
return obj;
}, {})
const arr = Object.values(res);
const max = Math.max(...arr)
setWinner(max)
}, [poll])
const countVotes = (index) => {
let count = 0
poll.users.filter(function (item) {
if (item.vote === index) {
count = count + 1
}
})
return (
<div className={`w-2/12 py-2 text-white text-2xl text-center rounded-l-lg ${winner === count ? 'bg-lime-500' : 'bg-black bg-opacity-40'} `}>
{count}
</div>
)
}
const buttonCN = classNames(`w-10/12 text-center bg-white w`)
return (
<div>
{poll.answers.map((answer, index) => (
<div key={index} className="flex flex-col my-8">
<div className="flex flex-row">
{countVotes(index)}
<button className="w-10/12 p-2 text-center align-middle bg-white rounded-r-lg drop-shadow-lg" onClick={() => onVote(index)} >
{answer}
</button>
</div>
<div className="mt-2">
{
poll.users.map((user, i) => {
if (user.name != props.user && poll.anonymous) {
return null
}
if (index === user.vote) {
return (
<div
key={i}
className={` text-white text-sm float-left py-1 px-2 mr-2 mb-2 rounded-md ${user.name === props.user ? 'bg-blue-500 bg-opacity-100' : 'bg-black bg-opacity-40'}`}
>
{user.name === props.user && <span className="mr-2">&#10026;</span>}
{user.name}
</div>)
}
})
}
</div>
</div>
))}
</div>
);
}
export default Answers
+159
View File
@@ -0,0 +1,159 @@
import { useNavigate } from "react-router-dom";
import { useEffect, useState, useMemo } from "react";
import { Button } from '../ui/button';
import { Title } from '../ui/title';
import { Input } from '../ui/input';
import { Checkbox } from '../ui/checkbox';
const Create = (props) => {
const {socket, io } = props;
const navigate = useNavigate();
const [disabled, setDisabled] = useState(false);
const [formData, setFormData] = useState({
title: "",
answers: ["", ""],
anonymous: false
});
useEffect(() => {
if (
formData.answers.indexOf("") === -1 &&
formData.answers.length > 1 &&
formData.title
) {
setDisabled(false);
} else {
setDisabled(true);
}
}, [formData]);
const handleInput = (e) => {
setFormData((prevState) => ({
...prevState,
[e.target.name]: e.target.value,
}));
};
const handleAnswers = (e, i) => {
const newArr = [...formData.answers];
newArr[i] = e.target.value;
setFormData((prevState) => ({
...prevState,
answers: newArr,
}));
};
const addAnswer = (e) => {
e.preventDefault();
const newArr = [...formData.answers];
newArr.push("");
setFormData((prevState) => ({
...prevState,
answers: newArr,
}));
};
const delAnswer = (index) => {
const newArr = [...formData.answers];
newArr.splice(index, 1);
setFormData((prevState) => ({
...prevState,
answers: newArr,
}));
}
const handleAnonymous = () => {
setFormData((prevState) => ({
...prevState,
anonymous: !prevState.anonymous,
}));
}
const handlePublish = () => {
const poll = {
// id: memoid,
title: formData.title,
answers: formData.answers,
anonymous: formData.anonymous
};
console.log('CREATE handlePublish poll: ', poll)
socket.emit("create", poll);
};
return (
<div>
<form>
<div className="mb-8">
<Title variant={1} label="Create Poll" />
</div>
<div className="">
<Title variant={2} label="Question" />
<Input
name="title"
getRef={(ref) => handleInput(ref.current)}
placeholder="Title"
value={formData.title}
onChange={handleInput}
/>
</div>
<div className="mt-10">
<Title variant={2} label="Answers" />
{formData.answers.map((option, i) => (
<div key={i} className="flex">
<div className="w-11/12">
<Input
id={option}
name={option}
getRef={(ref) => handleInput(ref.current)}
value={formData.answers[i]}
onChange={(e) => handleAnswers(e, i)}
/>
</div>
{i > 1 && <div className="w-1/12 flex">
<button
type='button'
onClick={() => delAnswer(i)}
className="text-red-500 text-2xl leading-10 font-extrabold ml-4"
>
&#10005;
</button>
</div>}
</div>
))}
<div className="text-right pr-16">
<button
disabled={formData.answers.length > 100 ? true : false}
onClick={(e) => addAnswer(e)}
className="text-green-400"
>
&#43; Add answer
</button>
</div>
</div>
<div className="mt-4 ml-4 w-full">
<Checkbox
id='anon'
label="Don't show voters names"
onChange={handleAnonymous}
/>
</div>
<div className="mt-2">
<Button
disabled={disabled}
label={disabled ? 'Fill the form to publish the poll' : 'Publish the poll'}
onClick={handlePublish}
big={true}
/>
</div>
</form>
</div>
);
}
export default Create
+36
View File
@@ -0,0 +1,36 @@
import { useNavigate } from "react-router-dom";
import { Title } from "../ui/title";
import { Button } from "../ui/button";
import { IconPlus } from "../ui/icons";
const Home = () => {
const navigate = useNavigate();
return (
<div className="flex flex-col">
<Title variant={1} label="Lets make a poll real fast!" />
<div className="text-white my-8">
<p><span className="mr-4">&#9734;</span>$0 cost.</p>
<p><span className="mr-4">&#9734;</span>No sign up. No personal data.</p>
<p><span className="mr-4">&#9734;</span>Anonymous voting option available!</p>
<p><span className="mr-4">&#9734;</span>Blazing fast poll creation with only 3 quick steps:</p>
<ul className="list-decimal my-8 pl-12">
<li className="pl-2">Create the poll with only title and questions.</li>
<li className="pl-2">Join the poll with a nickname.</li>
<li className="pl-2">Share the poll url to votes.</li>
</ul>
<p>Surprice the voters, its free!</p>
</div>
<div className="w-15">
<Button
className="p-15"
label="Create new poll"
icon={<IconPlus />}
onClick={() => navigate('/create')}
/>
</div>
</div>
)
}
export default Home
+49
View File
@@ -0,0 +1,49 @@
import { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { Title } from "../ui/title";
import { Button } from "../ui/button";
import { Input } from "../ui/input";
const URL ="http://192.168.1.14:4001"
const Join = (props) => {
const {socket} = props
const [user, setUser] = useState('');
const { id } = useParams();
const handleInput = (e) => {
setUser(e.target.value);
};
const submit = () => {
const data = {poll:id, user:user};
socket.emit('join', data); // triggers onRegister
console.log('JOIN submit data: ', data)
};
return (
<div>
<div className="">
<Title variant={2} label="Nickname:" />
<Input
name="title"
getRef={(ref) => handleInput(ref.current)}
placeholder="Choose a name to vote...."
value={user}
onChange={handleInput}
/>
</div>
<div className='mt-8'>
<Button
disabled={!user}
label="Let's vote!"
onClick={submit}
big
/>
</div>
</div>
);
}
export default Join
+61
View File
@@ -0,0 +1,61 @@
import { useState, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { CopyToClipboard } from "react-copy-to-clipboard";
import { Title } from "../ui/title";
import Answers from './Answers'
function Poll(props) {
const { user, poll, socket } = props
const navigate = useNavigate();
const { id } = useParams();
const token = localStorage.getItem(id)
const [copied, setCopied] = useState(false);
console.log('Poll props:', props)
const FRONT_URL = "http://192.168.1.14:3000"
useEffect(() => {
if (!token) {
return navigate(`/poll/${id}/join`)
}
socket.emit('poll', token);
console.log('Poll token:', token)
}, [id, token, navigate]);
const onCopy = () => {
console.log('onCopy')
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
return (
<div className="poll">
{poll &&
<div className="mb-8">
<Title variant={1} label={poll.title} />
<p className="text-white text-sm">Hello <strong>{user}</strong>, please choose the answer you like</p>
<Answers poll={poll} id={id} user={user} socket={socket} />
</div>
}
{/* SHARE */}
<div className="mt-2 text-white">
<p className="text-sm mb-3">Share the poll URL to the voters</p>
<div className="flex p-4 bg-black bg-opacity-20 rounded-xl justify-between">
<div className="text-sm">
{FRONT_URL}/poll/{id}
</div>
<div className="">
<CopyToClipboard onCopy={onCopy} text={`${URL}/poll/${id}`}>
<i className="flex mr-2 gg-copy"></i>
</CopyToClipboard>
</div>
</div>
<div className={`transition-opacity duration-600 flex justify-end text-sm mr-2 ${copied ? 'opacity-100' : 'opacity-0'}`}>
Copied
</div>
</div>
</div>
);
}
export default Poll;