Skip to content

Commit

Permalink
Merge pull request #51 from redbadger/submit-new-statement
Browse files Browse the repository at this point in the history
Add API to handle submitting new statements
  • Loading branch information
ruiramos authored Aug 24, 2021
2 parents 8d3bee2 + ee658aa commit b0aebf1
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 79 deletions.
102 changes: 81 additions & 21 deletions components/AddNewStatement.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,33 @@ import {
ModalCloseButton,
} from '@chakra-ui/react';
import { Textarea } from '@chakra-ui/textarea';
import { useState } from 'react';

export default function AddNewStatement() {
export default function AddNewStatement({ jamId }) {
const { isOpen, onOpen, onClose } = useDisclosure();
const [statement, setStatement] = useState();
const [submitted, setSubmitted] = useState(false);

const sendRequest = () => {
fetch('/api/statement', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ statement: statement, jamId: jamId }),
})
.then(() => {
setSubmitted(true);
})
.catch((error) =>
console.error('Error saving statement: ', error),
);
};

let handleInputChange = (e) => {
let inputValue = e.target.value;
setStatement(inputValue);
};

return (
<>
Expand All @@ -23,26 +47,62 @@ export default function AddNewStatement() {
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Submit statements for voting</ModalHeader>
<ModalCloseButton />
<ModalBody>
- Statements should be easy for everyone to understand
without further explanation.
<br />
- Keep them clear and under 140 characters long.
<br />- Each one should be unique and raise a different
point.
<Textarea
mt={5}
placeholder="Please enter your statement here"
/>
</ModalBody>
<ModalFooter>
<Button mr={3} onClick={onClose}>
Cancel
</Button>
<Button colorScheme="blue">Submit</Button>
</ModalFooter>
{!submitted ? (
<>
<ModalHeader>Submit statements for voting</ModalHeader>
<ModalCloseButton />
<ModalBody>
- Statements should be easy for everyone to understand
without further explanation.
<br />
- Keep them clear and under 140 characters long.
<br />- Each one should be unique and raise a
different point.
<Textarea
mt={5}
placeholder="Please enter your statement here"
value={statement}
onChange={handleInputChange}
/>
</ModalBody>
<ModalFooter>
<Button mr={3} onClick={onClose}>
Cancel
</Button>
<Button
colorScheme="blue"
onClick={() => sendRequest()}
>
Submit
</Button>
</ModalFooter>
</>
) : (
<>
<ModalHeader>
Thank you for submitting a statement
</ModalHeader>
<ModalCloseButton />
<ModalBody>
One of our moderators will review your submission.
</ModalBody>
<ModalFooter>
<Button
colorScheme="blue"
mr={3}
onClick={() => {
setStatement('');
setSubmitted(false);
}}
>
Submit another
</Button>
<Button colorScheme="blue" onClick={onClose}>
Back to survey
</Button>
</ModalFooter>
</>
)}
</ModalContent>
</Modal>
</>
Expand Down
33 changes: 33 additions & 0 deletions pages/api/jam.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import fire from '../../config/firebaseAdminConfig';

export default function handler(req, res) {
const {
query: { jamUrlPath },
method,
} = req;

if (method !== 'GET') {
res.setHeader('Allow', ['GET']);
res.status(405).end(`Method ${method} Not Allowed`);
}

const db = fire.firestore();
const jamsRef = db.collection('jams');

console.log(jamUrlPath, 'jamUrlPath');
return new Promise(() => {
jamsRef
.where('urlPath', '==', jamUrlPath)
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
const jam = doc.data();
jam.key = doc.id;

res.status(200);
res.setHeader('Content-Type', 'application/json');
res.json(jam);
});
});
});
}
94 changes: 43 additions & 51 deletions pages/api/question.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,61 +17,53 @@ export default function handler(req, res) {
const participantsRef = db.collection('participants');

return new Promise(() => {
jamsRef
.where('urlPath', '==', jamId)
const questionsPromise = jamsRef
.doc(jamId)
.collection('statements')
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
const questionsPromise = jamsRef
.doc(doc.id)
.collection('statements')
.get()
.then((query) => {
const questions = {};
query.forEach((question) => {
questions[question.id] = question.data();
});
return questions;
});
.then((query) => {
const questions = {};
query.forEach((question) => {
questions[question.id] = question.data();
});
return questions;
});

const votesPromise = participantsRef
.doc(participantId)
.collection('votes')
.get()
.then((query) => {
const allVotes = [];
query.forEach((vote) =>
allVotes.push(vote.data().statementId),
);
return allVotes;
});
const votesPromise = participantsRef
.doc(participantId)
.collection('votes')
.get()
.then((query) => {
const allVotes = [];
query.forEach((vote) =>
allVotes.push(vote.data().statementId),
);
return allVotes;
});

Promise.all([questionsPromise, votesPromise]).then(
([questions, votes]) => {
const unansweredQs = pickBy(
questions,
(value, key) => !votes.includes(key),
);
Promise.all([questionsPromise, votesPromise]).then(
([questions, votes]) => {
const unansweredQs = pickBy(
questions,
(value, key) => !votes.includes(key),
);

const keys = Object.keys(unansweredQs);
if (!keys.length) {
res.status(200);
res.setHeader('Content-Type', 'application/json');
res.json({});
} else {
const randomKey =
keys[(keys.length * Math.random()) << 0];
const randomQ = unansweredQs[randomKey];
randomQ.key = randomKey;
randomQ.jamId = doc.id;
const keys = Object.keys(unansweredQs);
if (!keys.length) {
res.status(200);
res.setHeader('Content-Type', 'application/json');
res.json({});
} else {
const randomKey = keys[(keys.length * Math.random()) << 0];
const randomQ = unansweredQs[randomKey];
randomQ.key = randomKey;
randomQ.jamId = jamId;

res.status(200);
res.setHeader('Content-Type', 'application/json');
res.json(randomQ);
}
},
);
});
});
res.status(200);
res.setHeader('Content-Type', 'application/json');
res.json(randomQ);
}
},
);
});
}
31 changes: 31 additions & 0 deletions pages/api/statement.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import fire from '../../config/firebaseAdminConfig';

