Skip to content

Commit 99b9d79

Browse files
committed
Add functionality for seeing list of polls in frontend
1 parent 16c4d26 commit 99b9d79

File tree

12 files changed

+171
-104
lines changed

12 files changed

+171
-104
lines changed

src/frontend/my-app/src/Components/CreatePollComponent.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,8 @@ function CreatePoll({ userName,
2323
pollRef.current.setCreator(userName)
2424
}
2525

26-
27-
2826
const sendPoll = async () => {
29-
3027

31-
3228
try {
3329
setCreator()
3430
const raw = document.cookie.split('; ')
@@ -47,7 +43,7 @@ function CreatePoll({ userName,
4743
},
4844
body: JSON.stringify(pollRef.current.toJSON()),
4945
});
50-
setResponse(await res.text());
46+
setResponse("Poll Sent Successfully");
5147
}
5248
catch(error:unknown){
5349
if(error instanceof Error){

src/frontend/my-app/src/Components/LogInComponent.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState } from "react";
1+
import { use, useEffect, useState } from "react";
22
import { BACKEND_URL } from './Constants/constants.js';
33
import {getCookieRaw} from './CreatePollComponent.js'
44

@@ -17,6 +17,7 @@ export default function LogInComponent({
1717
loginStatus
1818
}: LogInProps) {
1919
const [password, setPassword] = useState("");
20+
const [admin, setAdmin] = useState(false)
2021
const [loading, setLoading] = useState(false);
2122
const [err, setErr] = useState<string | null>(null);
2223
const [polls, setPolls] = useState<any[]>([]);
@@ -31,7 +32,7 @@ const fetchUsers = async () => {
3132
const xsrf = getCookieRaw('XSRF-TOKEN');
3233

3334
const res = await fetch(`${BACKEND_URL}/users`, {
34-
credentials: "include", // send JSESSIONID
35+
credentials: "include",
3536
headers: {
3637
'X-XSRF-TOKEN': xsrf,
3738
}
@@ -68,6 +69,16 @@ const fetchUsers = async () => {
6869
},
6970
body: body.toString(),
7071
});
72+
const roleRes = await fetch(`${BACKEND_URL}/users/me/role`, {
73+
credentials: "include",
74+
});
75+
if (!roleRes.ok) throw new Error("Could not fetch role");
76+
77+
const role = await roleRes.json();
78+
if (role === "ADMIN") {
79+
setAdmin(true);
80+
}
81+
7182

7283
if (!res.ok) throw new Error(`Login failed (${res.status})`);
7384
await fetch(`${BACKEND_URL}/auth/csrf`, { credentials: "include" });
@@ -161,9 +172,11 @@ return (
161172
)}
162173
</div>
163174
<div style={{ marginTop: "1rem" }}>
175+
{admin &&(
164176
<button className="pollButton" onClick={fetchUsers}>
165177
Show Users (admin)
166178
</button>
179+
)}
167180
{showUsers && (
168181
<ul className="poll-list" style={{ marginTop: "0.5rem" }}>
169182
{users.map(u => (
Lines changed: 91 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,67 @@
1-
import { useState } from "react"
1+
import { useEffect, useState } from "react";
22
import { Vote, VoteOption, VoteOptions } from "./Model/Poll";
3-
import { BACKEND_URL } from './Constants/constants.js';
3+
import { BACKEND_URL } from "./Constants/constants.js";
44

5-
export default function VotePoll({userName}: {userName:string}){
6-
const [pollID, setPollID] = useState(1);
7-
const url = BACKEND_URL + "/polls/"+pollID+"/votes"
8-
const [error, setError] = useState<string | null>(null);
9-
const [pollJson, setPollJson] = useState(null);
10-
const [pollOptions, setPollOptions] = useState<VoteOptions>();
11-
const [votes,setVotes] = useState([])
12-
const [pollTitle, setPolltitle] = useState("")
13-
const [pollQuestion, setPollQuestion] = useState("")
14-
5+
type PollListItem = { id: number; title: string; creatorName: string };
156

16-
const getVotes = async () => {
17-
const res = await fetch(url+"/results");
7+
export default function VotePoll({ userName }: { userName: string }) {
8+
const [polls, setPolls] = useState<PollListItem[]>([]);
9+
const [activeId, setActiveId] = useState<number | null>(null);
10+
const [error, setError] = useState<string | null>(null);
11+
const [pollJson, setPollJson] = useState<any>(null);
12+
const [pollOptions, setPollOptions] = useState<VoteOptions>();
13+
const [votes, setVotes] = useState<number[]>([]);
14+
const [pollTitle, setPollTitle] = useState("");
15+
const [pollQuestion, setPollQuestion] = useState("");
16+
const url = BACKEND_URL + "/polls/"+activeId+"/votes"
17+
const [seeAllPolls, setSeeAllPolls] = useState(true)
18+
19+
useEffect(() => {
20+
(async () => {
21+
try {
22+
const r = await fetch(`${BACKEND_URL}/polls/info`, { credentials: "include" });
23+
if (r.status === 204) return;
24+
if (!r.ok) throw new Error("list fetch failed");
25+
setPolls(await r.json());
26+
} catch {
27+
setError("Failed loading poll list");
28+
}
29+
})();
30+
}, []);
31+
32+
const getVotes = async (pollId:number) => {
33+
const res = await fetch(BACKEND_URL + "/polls/"+pollId+"/votes/" +"results");
1834
const data = await res.json();
1935
setVotes(data);
2036
}
21-
const vote = async(presentationOrder:number) =>{
22-
try{
23-
const vote:Vote = new Vote(presentationOrder,pollID, null)
24-
if(userName){
37+
38+
const getPollById = async (id: number) => {
39+
setError(null);
40+
try {
41+
const res = await fetch(`${BACKEND_URL}/polls/${id}`, { credentials: "include" });
42+
if (!res.ok) throw new Error("poll fetch failed");
43+
const data = await res.json();
44+
const vopts = new VoteOptions().fromJSON(data.options);
45+
setPollOptions(vopts);
46+
setPollJson(data);
47+
setPollTitle(data.title);
48+
setPollQuestion(data.question);
49+
setActiveId(data.id);
50+
await getVotes(id);
51+
setSeeAllPolls(false)
52+
} catch {
53+
setError(`Error fetching poll with id ${id}`);
54+
}
55+
};
56+
57+
const vote = async (presentationOrder: number) => {
58+
if (activeId == null) return;
59+
try {
60+
const vote:Vote = new Vote(presentationOrder,activeId, null)
61+
if(userName){
2562
vote.userName = userName;
2663
}
27-
const getCookieRaw = (name: string) =>
64+
const getCookieRaw = (name: string) =>
2865
document.cookie.split('; ')
2966
.find(c => c.startsWith(name + '='))?.split('=')[1] ?? '';
3067
const xsrf = getCookieRaw('XSRF-TOKEN');
@@ -38,77 +75,46 @@ export default function VotePoll({userName}: {userName:string}){
3875
'X-XSRF-TOKEN': xsrf,
3976
},
4077
body: JSON.stringify(vote.toJSON())});
41-
getVotes();
78+
getVotes(activeId);
4279
}
4380
catch(e: any){
4481
setError("Error voting");
45-
}
46-
}
47-
48-
const getPoll = async() =>{
49-
setError("")
50-
try {
51-
const res = await fetch(BACKEND_URL+"/polls/"+pollID);
52-
const data = await res.json();
53-
const voteoptions = new VoteOptions().fromJSON(data.options);
54-
setPollOptions(voteoptions)
55-
console.log((data))
56-
console.log(voteoptions)
57-
setPollJson(data);
58-
setPolltitle(data.title)
59-
setPollID(data.id)
60-
getVotes();
61-
setPollQuestion(data.question);
62-
}
63-
catch(e: any){
64-
setError("Error fetching poll with id"+pollID);
65-
}
6682
}
83+
};
6784

68-
const createOptionButtons = () => {
69-
return(
70-
<div className="voteOptionsBox">
71-
{pollOptions?.getVoteOptions().map(option => (
72-
<div className="voteOption"
73-
key={option.optionId}>
74-
<span className="caption">{option.getCaption()}</span>
75-
<button
76-
onClick={() => vote(option.presentationOrder)}
77-
>vote</button>
78-
<span className="voteCount">
79-
{votes[option.presentationOrder] ?? 0}</span>
80-
</div>
81-
82-
))}
83-
</div>
84-
)
85-
86-
}
87-
88-
89-
return (
90-
<div>
91-
<input
92-
placeholder={"Set Poll ID"}
93-
onChange={(e)=> setPollID(Number(e.target.value))}>
94-
</input>
95-
<button
96-
onClick={() => getPoll()}
97-
>
98-
Get Poll
85+
return (
86+
<div>
87+
<button onClick={() => setSeeAllPolls(!seeAllPolls)}>See All Polls</button>
88+
{seeAllPolls && (<div>
89+
<h3>Choose a poll</h3>
90+
{polls.length === 0 && <p>No polls available.</p>}
91+
<ul>
92+
{polls.map(p => (
93+
<li key={p.id}>
94+
<button onClick={() => getPollById(p.id)}>
95+
{p.title}{p.creatorName}
9996
</button>
100-
<h3>{error}</h3>
101-
{pollJson && (
102-
<>
103-
<h2>{pollTitle}</h2>
104-
<h3>Question: {pollQuestion}</h3>
105-
{createOptionButtons()}
106-
</>
107-
)}
108-
109-
110-
</div>
111-
112-
)
97+
</li>
98+
))}
99+
</ul>
100+
</div> )}
101+
{error && <h4>{error}</h4>}
113102

103+
{pollJson && (
104+
<>
105+
<h2>{pollTitle}</h2>
106+
<h3>Question: {pollQuestion}</h3>
107+
<div className="voteOptionsBox">
108+
{pollOptions?.getVoteOptions().map((option: VoteOption) => (
109+
<div className="voteOption" key={option.optionId}>
110+
<span className="caption">{option.getCaption()}</span>
111+
<button onClick={() => vote(option.presentationOrder)}>vote</button>
112+
<span className="voteCount">{votes[option.presentationOrder] ?? 0}</span>
113+
</div>
114+
))}
115+
</div>
116+
</>
117+
)}
118+
</div>
119+
);
114120
}

src/main/java/com/example/backend/Controllers/PollController.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.example.backend.Controllers;
22

33
import java.util.List;
4+
import java.util.Optional;
45

56
import org.springframework.http.HttpStatus;
67
import org.springframework.http.ResponseEntity;
@@ -17,6 +18,7 @@
1718
import org.springframework.web.bind.annotation.RestController;
1819

1920
import com.example.backend.Managers.PollManager;
21+
import com.example.backend.Model.Poll.BasicPollInfoDto;
2022
import com.example.backend.Model.Poll.Poll;
2123
import com.example.backend.Model.Poll.PollDTO;
2224

@@ -51,11 +53,15 @@ public ResponseEntity<?> deletePoll(@PathVariable Integer pollID, @Authenticatio
5153
}
5254

5355
@GetMapping("/{pollID}")
54-
public Poll getPoll(@PathVariable("pollID") Integer pollID) throws Exception {
56+
public ResponseEntity<Poll> getPoll(@PathVariable("pollID") Integer pollID) throws Exception {
5557
try {
56-
return manager.getPoll(pollID).get();
58+
Optional<Poll> poll = manager.getPoll(pollID);
59+
if(manager.getPoll(pollID).isPresent()){
60+
return ResponseEntity.ok(poll.get());
61+
}
62+
else return ResponseEntity.notFound().build();
5763
} catch (Exception e) {
58-
throw e;
64+
return ResponseEntity.internalServerError().build();
5965
}
6066

6167
}
@@ -71,4 +77,17 @@ public ResponseEntity<List<Poll>> getPolls(@RequestParam(value = "user", require
7177
return ResponseEntity.ok(polls);
7278
}
7379

80+
81+
82+
@GetMapping("/info")
83+
public ResponseEntity<List<BasicPollInfoDto>> getPollInfo() {
84+
85+
List<BasicPollInfoDto> pollInfo = manager.getBasicPollInfo();
86+
if(!pollInfo.isEmpty()){return ResponseEntity.ok(pollInfo);}
87+
else {
88+
return ResponseEntity.noContent().build();
89+
}
90+
}
91+
92+
7493
}

src/main/java/com/example/backend/Controllers/UserController.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
package com.example.backend.Controllers;
22

33
import java.util.Collection;
4-
import java.util.List;
5-
4+
import org.springframework.http.HttpStatus;
65
import org.springframework.http.ResponseEntity;
7-
import org.springframework.security.core.userdetails.UserDetails;
86
import org.springframework.web.bind.annotation.CrossOrigin;
97
import org.springframework.web.bind.annotation.GetMapping;
108
import org.springframework.web.bind.annotation.PostMapping;
119
import org.springframework.web.bind.annotation.RequestBody;
1210
import org.springframework.web.bind.annotation.RequestMapping;
1311
import org.springframework.web.bind.annotation.RestController;
12+
import org.springframework.web.server.ResponseStatusException;
1413

1514
import com.example.backend.Managers.PollManager;
16-
import com.example.backend.Model.Poll.Poll;
1715
import com.example.backend.Model.User.User;
16+
import com.example.backend.Model.User.User.Roles;
1817
import com.example.backend.Model.User.UserDTO;
1918

2019
import org.springframework.web.bind.annotation.PathVariable;
20+
import org.springframework.security.core.Authentication;
2121

2222
@RestController
2323
@CrossOrigin
@@ -34,6 +34,13 @@ public User getUser(@PathVariable("userName") String userName) {
3434
return pollManager.getUser(userName).get();
3535
}
3636

37+
@GetMapping("/me/role")
38+
public Roles getMyRole(Authentication auth) {
39+
return pollManager.getUser(auth.getName())
40+
.map(User::getRole)
41+
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
42+
}
43+
3744
@PostMapping
3845
public ResponseEntity<User> addUser(@RequestBody UserDTO userRequest) {
3946

src/main/java/com/example/backend/Managers/PollManager.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import com.example.backend.MessageBrokers.Listener;
1515
import com.example.backend.MessageBrokers.PollBroker;
1616
import com.example.backend.Model.Poll.Poll;
17+
import com.example.backend.Model.Poll.BasicPollInfoDto;
1718
import com.example.backend.Model.Poll.PollDTO;
1819
import com.example.backend.Model.User.User;
1920
import com.example.backend.Model.User.UserDTO;
@@ -57,6 +58,11 @@ public Optional<Poll> getPoll(Integer poll_id) {
5758
return pollRepo.findById(poll_id);
5859
}
5960

61+
@Transactional
62+
public List<BasicPollInfoDto> getBasicPollInfo() {
63+
return pollRepo.findBasicPollInfo();
64+
}
65+
6066
@Transactional
6167
public Optional<User> getUser(String userName) {
6268
return userRepo.findByUsername(userName);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.example.backend.Model.Poll;
2+
3+
public interface BasicPollInfoDto {
4+
Long getId();
5+
String getTitle();
6+
String getCreatorName();
7+
}

0 commit comments

Comments
 (0)