source: reactapp/src/Pages/Subject.js@ 4abf55a

main
Last change on this file since 4abf55a was af801e3, checked in by viktor <viktor@…>, 2 years ago

finished edit/delete/displace opinion/thread from report (react); todo reporting user/opinion/thread interface, public user pages and messaging (springboot)

  • Property mode set to 100644
File size: 8.9 KB
Line 
1import React, { useState, useEffect, useContext } from "react";
2import { useNavigate, useParams } from "react-router-dom";
3import JSOG from "jsog";
4import { Outlet } from "react-router-dom";
5import { CurrentPageNav } from "../Components/Styled/Main.style";
6import {
7 ProfessorCard,
8 ProfessorCardDetails,
9 ProfessorCardName,
10 ProfessorCardSeparator,
11} from "../Components/Styled/ProfessorCard.style";
12import AuthApi from "../api/AuthApi";
13import { AddOpinionButton } from "../Components/Styled/Modal.style";
14import {
15 EntityLi,
16 EntityUl,
17 EntityParam,
18} from "../Components/Styled/EntityList.style";
19import {
20 Modal,
21 ModalBody,
22 ModalClose,
23 ModalContent,
24 ModalFooter,
25 ModalHeader,
26 ModalInput,
27 ModalTextarea,
28} from "../Components/Styled/Modal.style";
29import axios from "../api/axios";
30import LoadingSpinner from "../Components/Styled/LoadingSpinner.style";
31
32const Subject = () => {
33 let params = useParams();
34 let navigate = useNavigate();
35
36 const { auth, setAuth } = useContext(AuthApi);
37 const [subject, setSubject] = useState(null);
38 const [loaded, setLoaded] = useState(false);
39 const [fetchError, setFetchError] = useState(false);
40 var totalTopics = 0;
41 var topics = [];
42
43 const [topicModalDisplay, setTopicModalDisplay] = useState("none");
44 const [topicTitle, setTopicTitle] = useState("");
45 const [topicContent, setTopicContent] = useState("");
46 const [errorMessage, setErrorMessage] = useState("");
47
48 useEffect(() => {
49 const url = `http://192.168.0.29:8080/public/subject/${params.subjectId}`;
50
51 const fetchData = async () => {
52 try {
53 const response = await fetch(url);
54 let cyclicGraph = await response.json();
55 let jsogStructure = JSOG.encode(cyclicGraph);
56 cyclicGraph = JSOG.decode(jsogStructure);
57 setSubject(cyclicGraph);
58 setLoaded(true);
59 } catch (error) {
60 setFetchError(true);
61 }
62 };
63
64 fetchData();
65 }, [params.subjectId]);
66
67 const handleAddTopicButtonClick = () => {
68 if (auth) {
69 setTopicModalDisplay("block");
70 document.body.style.overflowY = "hidden";
71 } else {
72 navigate("/login");
73 }
74 };
75
76 const handleModalCloseClick = () => {
77 setTopicModalDisplay("none");
78 document.body.style.overflowY = "auto";
79 };
80
81 const handleTopicSubmit = async (e) => {
82 e.preventDefault();
83
84 if (!topicTitle.length < 1 && !topicContent.length < 1) {
85 const response = await axios(
86 `http://192.168.0.29:8080/secure/subject/${params.subjectId}/addThread`,
87 {
88 method: "post",
89 data: {
90 title: topicTitle,
91 content: topicContent,
92 },
93 withCredentials: true,
94 }
95 );
96 setErrorMessage("");
97 window.location.reload();
98 } else {
99 setErrorMessage("Полињата за наслов и содржина не смеат да бидат празни");
100 }
101 };
102
103 const handleContentChange = (e) => {
104 setTopicContent(e.target.value);
105 };
106
107 const handleTitleChange = (e) => {
108 setTopicTitle(e.target.value);
109 };
110
111 return loaded ? (
112 <>
113 <CurrentPageNav>
114 &#187;{" "}
115 <a
116 href={
117 "/university/" +
118 subject.studyProgramme.faculty.university.universityId
119 }
120 >
121 {subject.studyProgramme.faculty.university.universityName}
122 </a>{" "}
123 &#187;{" "}
124 <a href={"/faculty/" + subject.studyProgramme.faculty.facultyId}>
125 {subject.studyProgramme.faculty.facultyName}
126 </a>{" "}
127 &#187; <a href="#">{subject.subjectName}</a>
128 </CurrentPageNav>
129 <ProfessorCard>
130 <ProfessorCardName>{subject.subjectName} <span style={{opacity:"50%", fontSize:"16px"}}>#{subject.subjectId}</span></ProfessorCardName>
131 <ProfessorCardSeparator />
132 <div style={{ marginTop: "10px" }}>
133 <ProfessorCardDetails fontSize="20px">
134 {subject.studyProgramme.studyProgrammeName} (
135 {subject.studyProgramme.cycle}
136 {"."}
137 {"циклус"})
138 </ProfessorCardDetails>
139 <ProfessorCardDetails fontSize="20px">
140 {subject.studyProgramme.faculty.facultyName}
141 </ProfessorCardDetails>
142 <ProfessorCardDetails fontSize="15px">
143 {subject.studyProgramme.faculty.university.universityName}
144 </ProfessorCardDetails>
145 </div>
146 </ProfessorCard>
147 <div style={{ height: "20px", marginBottom: "50px" }}>
148 <h3
149 style={{
150 float: "left",
151 }}
152 >
153 {subject.threads.map((thread) => {
154 if (thread.parent === null) {
155 totalTopics++;
156 topics.push(thread);
157 }
158 })}
159 {totalTopics} {totalTopics !== 1 ? "теми" : "тема"}
160 </h3>
161 {auth && (
162 <AddOpinionButton onClick={handleAddTopicButtonClick}>
163 Отвори тема
164 </AddOpinionButton>
165 )}
166 </div>
167 <Modal display={topicModalDisplay}>
168 <ModalContent>
169 <ModalHeader>
170 <ModalClose onClick={handleModalCloseClick}>&times;</ModalClose>
171 <h3 style={{ marginTop: "5px" }}>
172 Тема во врска со {subject.subjectName}
173 </h3>
174 </ModalHeader>
175 <form onSubmit={handleTopicSubmit}>
176 <ModalBody>
177 <label htmlFor="title">
178 <b>Наслов</b>:
179 <ModalInput
180 id="title"
181 value={topicTitle}
182 onChange={handleTitleChange}
183 spellCheck={false}
184 />
185 </label>
186 <label htmlFor="content">
187 <b>Содржина</b>:
188 <ModalTextarea
189 id="content"
190 rows="8"
191 cols="100"
192 value={topicContent}
193 onChange={handleContentChange}
194 spellCheck={false}
195 />
196 </label>
197 </ModalBody>
198 <p style={{ color: "red", marginLeft: "15px", marginTop: "10px" }}>
199 {errorMessage}
200 </p>
201 <ModalFooter type="submit">ОБЈАВИ</ModalFooter>
202 </form>
203 </ModalContent>
204 </Modal>
205 <div key={subject.subjectId}>
206 {topics.map((topic) => {
207 var numReplies = 0;
208 topic.children.map((c)=>numReplies += ++c.children.length); // ++c.children.length -> c + decata na c
209 return (
210 <EntityUl key={topic.postId}>
211 <EntityLi bgcolor="cornsilk">
212 <a href={"/topic/" + topic.postId}>{topic.title}</a>
213 <EntityParam right="30px">
214 <span style={{ fontWeight: "normal" }}>
215 отворил:{" "}
216 <a href={"/user/" + topic.author.id}>
217 {topic.author.username}
218 </a>
219 </span>
220 <span style={{ fontStyle: "normal" }}>,</span>{" "}
221 {numReplies !== 1 ? (
222 numReplies !== 0 ? (
223 <span
224 style={{
225 fontWeight: "normal",
226 opacity: numReplies === 0 ? "0.5" : "1",
227 }}
228 >
229 <span
230 style={{
231 fontWeight: "bold",
232 opacity: numReplies === 0 ? "0.5" : "1",
233 }}
234 >
235 {numReplies}
236 </span>{" "}
237 реплики
238 </span>
239 ) : (
240 <span
241 style={{
242 fontWeight: "normal",
243 opacity: numReplies === 0 ? "0.5" : "1",
244 }}
245 >
246 <span
247 style={{
248 fontWeight: "bold",
249 opacity: numReplies === 0 ? "0.5" : "1",
250 }}
251 >
252 {numReplies}
253 </span>{" "}
254 реплики
255 </span>
256 )
257 ) : (
258 <span style={{ fontWeight: "normal" }}>
259 <span style={{ fontWeight: "bold" }}>{numReplies}</span>{" "}
260 реплика
261 </span>
262 )}
263 </EntityParam>
264 </EntityLi>
265 </EntityUl>
266 );
267 })}
268 </div>
269 </>
270 ) : !fetchError ? (
271 <div>
272 <LoadingSpinner style={{ marginTop: "140px" }}/>
273 <Outlet />
274 </div>
275 ) : (
276 <div style={{ marginTop: "140px" }}>
277 <h1 style={{ textAlign: "center" }}>Страницата не е пронајдена.</h1>
278 </div>
279 );
280};
281
282export default Subject;
Note: See TracBrowser for help on using the repository browser.