export default function handler(req, res) {
if (req.method !== 'POST') {
res.setHeader('Allow', ['POST']);
res.status(405).end(`Method ${method} Not Allowed`);
}

const { jamId, statement } = req.body;
const db = fire.firestore();
const jamsRef = db.collection('jams');

return new Promise(() => {
jamsRef
.doc(jamId)
.collection('statements')
.add({
text: statement,
createdAt: fire.firestore.Timestamp.now(),
isUserSubmitted: true,
state: 0,
numAgrees: 0,
numDisagrees: 0,
numSkipped: 0,
})
.then(() => res.status(201).end())
.catch((error) => {
console.error('Error writing document: ', error);
});
});
}
39 changes: 32 additions & 7 deletions pages/jams/[jam].js
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,39 @@ import { useCookies } from 'react-cookie';
import JamButton from '../../components/JamButton';
import { Stack } from '@chakra-ui/layout';
import AddNewStatement from '../../components/AddNewStatement';
import { Center, Heading } from '@chakra-ui/react';
import { Center, Heading, Text } from '@chakra-ui/react';

const Jam = () => {
const router = useRouter();
const { jam: jamId } = router.query;
const { jam: jamUrlPath } = router.query;

const [jam, setJam] = useState();
const [question, setQuestion] = useState();
const [isDone, setIsDone] = useState(false);
const [participantId, setParticipantId] = useState();
const [cookies, setCookies] = useCookies();

const ids = {
participantId: participantId,
jamId: question ? question.jamId : null,
jamId: jam ? jam.key : null,
statementId: question ? question.key : null,
};

useEffect(() => {
if (router.isReady && participantId) {
loadQuestion();
loadJam();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [router.isReady, participantId]);

useEffect(() => {
if (jam == null) {
return;
}

loadQuestion();
}, [jam]);

useEffect(() => {
const id = cookies['jams-participant'];
if (id) {
Expand All @@ -39,10 +48,18 @@ const Jam = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [cookies]);

const loadJam = () => {
fetch(`/api/jam?jamUrlPath=${encodeURIComponent(jamUrlPath)}`)
.then((response) => response.json())
.then((json) => {
setJam(json);
});
};

const loadQuestion = () => {
fetch(
`/api/question?jamId=${encodeURIComponent(
jamId,
jam.key,
)}&participantId=${encodeURIComponent(participantId)}`,
)
.then((response) => response.json())
Expand All @@ -63,7 +80,6 @@ const Jam = () => {
})
.then((response) => response.json())
.then((participant) => {
// Can we do this using a Set-Cookie header?
setCookies('jams-participant', participant.participantId);
})
.catch((error) =>
Expand All @@ -79,6 +95,15 @@ const Jam = () => {
</Head>

<Stack direction="column" spacing={2} mb={5}>
{jam && (
<>
<Heading as="h1" size="2xl">
{jam.name}
</Heading>
<Text>{jam.description}</Text>
</>
)}

<Heading as="h1" size="3xl">
{!isDone
? question
Expand Down Expand Up @@ -111,7 +136,7 @@ const Jam = () => {
</>
)}

<AddNewStatement />
<AddNewStatement jamId={jam ? jam.key : null} />
</Stack>

<footer>
Expand Down

0 comments on commit b0aebf1

Please sign in to comment.