source: reactapp/src/Pages/Subject.js@ 9bf1f8d

main
Last change on this file since 9bf1f8d was 9bf1f8d, checked in by viktor <viktor@…>, 22 months ago

prefinal

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