From 11d89da4452fa6443afae5926cff6b530234c0d1 Mon Sep 17 00:00:00 2001 From: regularmute <93884347+Regularmute@users.noreply.github.com> Date: Thu, 2 May 2024 17:45:15 +0300 Subject: [PATCH 1/5] enable error messages through redux in instructor-logs page --- .../TimeLogsPage/InstructorTimeLogsPage.jsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx b/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx index 9d425b8..6001342 100644 --- a/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx +++ b/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react' import { withRouter } from 'react-router-dom' - +import { connect } from 'react-redux' import { TimeLogsSelectForm } from './TimeLogsSelectForm' import { TimeLogRow } from './TimeLogRow' import LoadingSpinner from '../common/LoadingSpinner' @@ -11,7 +11,7 @@ import groupManagementService from '../../services/groupManagement' import instructorTimeLogsService from '../../services/instructorTimeLogs' import * as notificationActions from '../../reducers/actions/notificationActions' -const InstructorTimeLogsPage = () => { +const InstructorTimeLogsPage = (props) => { const [allConfigurations, setAllConfigurations] = useState([]) const [selectedConfiguration, setSelectedConfiguration] = useState(null) const [allGroups, setAllGroups] = useState([]) @@ -131,6 +131,15 @@ const InstructorTimeLogsPage = () => { ) } +const mapStateToProps = (state) => ({ + state: state, +}) + +const mapDispatchToProps = { + setError: notificationActions.setError, + setSuccess: notificationActions.setSuccess, +} + export default withRouter( - (InstructorTimeLogsPage) + connect(mapStateToProps, mapDispatchToProps)(InstructorTimeLogsPage) ) From d2e1e439ecbb43c8ba9891e9cd7d303a96197f3d Mon Sep 17 00:00:00 2001 From: regularmute <93884347+Regularmute@users.noreply.github.com> Date: Thu, 2 May 2024 18:46:26 +0300 Subject: [PATCH 2/5] not try to map nonexistent mappedData in timelogChart component this prevents site from crashing when the component's mappedData prop is null. --- frontend/src/components/TimeLogsPage/TimeLogChart.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/TimeLogsPage/TimeLogChart.jsx b/frontend/src/components/TimeLogsPage/TimeLogChart.jsx index 7560097..5f01fe2 100644 --- a/frontend/src/components/TimeLogsPage/TimeLogChart.jsx +++ b/frontend/src/components/TimeLogsPage/TimeLogChart.jsx @@ -103,8 +103,10 @@ const TimeLogChart = (props) => { useEffect(() => { const mappedData = mapSprintSummaryData(groupSprintSummary) - setSprints([...new Set(mappedData.map((entry) => entry.sprint).filter((entry) => entry !== -1))]) - setChartData(mappedData) + if (mappedData) { + setSprints([...new Set(mappedData.map((entry) => entry.sprint).filter((entry) => entry !== -1))]) + setChartData(mappedData) + } }, [groupSprintSummary]) if (chartData && chartData.length > 0) { From 8f56b36190ff80decb39da36f7bc22947a96c038 Mon Sep 17 00:00:00 2001 From: regularmute <93884347+Regularmute@users.noreply.github.com> Date: Thu, 2 May 2024 18:48:49 +0300 Subject: [PATCH 3/5] display charts for selected group's logs as admin/instructor --- .../TimeLogsPage/InstructorTimeLogsPage.jsx | 102 +++++++++++++----- 1 file changed, 76 insertions(+), 26 deletions(-) diff --git a/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx b/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx index 6001342..ffa779d 100644 --- a/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx +++ b/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx @@ -3,15 +3,24 @@ import { withRouter } from 'react-router-dom' import { connect } from 'react-redux' import { TimeLogsSelectForm } from './TimeLogsSelectForm' import { TimeLogRow } from './TimeLogRow' +import TimeLogChart from './TimeLogChart' import LoadingSpinner from '../common/LoadingSpinner' +import { Typography } from '@material-ui/core' + import userService from '../../services/user' import configurationService from '../../services/configuration' import groupManagementService from '../../services/groupManagement' +import timeLogsService from '../../services/timeLogs' import instructorTimeLogsService from '../../services/instructorTimeLogs' import * as notificationActions from '../../reducers/actions/notificationActions' +import timeLogsActions from '../../reducers/actions/timeLogsActions' const InstructorTimeLogsPage = (props) => { + const { + setGroupSprintSummary + } = props + const [allConfigurations, setAllConfigurations] = useState([]) const [selectedConfiguration, setSelectedConfiguration] = useState(null) const [allGroups, setAllGroups] = useState([]) @@ -94,40 +103,80 @@ const InstructorTimeLogsPage = (props) => { fetchData() }, []) + useEffect(() => { + const fetchGroupSprintSummary = async (group_id) => { + try { + const summaryData = await timeLogsService.getGroupSprintSummary(group_id) + setGroupSprintSummary(JSON.parse(summaryData)) + } catch (error) { + console.error( + 'Error fetching group sprint summary:', + error.message, + ' / ', + error.response.data.error + ) + notificationActions.setError(error.response.data.error) + } + } + + const fetchChartData = async (selectedGroupId) => { + setIsLoading(true) + await fetchGroupSprintSummary(selectedGroupId) + setIsLoading(false) + } + selectedGroup?.id && fetchChartData(selectedGroup.id) + }, [selectedGroup]) + const isLogs = (logs) => logs && logs.length > 0 const logsByStudent = isLogs(allLogs) && allLogs.filter((log) => log.studentNumber === selectedStudentNumber) if (isLoading) return return ( -
-
- - {selectedGroup &&
{selectedGroup.name}
} +
+
+
+ + {selectedGroup &&
{selectedGroup.name}
} +
+
+ {isLogs(logsByStudent) && + logsByStudent.map((log) => ( + + ))} + {!isLogs(logsByStudent) && ( +

No logs by the selected user.

+ )} +
-
- {isLogs(logsByStudent) && - logsByStudent.map((log) => ( - - ))} - {!isLogs(logsByStudent) && ( -

No logs by the selected user.

- )} +
+ {selectedGroup && +
+
+ Sprint Chart + +
+
+ Project Chart + +
+
+ }
-
+
) } @@ -138,6 +187,7 @@ const mapStateToProps = (state) => ({ const mapDispatchToProps = { setError: notificationActions.setError, setSuccess: notificationActions.setSuccess, + setGroupSprintSummary: timeLogsActions.setGroupSprintSummary, } export default withRouter( From 6d489a8d024334fae571d0611bf0890d9f3ff01f Mon Sep 17 00:00:00 2001 From: regularmute <93884347+Regularmute@users.noreply.github.com> Date: Thu, 2 May 2024 19:30:50 +0300 Subject: [PATCH 4/5] enable sprint-specific chart results for groups in instructor-logs The user can click between sprints 0-100, which is reset to 0 every time the user selects a new group. --- .../TimeLogsPage/InstructorTimeLogsPage.jsx | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx b/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx index ffa779d..a887011 100644 --- a/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx +++ b/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx @@ -5,6 +5,7 @@ import { TimeLogsSelectForm } from './TimeLogsSelectForm' import { TimeLogRow } from './TimeLogRow' import TimeLogChart from './TimeLogChart' import LoadingSpinner from '../common/LoadingSpinner' +import { SprintSelect } from './SprintSelect' import { Typography } from '@material-ui/core' @@ -13,11 +14,14 @@ import configurationService from '../../services/configuration' import groupManagementService from '../../services/groupManagement' import timeLogsService from '../../services/timeLogs' import instructorTimeLogsService from '../../services/instructorTimeLogs' + import * as notificationActions from '../../reducers/actions/notificationActions' import timeLogsActions from '../../reducers/actions/timeLogsActions' const InstructorTimeLogsPage = (props) => { const { + selectedSprintNumber, + setSelectedSprintNumber, setGroupSprintSummary } = props @@ -30,6 +34,7 @@ const InstructorTimeLogsPage = (props) => { const [allLogs, setAllLogs] = useState(null) const [isLoading, setIsLoading] = useState(true) + const possibleSprintNumbers = Array.from({length: 101}, (_, i) => i) useEffect(() => { const fetchAllConfigurations = async () => { @@ -108,6 +113,7 @@ const InstructorTimeLogsPage = (props) => { try { const summaryData = await timeLogsService.getGroupSprintSummary(group_id) setGroupSprintSummary(JSON.parse(summaryData)) + setSelectedSprintNumber(0) } catch (error) { console.error( 'Error fetching group sprint summary:', @@ -127,6 +133,26 @@ const InstructorTimeLogsPage = (props) => { selectedGroup?.id && fetchChartData(selectedGroup.id) }, [selectedGroup]) + const previousSprint = [...possibleSprintNumbers] + .reverse() + .find((sprint) => sprint < selectedSprintNumber) + + const nextSprint = possibleSprintNumbers.find( + (sprint) => sprint > selectedSprintNumber + ) + + const handleClickNextSprint = () => { + setSelectedSprintNumber( + nextSprint !== undefined ? nextSprint : selectedSprintNumber + ) + } + + const handleClickPreviousSprint = () => { + setSelectedSprintNumber( + previousSprint !== undefined ? previousSprint : selectedSprintNumber + ) + } + const isLogs = (logs) => logs && logs.length > 0 const logsByStudent = isLogs(allLogs) && allLogs.filter((log) => log.studentNumber === selectedStudentNumber) @@ -147,7 +173,18 @@ const InstructorTimeLogsPage = (props) => { selectedStudentNumber={selectedStudentNumber} handleStudentNumberChange={setSelectedStudentNumber} /> - {selectedGroup &&
{selectedGroup.name}
} + {selectedGroup && ( +
+ {selectedGroup.name} + +
+ )}
{isLogs(logsByStudent) && @@ -182,12 +219,14 @@ const InstructorTimeLogsPage = (props) => { const mapStateToProps = (state) => ({ state: state, + selectedSprintNumber: state.timeLogs.selectedSprintNumber, }) const mapDispatchToProps = { setError: notificationActions.setError, setSuccess: notificationActions.setSuccess, setGroupSprintSummary: timeLogsActions.setGroupSprintSummary, + setSelectedSprintNumber: timeLogsActions.setSelectedSprintNumber, } export default withRouter( From 73db37ab5afd7c17f7c32c886f8de3bea57134aa Mon Sep 17 00:00:00 2001 From: regularmute <93884347+Regularmute@users.noreply.github.com> Date: Thu, 2 May 2024 19:35:16 +0300 Subject: [PATCH 5/5] Clarify the displayed group for instructor-timelogs --- frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx b/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx index a887011..633afbf 100644 --- a/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx +++ b/frontend/src/components/TimeLogsPage/InstructorTimeLogsPage.jsx @@ -175,7 +175,7 @@ const InstructorTimeLogsPage = (props) => { /> {selectedGroup && (
- {selectedGroup.name} + Timelogs by {selectedGroup.name}