Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
d22576c
Poll Table
dagostocsc Jul 14, 2025
201184d
seeded database and created router.get for polls
dagostocsc Jul 14, 2025
9d8f6d4
router.get by id
dagostocsc Jul 14, 2025
bfd233f
router post created
dagostocsc Jul 14, 2025
eef8579
added a route for update handling of user profiles in index.js
TJordan77 Jul 15, 2025
29e50ef
Update user.js with profile definitions (first/last name, profile pic…
TJordan77 Jul 15, 2025
28080d2
all routers for polls are finished
dagostocsc Jul 15, 2025
fbd7254
Merge pull request #1 from Ramses-Deborah-Tina/login-signup
RamsesCode Jul 15, 2025
bc1d6db
Merge pull request #2 from Ramses-Deborah-Tina/update-handler
RamsesCode Jul 15, 2025
dd9fcbb
made an admins table
dagostocsc Jul 15, 2025
8c17ef6
forgot de.define
dagostocsc Jul 15, 2025
ca12671
forgot module exports as well
dagostocsc Jul 15, 2025
c4c1cc5
created a Roles table
dagostocsc Jul 15, 2025
064fc10
Created a Poll Invites table
dagostocsc Jul 15, 2025
2435a4d
created poll_options table
dagostocsc Jul 15, 2025
c31384f
Merge pull request #3 from Ramses-Deborah-Tina/login-signup
dagostocsc Jul 15, 2025
d3b4549
set up associations and corrected poll options
dagostocsc Jul 15, 2025
d7be59e
Merge pull request #4 from Ramses-Deborah-Tina/login-signup
dagostocsc Jul 15, 2025
e5426bd
changed description in polls table so the null is false instead of true
dagostocsc Jul 15, 2025
6559020
created ballots table
dagostocsc Jul 15, 2025
81c6274
Merge pull request #5 from Ramses-Deborah-Tina/login-signup
dagostocsc Jul 15, 2025
31caf29
Updated app.js with poll import and routing
TJordan77 Jul 16, 2025
b288ebc
Updated poll.js and improved poll id logic
TJordan77 Jul 16, 2025
80d3d5c
Updated poll.js to remove duplicate line
TJordan77 Jul 16, 2025
a6aa1f6
Updated Polls.js to handle option support
TJordan77 Jul 16, 2025
e0b3b5f
Updated Polls.js to reflect options naming
TJordan77 Jul 16, 2025
42a871e
Update poll_options.js
TJordan77 Jul 16, 2025
23793b3
Updated index.js with poll associations
TJordan77 Jul 16, 2025
18d9fa5
Updated poll.js with option handling adjustments
TJordan77 Jul 16, 2025
51b9232
Update poll.js
TJordan77 Jul 16, 2025
39e99ff
Update poll.js typo in import
TJordan77 Jul 16, 2025
49dd34d
Merge pull request #6 from Ramses-Deborah-Tina/live-vote-count
TJordan77 Jul 16, 2025
ab3c473
Updated poll.js with publishing protections
TJordan77 Jul 16, 2025
69e343a
Merge pull request #7 from Ramses-Deborah-Tina/lock-edits-after-publi…
TJordan77 Jul 16, 2025
db6f5b7
temporaily commented out associations and making ballots,votes table
dagostocsc Jul 16, 2025
2b66d57
Merge pull request #8 from Ramses-Deborah-Tina/login-signup
dagostocsc Jul 16, 2025
625e111
ballot and vote association
dagostocsc Jul 16, 2025
5619981
forgot to do exports and needed to require ballot and vote at line 5 …
dagostocsc Jul 16, 2025
626f24f
forgot to put module.exports = Ballot
dagostocsc Jul 16, 2025
d1b9950
Merge pull request #9 from Ramses-Deborah-Tina/login-signup
dagostocsc Jul 16, 2025
d0f0f58
did more associations
dagostocsc Jul 16, 2025
dea0064
correction
dagostocsc Jul 16, 2025
cd04ca4
ballot router but not tested in postman, does not break the abckend t…
dagostocsc Jul 16, 2025
f68d369
fixed associations bug; api call test was successful
TJordan77 Jul 17, 2025
6b66316
Merge pull request #10 from Ramses-Deborah-Tina/poll-fix
TJordan77 Jul 17, 2025
eee771d
added duplication routes
TJordan77 Jul 17, 2025
1781f21
added routes for manually closing polls
TJordan77 Jul 17, 2025
61b2e78
Merge pull request #11 from Ramses-Deborah-Tina/poll-duplication
TJordan77 Jul 18, 2025
cecab5e
Merge pull request #12 from Ramses-Deborah-Tina/poll-transitions
TJordan77 Jul 18, 2025
3222427
user routes and middleware file
dagostocsc Jul 18, 2025
e357b39
Merge pull request #13 from Ramses-Deborah-Tina/login-signup
dagostocsc Jul 18, 2025
f63f2ba
suceeded in postman option route, troubleshooting user routes still
dagostocsc Jul 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .env.example

This file was deleted.

35 changes: 35 additions & 0 deletions api/ballot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const express = require("express");
const router = express.Router();
const { Ballot, Vote, Polls, PollOption, User } = require("../database");

router.post("/", async (req, res) => {
try {
const { pollId, userId, votes } = req.body;

if (!pollId || !votes || votes.length < 2) {
return res.status(400).json({ error: "Poll ID and at least 2 ranked votes are required." });
}

const poll = await Polls.findByPk(pollId);
if (!poll) {
return res.status(404).json({ error: "Poll not found." });
}

if (userId) {
const user = await User.findByPk(userId);
if (!user) {
return res.status(404).json({ error: "User not found." });
}

const existingBallot = await Ballot.findOne({ where: { poll_id: pollId, user_id: userId } });
if (existingBallot) {
return res.status(400).json({ error: "User has already submitted a ballot for this poll." });
}
}
} catch (err) {
console.error(err);
res.status(500).json({ error: "Failed to submit ballot." });
}
});

module.exports = router;
8 changes: 8 additions & 0 deletions api/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
const express = require("express");
const router = express.Router();

const testDbRouter = require("./test-db");
const ballotsRouter = require("./ballot");
const usersRouter = require("./user");
const pollsRouter = require("./poll");

router.use("/Polls", pollsRouter);
router.use("/ballots", ballotsRouter);
router.use("/users", usersRouter);
router.use("/test-db", testDbRouter);

module.exports = router;

242 changes: 242 additions & 0 deletions api/poll.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
const express = require("express");
const router = express.Router();
const { Polls, PollOption } = require("../database");

router.get("/", async (req, res) => {
try {
const polls = await Polls.findAll();
res.status(200).send(polls);
} catch (error) {
console.error(error);
res.status(500).json({ error: "Failed to fetch polls" });
}
});

// Adjusted poll id logic to include options, more info and vote count
router.get("/:id", async (req, res) => {
try {
const poll = await Polls.findByPk(req.params.id, {
include: [{ model: PollOption, as: "options" }],
});

if (!poll) return res.status(404).send({ error: "Poll not found" });

// Checking if the poll should transition to "ended"
const now = new Date();
if (poll.status === "published" && poll.endTime && new Date(poll.endTime) <= now) {
poll.status = "ended";
await poll.save();
}

//issue in creating the options from the polls themselves, do it as a separate functions in the router.post
// for options and not just for polls


const totalVotes = poll.options.reduce((sum, opt) => sum + (opt.votes || 0), 0);

res.send({
id: poll.id,
title: poll.title,
description: poll.description,
status: poll.status,
publishedAt: poll.publishedAt, // including this for clarity
endTime: poll.endTime,
options: poll.options,
totalVotes,
});
} catch (err) {
console.error("Error getting poll:", err);
res.status(500).send({ error: "Server error" });
}
});


router.patch("/:id", async (req, res) => {
try {
const poll = await Polls.findByPk(req.params.id);

if (!poll) {
return res.status(404).json({ error: "Poll not found" });
}

// Stop users from editing a poll after it's been published or ended
if (["published", "ended"].includes(poll.status)) {
return res.status(400).json({ error: "Cannot edit a published or ended poll" });
}

// Included validation fields
const { title, description } = req.body;
if (title !== undefined && !title.trim()) {
return res.status(400).json({ error: "Title cannot be empty" });
}
if (description !== undefined && !description.trim()) {
return res.status(400).json({ error: "Description cannot be empty" });
}

const updatedPoll = await poll.update(req.body);
res.send(updatedPoll);
} catch (error) {
console.error(error);
res.status(500).json({ error: "Failed to update poll" });
}
});



router.post("/", async (req, res) => {
try {
const createPoll = await Polls.create(req.body);
res.status(201).send(createPoll);
} catch (error) {
console.error(error);
res.status(500).json({ error: "Failed to create poll" });
}
});

// Duplication logic
router.post("/:id/duplicate", async (req, res) => {
try {
const originalPoll = await Polls.findByPk(req.params.id, {
include: [{ model: PollOption, as: "options" }],
});

if (!originalPoll) return res.status(404).json({ error: "Poll not found" });

const duplicatedPoll = await Polls.create({
user_id: originalPoll.user_id, // or req.user.id if we're using auth
title: originalPoll.title + " (Copy)",
description: originalPoll.description,
status: "draft",
});

const duplicatedOptions = await Promise.all(
originalPoll.options.map((opt) =>
PollOption.create({
text: opt.text,
votes: 0,
pollId: duplicatedPoll.id,
})
)
);

res.status(201).json({
message: "Poll duplicated successfully",
poll: duplicatedPoll,
options: duplicatedOptions,
});
} catch (err) {
console.error("Duplicate poll error:", err);
res.status(500).json({ error: "Failed to duplicate poll" });
}
});

router.post("/:id/options", async (req, res) => {
try {
const { id: pollId } = req.params;
const { text } = req.body;

// Validate text
if (!text || !text.trim()) {
return res.status(400).json({ error: "Option text is required." });
}

// Check that poll exists
const poll = await Polls.findByPk(pollId);
if (!poll) {
return res.status(404).json({ error: "Poll not found." });
}

// Check that poll is in draft state
if (poll.status !== "draft") {
return res.status(400).json({ error: "Cannot add options to a published or ended poll." });
}

// Create the option
const newOption = await PollOption.create({
text,
pollId: poll.id,
});

res.status(201).json({
message: "Option added successfully.",
option: newOption,
});
} catch (err) {
console.error("Error adding poll option:", err);
res.status(500).json({ error: "Failed to add option." });
}
});


// Publishing logic and validation added
router.put("/publish/:id", async (req, res) => {
try {
const poll = await Polls.findByPk(req.params.id, {
include: [{ model: PollOption, as: "options" }],
});

if (!poll) return res.status(404).json({ error: "Poll not found" });
if (poll.status !== "draft") return res.status(400).json({ error: "Only draft polls can be published" });

if (!poll.title || !poll.description) {
return res.status(400).json({ error: "Title and description are required to publish" });
}

if (!poll.options || poll.options.length < 2) {
return res.status(400).json({ error: "Poll must have at least two options to publish" });
}

poll.status = "published";
poll.publishedAt = new Date();
await poll.save();

res.send({ message: "Poll published successfully", poll });
} catch (err) {
console.error("Publish error:", err);
res.status(500).json({ error: "Failed to publish poll" });
}
});

// Manually close the polls
router.put("/:id/end", async (req, res) => {
try {
const poll = await Polls.findByPk(req.params.id);

if (!poll) return res.status(404).json({ error: "Poll not found" });
if (poll.status !== "published") {
return res.status(400).json({ error: "Only published polls can be ended" });
}

poll.status = "ended";
await poll.save();

res.json({ message: "Poll ended successfully", poll });
} catch (error) {
console.error("Error ending poll:", error);
res.status(500).json({ error: "Failed to end poll" });
}
});


router.delete("/:id", async (req, res) => {
try {
const deletePoll = await Polls.findByPk(req.params.id);

if (!deletePoll) {
return res.status(404).json({ error: "Poll not found" });
}

// Protects against polls getting deleted once published
if (["published", "ended"].includes(deletePoll.status)) {
return res.status(400).json({ error: "Cannot delete a published or ended poll" });
}

await deletePoll.destroy();
res.sendStatus(200);
} catch (error) {
console.error(error);
res.status(500).json({ error: "Failed to delete poll" });
}
});

module.exports = router;
Loading