From 0adad598c20e79f1870c4079f753439f07325d30 Mon Sep 17 00:00:00 2001 From: JDC-horizons Date: Fri, 20 Sep 2024 13:53:08 +0100 Subject: [PATCH 1/2] cohort page built --- src/App.js | 39 +++++ src/components/navigation/index.js | 70 ++++---- src/components/posts/index.js | 44 ++--- src/pages/cohortViewStudent/index.js | 221 ++++++++++++++++++++++++++ src/pages/cohortViewStudent/style.css | 87 ++++++++++ src/pages/dashboard/index.js | 53 +++++- src/pages/login/index.js | 22 +-- src/pages/register/index.js | 25 ++- 8 files changed, 478 insertions(+), 83 deletions(-) create mode 100644 src/pages/cohortViewStudent/index.js create mode 100644 src/pages/cohortViewStudent/style.css diff --git a/src/App.js b/src/App.js index f871e4c5..c98ab98c 100644 --- a/src/App.js +++ b/src/App.js @@ -11,6 +11,7 @@ import { AuthProvider, ProtectedRoute } from "./context/auth"; import { ModalProvider } from "./context/modal"; import { ProfileProvider } from "./context/profile"; import Welcome from "./pages/welcome"; +import CohortViewStudent from "./pages/cohortViewStudent"; const App = () => { return ( @@ -24,6 +25,7 @@ const App = () => { } /> } /> +<<<<<<< Updated upstream { +======= + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + +>>>>>>> Stashed changes ); }; diff --git a/src/components/navigation/index.js b/src/components/navigation/index.js index 36fca654..57865795 100644 --- a/src/components/navigation/index.js +++ b/src/components/navigation/index.js @@ -1,41 +1,41 @@ -import { NavLink } from "react-router-dom" -import CohortIcon from "../../assets/icons/cohortIcon" -import HomeIcon from "../../assets/icons/homeIcon" -import ProfileIcon from "../../assets/icons/profileIcon" -import useAuth from "../../hooks/useAuth" -import './style.css' +import { NavLink } from "react-router-dom"; +import CohortIcon from "../../assets/icons/cohortIcon"; +import HomeIcon from "../../assets/icons/homeIcon"; +import ProfileIcon from "../../assets/icons/profileIcon"; +import useAuth from "../../hooks/useAuth"; +import "./style.css"; const Navigation = () => { - const { token, user } = useAuth(); + const { token, user } = useAuth(); - if (!token) { - return null; - } + if (!token) { + return null; + } - return ( - - ); -} + return ( + + ); +}; export default Navigation; diff --git a/src/components/posts/index.js b/src/components/posts/index.js index 785b6798..8d29caea 100644 --- a/src/components/posts/index.js +++ b/src/components/posts/index.js @@ -2,28 +2,28 @@ import { useEffect, useState } from "react"; import Post from "../post"; import { getPosts } from "../../service/apiClient"; -const Posts = ({ postCreated }) => { - const [posts, setPosts] = useState([]) +const Posts = () => { + const [posts, setPosts] = useState([]); - useEffect(() => { - getPosts().then((r) => { - setPosts(r.sort((a, b) => b.id - a.id)) - }) - }, [postCreated]) + useEffect(() => { + getPosts().then(setPosts); + }, []); - return ( - <> - {posts ? posts.map(post => { - return - }) :

No posts to show.

} - - ) -} + return ( + <> + {posts.map((post) => { + return ( + + ); + })} + + ); +}; -export default Posts \ No newline at end of file +export default Posts; diff --git a/src/pages/cohortViewStudent/index.js b/src/pages/cohortViewStudent/index.js new file mode 100644 index 00000000..984957d1 --- /dev/null +++ b/src/pages/cohortViewStudent/index.js @@ -0,0 +1,221 @@ +import { useState, useEffect, useContext } from "react"; +import useAuth from "../../hooks/useAuth"; +import "./style.css"; +import Card from "../../components/card"; + +const CohortViewStudent = () => { + const { token, user } = useAuth(); + const apiUrl = process.env.REACT_APP_API_URL; + + const [cohort, setCohort] = useState(""); + const [teachers, setTeachers] = useState([]); + const [loading, setLoading] = useState(true); + const [grades, setGrades] = useState(); + const [completion, setCompletion] = useState(); + + //function to calculate completion for user's grades + function calculateCompletion(data) { + let result = { + modules: { total: 0, completed: 0 }, + units: { total: 0, completed: 0 }, + exercises: { total: 0, completed: 0 }, + }; + + data.grades.forEach((module) => { + result.modules.total += 1; + let moduleCompleted = true; + + module.units.forEach((unit) => { + result.units.total += 1; + let unitCompleted = true; + + unit.exercises.forEach((exercise) => { + result.exercises.total += 1; + + if (exercise.grade >= 70) { + result.exercises.completed += 1; + } else { + unitCompleted = false; + moduleCompleted = false; + } + }); + + if (unitCompleted) { + result.units.completed += 1; + } + }); + + if (moduleCompleted) { + result.modules.completed += 1; + } + }); + + return result; + } + + // useEffect to load required page data + useEffect(() => { + const fetchData = async () => { + try { + setLoading(true); + + // Fetch cohort information + const cohortResponse = await fetch( + `${apiUrl}/cohorts/${user.cohort_id}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + } + ); + const cohortData = await cohortResponse.json(); + setCohort(cohortData.data); + + // Fetch list of teachers + const usersResponse = await fetch(`${apiUrl}/users`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + }); + const usersData = await usersResponse.json(); + + const teacherList = usersData.data.users + .filter((userObj) => userObj.user.role === "TEACHER") + .map((userObj) => userObj.user); + setTeachers(teacherList); + + // Fetch grades and calculate completion + const gradesResponse = await fetch( + `${apiUrl}/users/${user.id}/completion`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + } + ); + const gradesData = await gradesResponse.json(); + setGrades(gradesData); + setCompletion(calculateCompletion(gradesData)); + } catch (error) { + console.error("Error fetching data:", error); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, [apiUrl, token, user.cohort_id, user.id]); + + return ( +
+
+ +

My Cohort

+
+
+

< >

+
+
+

Cohort Name

+

Cohort Dates

+
+
+
    + {loading ? ( +
  • +

    Loading...

    +
  • + ) : ( + cohort?.users.map((user) => ( +
  • +
    + {/*

    {`${user.id}`}

    */} +

    JB

    +
    +

    User Name

    +
  • + )) + )} +
  • +
    +

    AB

    +
    +

    User Name

    +
  • +
  • +
    +

    AB

    +
    +

    User Name

    +
  • +
  • +
    +

    AB

    +
    +

    User Name

    +
  • +
+
+
+
+
+ +

Teachers

+
    + {loading ? ( +
  • +

    Loading...

    +
  • + ) : ( + teachers.map((teacher) => ( +
  • +
    +

    {`${teacher.firstName[0]}${teacher.lastName[0]}`}

    +
    +
    +

    {`${teacher.firstName} ${teacher.lastName}`}

    +

    + {teacher.specialism ? teacher.specialism : "General"} +

    +
    +
  • + )) + )} +
+
+
+
+ +

My Exercises

+ {completion ? ( +
+

Modules:

+

+ {`${completion.modules.completed}/${completion.modules.total} completed`} +

+

Units:

+

+ {`${completion.units.completed}/${completion.units.total} completed`} +

+

Exercises:

+

+ {`${completion.exercises.completed}/${completion.exercises.total} completed`} +

+
+ ) : ( +

Loading exercise data...

+ )} +
+
+
+
+ ); +}; + +export default CohortViewStudent; diff --git a/src/pages/cohortViewStudent/style.css b/src/pages/cohortViewStudent/style.css new file mode 100644 index 00000000..bfc30902 --- /dev/null +++ b/src/pages/cohortViewStudent/style.css @@ -0,0 +1,87 @@ +#cohort-page-container { + display: flex; + gap: 25px; + margin: 24px; + width: calc(100vw - 201px); +} + +.bold-text { + font-weight: 600; + font-size: 24px; + line-height: 32px; +} + +.light-text { + font-weight: 200; + font-size: 20px; + line-height: 28px; +} + +#cohort-page-container .profile-icon { + background-color: #28c846; + display: flex; + justify-content: center; + align-items: center; +} + +.cohort-section-header { + border-bottom: 1px solid #e6ebf5; + display: flex; + gap: 15px; + padding: 15px 0; +} + +.cohort-section { + width: 65%; +} + +.right-side-container { + width: 35%; +} + +#cohort-page-container .profile-icon .icon-text { + color: white; + font-size: 2.3rem; + font-weight: 400; + margin-bottom: 4px; +} + +.cohort-section h3, +.teachers-section h3, +.exercises-section h3 { + border-bottom: 1px solid #e6ebf5; + padding-bottom: 24px; +} + +.cohort-section .cohort-details { + width: 100%; + display: flex; + flex-wrap: wrap; + gap: space-between; +} + +.user-card { + flex: 0 0 50%; + box-sizing: border-box; + margin-top: 10px; + display: flex; + flex-direction: row; + align-items: center; + gap: 10px; +} + +.exercise-details-container { + width: 100%; + display: flex; + flex-wrap: wrap; +} + +.exercise-details-container .aligned-left { + width: 35%; + margin-top: 5px; +} + +.exercise-details-container .aligned-right { + width: 65%; + margin-top: 5px; +} diff --git a/src/pages/dashboard/index.js b/src/pages/dashboard/index.js index 8b8c62fe..bdfa8b6e 100644 --- a/src/pages/dashboard/index.js +++ b/src/pages/dashboard/index.js @@ -4,13 +4,14 @@ import Button from "../../components/button"; import Card from "../../components/card"; import CreatePostModal from "../../components/createPostModal"; import TextInput from "../../components/form/textInput"; -import Posts from "../../components/posts"; +// import Posts from "../../components/posts"; import useModal from "../../hooks/useModal"; import useAuth from "../../hooks/useAuth"; import { getInitials } from "../../service/getInitials"; import "./style.css"; const Dashboard = () => { +<<<<<<< Updated upstream const [searchVal, setSearchVal] = useState(""); // Search input state const [searchResults, setSearchResults] = useState([]); // State for search results const [isLoading, setIsLoading] = useState(false); // Loading state @@ -113,6 +114,56 @@ const Dashboard = () => { ) : ( !isLoading && searchVal &&

