Index: reactapp/src/Pages/Faculty.js
===================================================================
--- reactapp/src/Pages/Faculty.js	(revision 8d831804abcee99d72c207c57a8f16e54b1329a2)
+++ reactapp/src/Pages/Faculty.js	(revision c68150fac8ab8e6f5dc5b8b2a9cd73d391dc1aa7)
@@ -60,5 +60,5 @@
   }, [params.facultyId]);
 
-  return loadedProfessors ? (
+  return loadedProfessors && professors.length != 0 ? (
     entityType === 0 ? (
       <>
@@ -114,5 +114,5 @@
                     {professor.professorName}
                   </a>
-                  <EntityParam>
+                  <EntityParam right="30px">
                     {totalPosts !== 1 ? (
                       totalPosts !== 0 ? (
@@ -168,4 +168,5 @@
         <>
           <CurrentPageNav>
+            &#187;{" "}
             <a
               href={
@@ -175,5 +176,5 @@
               {professors[0].faculty.university.universityName}
             </a>{" "}
-            / <a href="#">{professors[0].faculty.facultyName}</a>
+            &#187; <a href="#">{professors[0].faculty.facultyName}</a>
           </CurrentPageNav>
           <ProfessorCard>
@@ -221,5 +222,5 @@
       )
     )
-  ) : !fetchError ? (
+  ) : !fetchError && !loadedProfessors ? (
     <div>
       <p style={{ marginTop: "140px" }}>се вчитува...</p>
Index: reactapp/src/Pages/Home.js
===================================================================
--- reactapp/src/Pages/Home.js	(revision 8d831804abcee99d72c207c57a8f16e54b1329a2)
+++ reactapp/src/Pages/Home.js	(revision c68150fac8ab8e6f5dc5b8b2a9cd73d391dc1aa7)
@@ -24,5 +24,5 @@
       </a>{" "}
       <Search />
-      {auth && <UserHeader user={user} userLoaded={userLoaded} />}
+      {auth && <UserHeader />}
       <div style={{ marginTop: "140px" }}></div>
       <Outlet />
Index: reactapp/src/Pages/Login.js
===================================================================
--- reactapp/src/Pages/Login.js	(revision 8d831804abcee99d72c207c57a8f16e54b1329a2)
+++ reactapp/src/Pages/Login.js	(revision c68150fac8ab8e6f5dc5b8b2a9cd73d391dc1aa7)
@@ -35,5 +35,8 @@
     if (!response.request.responseURL.includes("error")) {
       // ako NE redirektira na /login?error
-      Cookies.set("JSESSIONID", response.data.sessionId);
+      var in30Minutes = 1 / 48;
+      Cookies.set("JSESSIONID", response.data.sessionId, {
+        expires: in30Minutes,
+      });
       setAuth(true);
       setErrMsg("");
Index: reactapp/src/Pages/Professor.js
===================================================================
--- reactapp/src/Pages/Professor.js	(revision 8d831804abcee99d72c207c57a8f16e54b1329a2)
+++ reactapp/src/Pages/Professor.js	(revision c68150fac8ab8e6f5dc5b8b2a9cd73d391dc1aa7)
@@ -25,20 +25,21 @@
 import { CurrentPageNav } from "../Components/Styled/Main.style";
 
-function Professor(user, userLoaded) {
+function Professor() {
   let params = useParams();
+  let navigate = useNavigate();
 
-  let [professor, setProfessor] = useState(null);
-  let [loaded, setLoaded] = useState(null);
-  let [postModalDisplay, setPostModalDisplay] = useState("none");
-  let navigate = useNavigate();
+  const [professor, setProfessor] = useState(null);
+  const [loadedProfessor, setLoadedProfessor] = useState(false);
+
+  const [postModalDisplay, setPostModalDisplay] = useState("none");
   const { auth, setAuth } = useContext(AuthApi);
-  const [postTitle, setPostTitle] = useState("");
   const [postContent, setPostContent] = useState("");
   const [fetchError, setFetchError] = useState(false);
+  const [errorMessage, setErrorMessage] = useState("");
 
   useEffect(() => {
     const url = `http://192.168.0.17:8080/public/professor/${params.professorId}`;
 
-    const fetchData = async () => {
+    const fetchProfessor = async () => {
       try {
         const response = await fetch(url);
@@ -47,5 +48,5 @@
         cyclicGraph = JSOG.decode(jsogStructure);
         setProfessor(cyclicGraph);
-        setLoaded(true);
+        setLoadedProfessor(true);
       } catch (error) {
         setFetchError(true);
@@ -53,5 +54,5 @@
     };
 
-    fetchData();
+    fetchProfessor();
   }, [params.professorId]);
 
@@ -71,21 +72,20 @@
     e.preventDefault();
 
-    const response = await axios(
-      `http://192.168.0.17:8080/secure/professor/${professor.professorId}/addOpinion`,
-      {
-        method: "post",
-        data: {
-          title: postTitle,
-          content: postContent,
-        },
-        withCredentials: true,
-      }
-    );
-
-    window.location.reload(false);
-  };
-
-  const handleTitleChange = (e) => {
-    setPostTitle(e.target.value);
+    if (!postContent.length < 1) {
+      const response = await axios(
+        `http://192.168.0.17:8080/secure/professor/${params.professorId}/addOpinion`,
+        {
+          method: "post",
+          data: {
+            content: postContent,
+          },
+          withCredentials: true,
+        }
+      );
+      setErrorMessage("");
+      window.location.reload(false);
+    } else {
+      setErrorMessage("Полето за содржина не смее да биде празно");
+    }
   };
 
@@ -94,5 +94,5 @@
   };
 
-  if (loaded) {
+  if (loadedProfessor) {
     return (
       <div>
@@ -120,5 +120,5 @@
           </div>
         </ProfessorCard>
-        <div style={{ height: "20px", marginBottom: "30px" }}>
+        <div style={{ height: "20px", marginBottom: "50px" }}>
           <h3
             style={{
@@ -146,13 +146,4 @@
             <form onSubmit={handlePostSubmit}>
               <ModalBody>
-                <label htmlFor="title">
-                  <b>Наслов</b>:
-                  <ModalInput
-                    id="title"
-                    type="text"
-                    value={postTitle}
-                    onChange={handleTitleChange}
-                  />
-                </label>
                 <label htmlFor="content">
                   <b>Содржина</b>:
@@ -166,4 +157,9 @@
                 </label>
               </ModalBody>
+              <p
+                style={{ color: "red", marginLeft: "15px", marginTop: "10px" }}
+              >
+                {errorMessage}
+              </p>
               <ModalFooter type="submit">ОБЈАВИ</ModalFooter>
             </form>
@@ -172,9 +168,5 @@
 
         <div className="opinionTree">
-          <OpinionTree
-            professor={professor}
-            user={user}
-            userLoaded={userLoaded}
-          />
+          <OpinionTree professor={professor} />
         </div>
         <Outlet />
Index: reactapp/src/Pages/Registration.js
===================================================================
--- reactapp/src/Pages/Registration.js	(revision 8d831804abcee99d72c207c57a8f16e54b1329a2)
+++ reactapp/src/Pages/Registration.js	(revision c68150fac8ab8e6f5dc5b8b2a9cd73d391dc1aa7)
@@ -1,3 +1,3 @@
-import React, { useRef, useState, useEffect } from "react";
+import React, { useRef, useState, useEffect, useContext } from "react";
 import axios from "../api/axios";
 import {
@@ -7,7 +7,10 @@
   RequiredAsterisk,
 } from "../Components/Styled/Login.style";
+import { Navigate } from "react-router-dom";
+import AuthApi from "../api/AuthApi";
 const REGISTRATION_URL = "/registration";
 
 const Registration = () => {
+  const { auth, setAuth } = useContext(AuthApi);
   const userRef = useRef();
   const errRef = useRef();
@@ -50,5 +53,7 @@
     useState(0);
 
-  return registrationSuccessful === false ? (
+  return auth ? (
+    <Navigate to="/user_dashboard" />
+  ) : registrationSuccessful === false ? (
     <div
       style={{
Index: reactapp/src/Pages/Subject.js
===================================================================
--- reactapp/src/Pages/Subject.js	(revision 8d831804abcee99d72c207c57a8f16e54b1329a2)
+++ reactapp/src/Pages/Subject.js	(revision c68150fac8ab8e6f5dc5b8b2a9cd73d391dc1aa7)
@@ -1,8 +1,275 @@
-import React from "react";
-import { useParams } from "react-router-dom";
+import React, { useState, useEffect, useContext } from "react";
+import { useNavigate, useParams } from "react-router-dom";
+import JSOG from "jsog";
+import { Outlet } from "react-router-dom";
+import { CurrentPageNav } from "../Components/Styled/Main.style";
+import {
+  ProfessorCard,
+  ProfessorCardDetails,
+  ProfessorCardName,
+  ProfessorCardSeparator,
+} from "../Components/Styled/ProfessorCard.style";
+import AuthApi from "../api/AuthApi";
+import { AddOpinionButton } from "../Components/Styled/Modal.style";
+import {
+  EntityLi,
+  EntityUl,
+  EntityParam,
+} from "../Components/Styled/EntityList.style";
+import {
+  Modal,
+  ModalBody,
+  ModalClose,
+  ModalContent,
+  ModalFooter,
+  ModalHeader,
+  ModalInput,
+  ModalTextarea,
+} from "../Components/Styled/Modal.style";
+import axios from "../api/axios";
 
 const Subject = () => {
   let params = useParams();
-  return <div>Subject {params.subjectId}</div>;
+  let navigate = useNavigate();
+
+  const { auth, setAuth } = useContext(AuthApi);
+  const [subject, setSubject] = useState(null);
+  const [loaded, setLoaded] = useState(false);
+  const [fetchError, setFetchError] = useState(false);
+  var totalTopics = 0;
+  var topics = [];
+
+  const [topicModalDisplay, setTopicModalDisplay] = useState("none");
+  const [topicTitle, setTopicTitle] = useState("");
+  const [topicContent, setTopicContent] = useState("");
+  const [errorMessage, setErrorMessage] = useState("");
+
+  useEffect(() => {
+    const url = `http://192.168.0.17:8080/public/subject/${params.subjectId}`;
+
+    const fetchData = async () => {
+      try {
+        const response = await fetch(url);
+        let cyclicGraph = await response.json();
+        let jsogStructure = JSOG.encode(cyclicGraph);
+        cyclicGraph = JSOG.decode(jsogStructure);
+        setSubject(cyclicGraph);
+        setLoaded(true);
+      } catch (error) {
+        setFetchError(true);
+      }
+    };
+
+    fetchData();
+  }, [params.subjectId]);
+
+  const handleAddTopicButtonClick = () => {
+    if (auth) {
+      setTopicModalDisplay("block");
+    } else {
+      navigate("/login");
+    }
+  };
+
+  const handleModalCloseClick = () => {
+    setTopicModalDisplay("none");
+  };
+
+  const handleTopicSubmit = async (e) => {
+    e.preventDefault();
+
+    if (!topicTitle.length < 1 && !topicContent.length < 1) {
+      const response = await axios(
+        `http://192.168.0.17:8080/secure/subject/${params.subjectId}/addThread`,
+        {
+          method: "post",
+          data: {
+            title: topicTitle,
+            content: topicContent,
+          },
+          withCredentials: true,
+        }
+      );
+      setErrorMessage("");
+      window.location.reload(false);
+    } else {
+      setErrorMessage("Полињата за наслов и содржина не смеат да бидат празни");
+    }
+  };
+
+  const handleContentChange = (e) => {
+    setTopicContent(e.target.value);
+  };
+
+  const handleTitleChange = (e) => {
+    setTopicTitle(e.target.value);
+  };
+
+  return loaded ? (
+    <>
+      <CurrentPageNav>
+        &#187;{" "}
+        <a
+          href={
+            "/university/" +
+            subject.studyProgramme.faculty.university.universityId
+          }
+        >
+          {subject.studyProgramme.faculty.university.universityName}
+        </a>{" "}
+        &#187;{" "}
+        <a href={"/faculty/" + subject.studyProgramme.faculty.facultyId}>
+          {subject.studyProgramme.faculty.facultyName}
+        </a>{" "}
+        &#187; <a href="#">{subject.subjectName}</a>
+      </CurrentPageNav>
+      <ProfessorCard>
+        <ProfessorCardName>{subject.subjectName}</ProfessorCardName>
+        <ProfessorCardSeparator />
+        <div style={{ marginTop: "10px" }}>
+          <ProfessorCardDetails fontSize="20px">
+            {subject.studyProgramme.studyProgrammeName} (
+            {subject.studyProgramme.cycle}
+            {"."}
+            {"циклус"})
+          </ProfessorCardDetails>
+          <ProfessorCardDetails fontSize="20px">
+            {subject.studyProgramme.faculty.facultyName}
+          </ProfessorCardDetails>
+          <ProfessorCardDetails fontSize="15px">
+            {subject.studyProgramme.faculty.university.universityName}
+          </ProfessorCardDetails>
+        </div>
+      </ProfessorCard>
+      <div style={{ height: "20px", marginBottom: "50px" }}>
+        <h3
+          style={{
+            float: "left",
+          }}
+        >
+          {subject.threads.map((thread) => {
+            if (thread.parent === null) {
+              totalTopics++;
+              topics.push(thread);
+            }
+          })}
+          {totalTopics} {totalTopics !== 1 ? "теми" : "тема"}
+        </h3>
+        {auth && (
+          <AddOpinionButton onClick={handleAddTopicButtonClick}>
+            Отвори тема
+          </AddOpinionButton>
+        )}
+      </div>
+      <Modal display={topicModalDisplay}>
+        <ModalContent>
+          <ModalHeader>
+            <ModalClose onClick={handleModalCloseClick}>&times;</ModalClose>
+            <h3 style={{ marginTop: "5px" }}>
+              Тема во врска со {subject.subjectName}
+            </h3>
+          </ModalHeader>
+          <form onSubmit={handleTopicSubmit}>
+            <ModalBody>
+              <label htmlFor="title">
+                <b>Наслов</b>:
+                <ModalInput
+                  id="title"
+                  value={topicTitle}
+                  onChange={handleTitleChange}
+                />
+              </label>
+              <label htmlFor="content">
+                <b>Содржина</b>:
+                <ModalTextarea
+                  id="content"
+                  rows="8"
+                  cols="100"
+                  value={topicContent}
+                  onChange={handleContentChange}
+                />
+              </label>
+            </ModalBody>
+            <p style={{ color: "red", marginLeft: "15px", marginTop: "10px" }}>
+              {errorMessage}
+            </p>
+            <ModalFooter type="submit">ОБЈАВИ</ModalFooter>
+          </form>
+        </ModalContent>
+      </Modal>
+      <div key={subject.subjectId}>
+        {topics.map((topic) => {
+          var numReplies = topic.children.length;
+          return (
+            <EntityUl key={topic.postId}>
+              <EntityLi bgcolor="cornsilk">
+                <a href={"/topic/" + topic.postId}>{topic.title}</a>
+                <EntityParam right="30px">
+                  <span style={{ fontWeight: "normal" }}>
+                    отворил:{" "}
+                    <a href={"/user/" + topic.author.id}>
+                      {topic.author.username}
+                    </a>
+                  </span>
+                  <span style={{ fontStyle: "normal" }}>,</span>{" "}
+                  {numReplies !== 1 ? (
+                    numReplies !== 0 ? (
+                      <span
+                        style={{
+                          fontWeight: "normal",
+                          opacity: numReplies === 0 ? "0.5" : "1",
+                        }}
+                      >
+                        <span
+                          style={{
+                            fontWeight: "bold",
+                            opacity: numReplies === 0 ? "0.5" : "1",
+                          }}
+                        >
+                          {numReplies}
+                        </span>{" "}
+                        реплики
+                      </span>
+                    ) : (
+                      <span
+                        style={{
+                          fontWeight: "normal",
+                          opacity: numReplies === 0 ? "0.5" : "1",
+                        }}
+                      >
+                        <span
+                          style={{
+                            fontWeight: "bold",
+                            opacity: numReplies === 0 ? "0.5" : "1",
+                          }}
+                        >
+                          {numReplies}
+                        </span>{" "}
+                        реплики
+                      </span>
+                    )
+                  ) : (
+                    <span style={{ fontWeight: "normal" }}>
+                      <span style={{ fontWeight: "bold" }}>{numReplies}</span>{" "}
+                      реплика
+                    </span>
+                  )}
+                </EntityParam>
+              </EntityLi>
+            </EntityUl>
+          );
+        })}
+      </div>
+    </>
+  ) : !fetchError ? (
+    <div>
+      <p style={{ marginTop: "140px" }}>се вчитува...</p>
+      <Outlet />
+    </div>
+  ) : (
+    <div style={{ marginTop: "140px" }}>
+      <h1 style={{ textAlign: "center" }}>Страницата не е пронајдена.</h1>
+    </div>
+  );
 };
 
Index: reactapp/src/Pages/Topic.js
===================================================================
--- reactapp/src/Pages/Topic.js	(revision c68150fac8ab8e6f5dc5b8b2a9cd73d391dc1aa7)
+++ reactapp/src/Pages/Topic.js	(revision c68150fac8ab8e6f5dc5b8b2a9cd73d391dc1aa7)
@@ -0,0 +1,538 @@
+import React, { useState, useEffect, useContext } from "react";
+import { useParams, Outlet } from "react-router-dom";
+import JSOG from "jsog";
+import { CurrentPageNav } from "../Components/Styled/Main.style";
+import AuthApi from "../api/AuthApi";
+import { useNavigate } from "react-router-dom";
+import {
+  OpinionCard,
+  OpinionCardContent,
+  OpinionCardContentTime,
+  OpinionReplyCard,
+  OpinionReplyCardContent,
+  OpinionReplyCardContentTime,
+  VoteCount,
+} from "../Components/Styled/OpinionCard.style";
+import { dateConverter } from "../Util/dateConverter";
+import { StyledFontAwesomeIcon } from "../Components/Styled/OpinionCard.style";
+import { solid } from "@fortawesome/fontawesome-svg-core/import.macro";
+import {
+  AddOpinionButton,
+  Modal,
+  ModalContent,
+  ModalHeader,
+  ModalClose,
+  ModalBody,
+  ModalTextarea,
+  ModalFooter,
+} from "../Components/Styled/Modal.style";
+import axios from "../api/axios";
+
+const Topic = () => {
+  let params = useParams();
+  let navigate = useNavigate();
+  const { auth, setAuth } = useContext(AuthApi);
+
+  const [thread, setThread] = useState(null);
+  const [loadedThread, setLoadedThread] = useState(false);
+  const [user, setUser] = useState(null);
+  const [loadedUser, setLoadedUser] = useState(false);
+  const [fetchError, setFetchError] = useState(false);
+
+  const [postModalDisplay, setPostModalDisplay] = useState("none");
+  const [postContent, setPostContent] = useState("");
+  const [replyModalDisplay, setReplyModalDisplay] = useState("none");
+  const [replyContent, setReplyContent] = useState("");
+  const [postForModal, setPostForModal] = useState(null);
+  const [errorMessage, setErrorMessage] = useState("");
+
+  useEffect(() => {
+    const url1 = `http://192.168.0.17:8080/public/thread/${params.topicId}`;
+    const url2 = `http://192.168.0.17:8080/secure/currentUser`;
+
+    const fetchTopic = async () => {
+      try {
+        const response = await fetch(url1);
+        let cyclicGraph = await response.json();
+        let jsogStructure = JSOG.encode(cyclicGraph);
+        cyclicGraph = JSOG.decode(jsogStructure);
+        setThread(cyclicGraph);
+        setLoadedThread(true);
+      } catch (error) {
+        setFetchError(true);
+      }
+    };
+
+    const fetchUser = async () => {
+      try {
+        const response = await axios.get(url2, { withCredentials: true });
+        var cyclicGraph = await response.data;
+        var jsogStructure = JSOG.encode(cyclicGraph);
+        cyclicGraph = JSOG.decode(jsogStructure);
+        setUser(cyclicGraph);
+        setLoadedUser(true);
+      } catch (error) {
+        setFetchError(true);
+      }
+    };
+
+    fetchTopic().then(fetchUser);
+  }, []);
+
+  const handleReply = (post) => {
+    if (auth) {
+      setReplyModalDisplay("block");
+      setPostForModal(post);
+    } else {
+      navigate("/login");
+    }
+  };
+
+  const handleReplyContentChange = (e) => {
+    setReplyContent(e.target.value);
+  };
+
+  const handleReplySubmit = async (e, postId) => {
+    e.preventDefault();
+
+    if (!replyContent.length < 1) {
+      const response = await axios(
+        `http://192.168.0.17:8080/secure/subject/${thread.targetSubject.subjectId}/replyToThread/${postId}`,
+        {
+          method: "post",
+          data: {
+            content: replyContent,
+          },
+          withCredentials: true,
+        }
+      );
+      setErrorMessage("");
+      window.location.reload(false);
+    } else {
+      setErrorMessage("Полето за содржина не смее да биде празно");
+    }
+  };
+
+  const handleAddOpinionButtonClick = () => {
+    if (auth) {
+      setPostModalDisplay("block");
+    } else {
+      navigate("/login");
+    }
+  };
+
+  const handlePostSubmit = async (e) => {
+    e.preventDefault();
+    if (!postContent.length < 1) {
+      const response = await axios(
+        `http://192.168.0.17:8080/secure/subject/${thread.targetSubject.subjectId}/replyToThread/${params.topicId}`,
+        {
+          method: "post",
+          data: {
+            content: postContent,
+          },
+          withCredentials: true,
+        }
+      );
+      setErrorMessage("");
+      window.location.reload(false);
+    } else {
+      setErrorMessage("Полето за содржина не смее да биде празно");
+    }
+  };
+  const handleModalCloseClick = () => {
+    setPostModalDisplay("none");
+    setReplyModalDisplay("none");
+  };
+  const handleContentChange = (e) => {
+    setPostContent(e.target.value);
+  };
+
+  const handleLike = async (post) => {
+    if (auth) {
+      if (
+        loadedUser &&
+        user &&
+        !post.votes.some((e) => e.user.id === user.id)
+      ) {
+        const response = await axios(
+          `http://192.168.0.17:8080/secure/upvoteThread/${post.postId}`,
+          {
+            method: "get",
+            withCredentials: true,
+          }
+        );
+        window.location.reload(false);
+      } else {
+        return;
+      }
+    } else {
+      navigate("/login");
+    }
+  };
+
+  const handleDislike = async (post) => {
+    if (auth) {
+      if (
+        loadedUser &&
+        user &&
+        !post.votes.some((e) => e.user.id === user.id)
+      ) {
+        const response = await axios(
+          `http://192.168.0.17:8080/secure/downvoteThread/${post.postId}`,
+          {
+            method: "get",
+            withCredentials: true,
+          }
+        );
+
+        window.location.reload(false);
+      } else {
+        return;
+      }
+    } else {
+      navigate("/login");
+    }
+  };
+
+  function displayChildPosts(child, parentPostAuthorUsername, replyIndent) {
+    if (child == null) return;
+    //postCount = renderedOpinionIds.push(child.postId);
+    return (
+      <div key={child.postId}>
+        <OpinionReplyCard indent={replyIndent + "px"}>
+          <OpinionReplyCardContent>
+            <p style={{ fontStyle: "italic", marginBottom: "10px" }}>
+              <a href={"/user/" + child.author.id}>{child.author.username}</a>{" "}
+              му реплицирал на {parentPostAuthorUsername}
+            </p>
+            <p style={{ marginBottom: "10px", maxWidth: "90%" }}>
+              {child.content}
+            </p>
+            <OpinionReplyCardContentTime>
+              {dateConverter(
+                new Date(child.timePosted).toString().slice(4, -43)
+              )}
+            </OpinionReplyCardContentTime>
+
+            <div
+              style={{
+                display:
+                  !auth || (auth && loadedUser && user.id !== child.author.id)
+                    ? "block"
+                    : "none",
+              }}
+            >
+              <StyledFontAwesomeIcon
+                icon={solid("thumbs-up")}
+                right={50 + "px"}
+                color={
+                  auth && loadedUser && user
+                    ? child.votes.some(
+                        (e) => e.vote === "UPVOTE" && e.user.id === user.id
+                      )
+                      ? "greenyellow"
+                      : "darkgrey"
+                    : "darkgrey"
+                }
+                onClick={() => handleLike(child)}
+              />
+
+              <VoteCount right={50 + "px"}>
+                {child.votes.filter((v) => v.vote === "UPVOTE").length}
+              </VoteCount>
+
+              <StyledFontAwesomeIcon
+                icon={solid("thumbs-down")}
+                right={10 + "px"}
+                color={
+                  auth && loadedUser && user
+                    ? child.votes.some(
+                        (e) => e.vote === "DOWNVOTE" && e.user.id === user.id
+                      )
+                      ? "indianred"
+                      : "darkgrey"
+                    : "darkgrey"
+                }
+                onClick={() => handleDislike(child)}
+              />
+
+              <VoteCount right={50 + "px"}>
+                {child.votes.filter((v) => v.vote === "DOWNVOTE").length}
+              </VoteCount>
+
+              <StyledFontAwesomeIcon
+                icon={solid("reply")}
+                right={90 + "px"}
+                color="darkgrey"
+                onClick={() => handleReply(child)}
+              />
+            </div>
+          </OpinionReplyCardContent>
+
+          {child.children.map((childOfChild) =>
+            displayChildPosts(
+              childOfChild,
+              child.author.username,
+              replyIndent + 30
+            )
+          )}
+        </OpinionReplyCard>
+      </div>
+    );
+  }
+
+  return loadedThread && thread.length !== 0 ? (
+    <>
+      <CurrentPageNav>
+        &#187;{" "}
+        <a
+          href={
+            "/university/" +
+            thread.targetSubject.studyProgramme.faculty.university.universityId
+          }
+        >
+          {
+            thread.targetSubject.studyProgramme.faculty.university
+              .universityName
+          }
+        </a>{" "}
+        &#187;{" "}
+        <a
+          href={
+            "/faculty/" + thread.targetSubject.studyProgramme.faculty.facultyId
+          }
+        >
+          {thread.targetSubject.studyProgramme.faculty.facultyName}
+        </a>{" "}
+        &#187;{" "}
+        <a href={"/subject/" + thread.targetSubject.subjectId}>
+          {thread.targetSubject.subjectName}
+        </a>
+      </CurrentPageNav>
+      <div style={{ height: "20px", marginBottom: "50px", marginTop: "50px" }}>
+        <h3 style={{ float: "left" }}>{thread.title}</h3>
+        {auth && (
+          <AddOpinionButton onClick={handleAddOpinionButtonClick}>
+            Реплицирај
+          </AddOpinionButton>
+        )}
+      </div>
+      <Modal display={postModalDisplay}>
+        <ModalContent>
+          <ModalHeader>
+            <ModalClose onClick={handleModalCloseClick}>&times;</ModalClose>
+            <h3 style={{ marginTop: "5px" }}>
+              Реплика на темата {thread.title}
+            </h3>
+          </ModalHeader>
+          <form onSubmit={handlePostSubmit}>
+            <ModalBody>
+              <label htmlFor="content">
+                <b>Содржина</b>:
+                <ModalTextarea
+                  id="content"
+                  rows="8"
+                  cols="100"
+                  value={postContent}
+                  onChange={handleContentChange}
+                />
+              </label>
+            </ModalBody>
+            <p style={{ color: "red", marginLeft: "15px", marginTop: "10px" }}>
+              {errorMessage}
+            </p>
+            <ModalFooter type="submit">ОБЈАВИ</ModalFooter>
+          </form>
+        </ModalContent>
+      </Modal>
+      <OpinionCard>
+        <OpinionCardContent>
+          <p style={{ fontStyle: "italic", marginBottom: "10px" }}>
+            <a href={"/user/" + thread.author.id}>{thread.author.username}</a>{" "}
+            напишал
+          </p>
+          <p style={{ marginBottom: "10px", maxWidth: "90%" }}>
+            {thread.content}
+          </p>
+          <OpinionCardContentTime>
+            {dateConverter(
+              new Date(thread.timePosted).toString().slice(4, -43)
+            )}
+          </OpinionCardContentTime>
+          <div
+            style={{
+              display:
+                !auth || (auth && loadedUser && user.id !== thread.author.id)
+                  ? "block"
+                  : "none",
+            }}
+          >
+            <StyledFontAwesomeIcon
+              icon={solid("thumbs-up")}
+              right={50 + "px"}
+              color={
+                auth && loadedUser && user
+                  ? thread.votes.some(
+                      (e) => e.vote === "UPVOTE" && e.user.id === user.id
+                    )
+                    ? "greenyellow"
+                    : "darkgrey"
+                  : "darkgrey"
+              }
+              onClick={() => handleLike(thread)}
+            />
+
+            <VoteCount right={50 + "px"}>
+              {thread.votes.filter((v) => v.vote === "UPVOTE").length}
+            </VoteCount>
+
+            <StyledFontAwesomeIcon
+              icon={solid("thumbs-down")}
+              right={10 + "px"}
+              color={
+                auth && loadedUser && user
+                  ? thread.votes.some(
+                      (e) => e.vote === "DOWNVOTE" && e.user.id === user.id
+                    )
+                    ? "indianred"
+                    : "darkgrey"
+                  : "darkgrey"
+              }
+              onClick={() => handleDislike(thread)}
+            />
+
+            <VoteCount right={10 + "px"}>
+              {thread.votes.filter((v) => v.vote === "DOWNVOTE").length}
+            </VoteCount>
+          </div>
+        </OpinionCardContent>
+      </OpinionCard>
+      {thread.children.map((directChild) => {
+        return (
+          <OpinionCard key={directChild.postId}>
+            <OpinionCardContent>
+              <p style={{ fontStyle: "italic", marginBottom: "10px" }}>
+                <a href={"/user/" + directChild.author.id}>
+                  {directChild.author.username}
+                </a>{" "}
+                напишал
+              </p>
+              <p style={{ marginBottom: "10px", maxWidth: "90%" }}>
+                {directChild.content}
+              </p>
+              <OpinionCardContentTime>
+                {dateConverter(
+                  new Date(directChild.timePosted).toString().slice(4, -43)
+                )}
+              </OpinionCardContentTime>
+              <div
+                style={{
+                  display:
+                    !auth ||
+                    (auth && loadedUser && user.id !== directChild.author.id)
+                      ? "block"
+                      : "none",
+                }}
+              >
+                <StyledFontAwesomeIcon
+                  icon={solid("thumbs-up")}
+                  right={50 + "px"}
+                  color={
+                    auth && loadedUser && user
+                      ? directChild.votes.some(
+                          (e) => e.vote === "UPVOTE" && e.user.id === user.id
+                        )
+                        ? "greenyellow"
+                        : "darkgrey"
+                      : "darkgrey"
+                  }
+                  onClick={() => handleLike(directChild)}
+                />
+
+                <VoteCount right={50 + "px"}>
+                  {directChild.votes.filter((v) => v.vote === "UPVOTE").length}
+                </VoteCount>
+
+                <StyledFontAwesomeIcon
+                  icon={solid("thumbs-down")}
+                  right={10 + "px"}
+                  color={
+                    auth && loadedUser && user
+                      ? directChild.votes.some(
+                          (e) => e.vote === "DOWNVOTE" && e.user.id === user.id
+                        )
+                        ? "indianred"
+                        : "darkgrey"
+                      : "darkgrey"
+                  }
+                  onClick={() => handleDislike(directChild)}
+                />
+
+                <VoteCount right={10 + "px"}>
+                  {
+                    directChild.votes.filter((v) => v.vote === "DOWNVOTE")
+                      .length
+                  }
+                </VoteCount>
+
+                <StyledFontAwesomeIcon
+                  icon={solid("reply")}
+                  right={90 + "px"}
+                  color="darkgrey"
+                  onClick={() => handleReply(directChild)}
+                />
+              </div>
+            </OpinionCardContent>
+            {directChild.children.map((child) =>
+              displayChildPosts(child, directChild.author.username, 30)
+            )}
+          </OpinionCard>
+        );
+      })}
+      {postForModal && (
+        <Modal display={replyModalDisplay}>
+          <ModalContent>
+            <ModalHeader>
+              <ModalClose onClick={handleModalCloseClick}>&times;</ModalClose>
+              <h3 style={{ marginTop: "5px" }}>
+                Реплика на {postForModal.author.username}
+              </h3>
+            </ModalHeader>
+            <form onSubmit={(e) => handleReplySubmit(e, postForModal.postId)}>
+              <ModalBody>
+                <label htmlFor="content">
+                  <b>Содржина</b>:
+                  <ModalTextarea
+                    id="content"
+                    rows="8"
+                    cols="100"
+                    value={replyContent}
+                    onChange={handleReplyContentChange}
+                  />
+                </label>
+              </ModalBody>
+              <p
+                style={{ color: "red", marginLeft: "15px", marginTop: "10px" }}
+              >
+                {errorMessage}
+              </p>
+              <ModalFooter type="submit">РЕПЛИЦИРАЈ</ModalFooter>
+            </form>
+          </ModalContent>
+        </Modal>
+      )}
+    </>
+  ) : !fetchError && !loadedThread ? (
+    <div>
+      <p style={{ marginTop: "140px" }}>се вчитува...</p>
+      <Outlet />
+    </div>
+  ) : (
+    <div style={{ marginTop: "140px" }}>
+      <h1 style={{ textAlign: "center" }}>Страницата не е пронајдена.</h1>
+    </div>
+  );
+};
+
+export default Topic;
Index: reactapp/src/Pages/University.js
===================================================================
--- reactapp/src/Pages/University.js	(revision 8d831804abcee99d72c207c57a8f16e54b1329a2)
+++ reactapp/src/Pages/University.js	(revision c68150fac8ab8e6f5dc5b8b2a9cd73d391dc1aa7)
@@ -18,5 +18,5 @@
   let params = useParams();
   const [loaded, setLoaded] = useState(false);
-  const [faculties, setFaculties] = useState(false);
+  const [faculties, setFaculties] = useState(null);
   const [fetchError, setFetchError] = useState(false);
 
@@ -39,5 +39,5 @@
   }, [params.universityId]);
 
-  return loaded ? (
+  return loaded && !fetchError && faculties.length !== 0 ? (
     <>
       <CurrentPageNav>
@@ -74,5 +74,5 @@
                   {faculty.facultyName}
                 </a>
-                <EntityParam>
+                <EntityParam right="30px">
                   {totalSections}{" "}
                   {totalSections !== 1 ? (
@@ -103,5 +103,5 @@
       </div>
     </>
-  ) : !fetchError ? (
+  ) : !fetchError && !loaded ? (
     <div>
       <p style={{ marginTop: "140px" }}>се вчитува...</p>
Index: reactapp/src/Pages/UserDashboard.js
===================================================================
--- reactapp/src/Pages/UserDashboard.js	(revision 8d831804abcee99d72c207c57a8f16e54b1329a2)
+++ reactapp/src/Pages/UserDashboard.js	(revision c68150fac8ab8e6f5dc5b8b2a9cd73d391dc1aa7)
@@ -1,8 +1,7 @@
-import React, { useEffect } from "react";
+import React, { useEffect, useState, useContext } from "react";
 import {
   OpinionCard,
   OpinionCardContent,
   OpinionCardContentTime,
-  OpinionCardContentTitle,
 } from "../Components/Styled/OpinionCard.style";
 import {
@@ -11,14 +10,47 @@
 } from "../Components/Styled/UserDetails.style";
 import { dateConverter } from "../Util/dateConverter";
+import axios from "../api/axios";
+import JSOG from "jsog";
+import AuthApi from "../api/AuthApi";
 
-function UserDashboard({ user, userLoaded }) {
+function UserDashboard() {
+  const { auth, setAuth } = useContext(AuthApi);
+
+  const [user, setUser] = useState(null);
+  const [loadedUser, setLoadedUser] = useState(false);
+  const [fetchError, setFetchError] = useState(false);
+
   useEffect(() => {
-    const timer = setTimeout(() => {
-      if (user === null) window.location.reload(false);
-    }, 3000);
-    return () => clearTimeout(timer);
+    const url = `http://192.168.0.17:8080/secure/currentUser`;
+
+    const fetchUser = async () => {
+      try {
+        const response = await axios.get(url, { withCredentials: true });
+        var cyclicGraph = await response.data;
+        var jsogStructure = JSOG.encode(cyclicGraph);
+        cyclicGraph = JSOG.decode(jsogStructure);
+        setUser(cyclicGraph);
+        setLoadedUser(true);
+      } catch (error) {
+        setFetchError(true);
+      }
+    };
+
+    if (auth) fetchUser();
   }, []);
 
-  return userLoaded ? (
+  // useEffect(() => {
+  //   const timer = setTimeout(() => {
+  //     if (user === null) window.location.reload(false); <---- :-)
+  //   }, 3000);
+  //   return () => clearTimeout(timer);
+  // }, []);
+
+  function findParentThread(post) {
+    if (post.parent === null) return post;
+    return findParentThread(post.parent);
+  }
+
+  return loadedUser ? (
     <>
       <h3>Кориснички податоци:</h3>
@@ -49,12 +81,23 @@
             <OpinionCard>
               <OpinionCardContent>
-                <p>
-                  Во дискусија за{" "}
-                  <a href={"/professor/" + post.targetProfessor.professorId}>
-                    {post.targetProfessor.professorName}
-                  </a>
+                <p style={{ fontStyle: "italic", marginBottom: "10px" }}>
+                  во дискусија за{" "}
+                  {post.targetProfessor !== undefined ? (
+                    <a href={"/professor/" + post.targetProfessor.professorId}>
+                      {post.targetProfessor.professorName}
+                    </a>
+                  ) : (
+                    <a
+                      href={
+                        post.parent === null
+                          ? "/topic/" + post.postId
+                          : "/topic/" + findParentThread(post).postId
+                      }
+                    >
+                      {post.targetSubject.subjectName}
+                    </a>
+                  )}
                 </p>
-                <OpinionCardContentTitle>{post.title}</OpinionCardContentTitle>
-                <p>{post.content}</p>
+                <p style={{ marginBottom: "10px" }}>{post.content}</p>
                 <OpinionCardContentTime>
                   {dateConverter(