No users found.

// Show message if no users found )} +======= + const [searchVal, setSearchVal] = useState(""); + const { user } = useAuth(); + + const onChange = (e) => { + setSearchVal(e.target.value); + }; + + // Use the useModal hook to get the openModal and setModal functions + const { openModal, setModal } = useModal(); + + // Create a function to run on user interaction + const showModal = () => { + // Use setModal to set the header of the modal and the component the modal should render + setModal("Create a post", ); // CreatePostModal is just a standard React component, nothing special + + // Open the modal! + openModal(); + }; + + return ( + <> +
+ +
+
+

{`${user.firstName[0]}${user.lastName[0]}`}

+
+
+
+ + {/* */} +
+ + diff --git a/src/pages/login/index.js b/src/pages/login/index.js index 087a46b0..b2a88a71 100644 --- a/src/pages/login/index.js +++ b/src/pages/login/index.js @@ -3,7 +3,7 @@ import Button from "../../components/button"; import TextInput from "../../components/form/textInput"; import useAuth from "../../hooks/useAuth"; import CredentialsCard from "../../components/credentials"; -import { validateEmail, validatePassword } from "../../utils/validations"; +// import { validateEmail, validatePassword } from "../../utils/validations"; import "./login.css"; const Login = () => { @@ -19,18 +19,18 @@ const Login = () => { const handleSubmit = (e) => { e.preventDefault(); - const emailError = validateEmail(formData.email); - const passwordError = validatePassword(formData.password) - ? "" - : "Password must be at least 8 characters long."; + // const emailError = validateEmail(formData.email); + // const passwordError = validatePassword(formData.password) + // ? "" + // : "Password must be at least 8 characters long."; - if (emailError || passwordError) { - setErrors({ email: emailError, password: passwordError }); - return; - } + // if (emailError || passwordError) { + // setErrors({ email: emailError, password: passwordError }); + // return; + // } - // If no errors, proceed with login - onLogin(formData.email, formData.password); + // // If no errors, proceed with login + // onLogin(formData.email, formData.password); }; return ( diff --git a/src/pages/register/index.js b/src/pages/register/index.js index 2d9ab0ec..dc60ed2a 100644 --- a/src/pages/register/index.js +++ b/src/pages/register/index.js @@ -3,7 +3,7 @@ import Button from "../../components/button"; import TextInput from "../../components/form/textInput"; import useAuth from "../../hooks/useAuth"; import CredentialsCard from "../../components/credentials"; -import { validateEmail, validatePassword } from "../../utils/validations"; +// import { validateEmail, validatePassword } from "../../utils/validations"; import "./register.css"; const Register = () => { @@ -17,19 +17,16 @@ const Register = () => { }; const handleSubmit = (e) => { - e.preventDefault(); - - const emailError = validateEmail(formData.email); - const passwordError = validatePassword(formData.password); - - if (emailError || passwordError) { - // If there are any validation errors, set them - setErrors({ email: emailError, password: passwordError }); - return; - } - - // If both validations pass, proceed with registration - onRegister(formData.email, formData.password); + // e.preventDefault(); + // const emailError = validateEmail(formData.email); + // const passwordError = validatePassword(formData.password); + // if (emailError || passwordError) { + // // If there are any validation errors, set them + // setErrors({ email: emailError, password: passwordError }); + // return; + // } + // // If both validations pass, proceed with registration + // onRegister(formData.email, formData.password); }; return ( From 5cd57f761c71e0024d4922ae7fb7ede687e66155 Mon Sep 17 00:00:00 2001 From: JDC-horizons Date: Fri, 20 Sep 2024 14:37:10 +0100 Subject: [PATCH 2/2] fixed issues --- src/App.js | 47 ++---------- src/pages/cohortViewStudent/index.js | 26 +++---- src/pages/dashboard/index.js | 105 --------------------------- 3 files changed, 19 insertions(+), 159 deletions(-) diff --git a/src/App.js b/src/App.js index c98ab98c..48fb3e59 100644 --- a/src/App.js +++ b/src/App.js @@ -25,7 +25,6 @@ const App = () => { } /> } /> -<<<<<<< Updated upstream { } /> - { } /> + + + + } + /> -======= - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - ->>>>>>> Stashed changes ); }; diff --git a/src/pages/cohortViewStudent/index.js b/src/pages/cohortViewStudent/index.js index 984957d1..8502b409 100644 --- a/src/pages/cohortViewStudent/index.js +++ b/src/pages/cohortViewStudent/index.js @@ -7,11 +7,11 @@ const CohortViewStudent = () => { const { token, user } = useAuth(); const apiUrl = process.env.REACT_APP_API_URL; - const [cohort, setCohort] = useState(""); + const [cohort, setCohort] = useState(null); const [teachers, setTeachers] = useState([]); const [loading, setLoading] = useState(true); - const [grades, setGrades] = useState(); - const [completion, setCompletion] = useState(); + const [grades, setGrades] = useState(null); + const [completion, setCompletion] = useState(null); //function to calculate completion for user's grades function calculateCompletion(data) { @@ -60,16 +60,13 @@ const CohortViewStudent = () => { setLoading(true); // Fetch cohort information - const cohortResponse = await fetch( - `${apiUrl}/cohorts/${user.cohort_id}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, - } - ); + const cohortResponse = await fetch(`${apiUrl}/cohorts/1`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + }); const cohortData = await cohortResponse.json(); setCohort(cohortData.data); @@ -110,7 +107,7 @@ const CohortViewStudent = () => { }; fetchData(); - }, [apiUrl, token, user.cohort_id, user.id]); + }, [apiUrl, token, user]); return (
@@ -135,7 +132,6 @@ const CohortViewStudent = () => { cohort?.users.map((user) => (
  • - {/*

    {`${user.id}`}

    */}

    JB

    User Name

    diff --git a/src/pages/dashboard/index.js b/src/pages/dashboard/index.js index bdfa8b6e..b034fe1f 100644 --- a/src/pages/dashboard/index.js +++ b/src/pages/dashboard/index.js @@ -11,110 +11,6 @@ import { getInitials } from "../../service/getInitials"; import "./style.css"; const Dashboard = () => { -<<<<<<< Updated upstream - const [searchVal, setSearchVal] = useState(""); // Search input state - const [searchResults, setSearchResults] = useState([]); // State for search results - const [isLoading, setIsLoading] = useState(false); // Loading state - const [errorMessage, setErrorMessage] = useState(""); // Error message state - const [postCreated, setPostCreated] = useState(false); - const { user } = useAuth(); - - // Use the useModal hook to get the openModal and setModal functions - const { openModal, setModal } = useModal(); - - const onChangeSearchTerm = (e) => { - setSearchVal(e.target.value); - }; - - // Show modal when the button is clicked - const showCreatePostModal = () => { - setModal("Create a post", ); - openModal(); - }; - - // Search handler for fetching user data - const handleSearch = async () => { - if (searchVal.trim().length === 0) return; // Return if search input is empty - - try { - // Split searchVal into firstName and lastName (assuming space separates them) - const [firstName, lastName] = searchVal.trim().split(" "); - - // Create the appropriate query string for the API call - const query = new URLSearchParams({ - first_name: firstName || "", - last_name: lastName || "", - }).toString(); - - // Fetch users from the updated route with query parameters - const response = await fetch(`/api/users?${query}`); - const result = await response.json(); - - // Handle the search results - setSearchResults(result.users); // Store the fetched users - } catch (error) { - setErrorMessage("Failed to fetch search results."); // Set error message - } finally { - setIsLoading(false); // Reset loading state - } - }; - - // Handle "Enter" key press to trigger search - const handleKeyPress = (event) => { - if (event.key === "Enter" && searchVal.trim()) { - event.preventDefault(); - handleSearch(); - } - }; - - return ( - <> -
    - -
    -
    -

    {getInitials(user.firstName, user.lastName)}

    -
    -
    -
    - - -
    - -