diff --git a/src/commons/application/types/SessionTypes.ts b/src/commons/application/types/SessionTypes.ts index fbf069b11f..f2806898ef 100644 --- a/src/commons/application/types/SessionTypes.ts +++ b/src/commons/application/types/SessionTypes.ts @@ -104,7 +104,7 @@ export type SessionState = { readonly enableAchievements?: boolean; readonly enableSourcecast?: boolean; readonly enableStories?: boolean; - readonly sourceChapter?: number; + readonly sourceChapter?: Chapter; readonly sourceVariant?: Variant; readonly moduleHelpText?: string; readonly assetsPrefix?: string; diff --git a/src/commons/missionCreator/MissionCreator.tsx b/src/commons/missionCreator/MissionCreator.tsx index 57250b6bb7..3d6da510bb 100644 --- a/src/commons/missionCreator/MissionCreator.tsx +++ b/src/commons/missionCreator/MissionCreator.tsx @@ -1,8 +1,10 @@ import { FileInput } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; -import * as React from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useDispatch } from 'react-redux'; import { parseString } from 'xml2js'; +import { updateAssessment } from '../application/actions/SessionActions'; import { Assessment, AssessmentOverview, @@ -17,27 +19,26 @@ import { storeLocalAssessmentOverview } from '../XMLParser/XMLParserHelper'; -type MissionCreatorProps = DispatchProps & OwnProps; - -export type DispatchProps = { - newAssessment: (assessment: Assessment) => void; -}; - -type OwnProps = { +type Props = { updateEditingOverview: (overview: AssessmentOverview) => void; }; -function MissionCreator(props: MissionCreatorProps) { - const [fileInputText, setFileInputText] = React.useState('Import XML'); +const MissionCreator: React.FC = props => { + const [fileInputText, setFileInputText] = useState('Import XML'); let fileReader: FileReader | undefined = undefined; - React.useEffect(() => { + const dispatch = useDispatch(); + const newAssessment = useCallback( + (assessment: Assessment) => dispatch(updateAssessment(assessment)), + [dispatch] + ); + + useEffect(() => { const assessment = retrieveLocalAssessment(); if (assessment) { - props.newAssessment(assessment); + newAssessment(assessment); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.newAssessment]); + }, [newAssessment]); const handleFileRead = (file: any) => (e: any) => { if (!fileReader) { @@ -46,7 +47,6 @@ function MissionCreator(props: MissionCreatorProps) { const content = fileReader.result; if (content) { parseString(content, (err: any, result: any) => { - console.dir(file); try { const entireAssessment: [AssessmentOverview, Assessment] = makeEntireAssessment(result); entireAssessment[0].fileName = file.name.slice(0, -4); @@ -54,10 +54,9 @@ function MissionCreator(props: MissionCreatorProps) { props.updateEditingOverview(entireAssessment[0]); storeLocalAssessment(entireAssessment[1]); - props.newAssessment(entireAssessment[1]); + newAssessment(entireAssessment[1]); setFileInputText('Success!'); } catch (err) { - console.log(err); setFileInputText('Invalid XML!'); } }); @@ -77,7 +76,7 @@ function MissionCreator(props: MissionCreatorProps) { storeLocalAssessmentOverview(overviewTemplate()); props.updateEditingOverview(overviewTemplate()); storeLocalAssessment(assessmentTemplate()); - props.newAssessment(assessmentTemplate()); + newAssessment(assessmentTemplate()); }; return ( @@ -95,6 +94,6 @@ function MissionCreator(props: MissionCreatorProps) { ); -} +}; export default MissionCreator; diff --git a/src/commons/missionCreator/MissionCreatorContainer.ts b/src/commons/missionCreator/MissionCreatorContainer.ts deleted file mode 100644 index f6726f6411..0000000000 --- a/src/commons/missionCreator/MissionCreatorContainer.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'; -import { bindActionCreators, Dispatch } from 'redux'; - -import { updateAssessment } from '../application/actions/SessionActions'; -import MissionCreator, { DispatchProps } from './MissionCreator'; - -const mapStateToProps: MapStateToProps<{}, any, {}> = (state, ownProps) => ownProps; - -const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch) => - bindActionCreators( - { - newAssessment: updateAssessment - }, - dispatch - ); - -const MissionCreatorContainer = connect(mapStateToProps, mapDispatchToProps)(MissionCreator); - -export default MissionCreatorContainer; diff --git a/src/pages/academy/grading/subcomponents/GradingEditor.tsx b/src/pages/academy/grading/subcomponents/GradingEditor.tsx index 54ac9edb7d..57dc6a69ca 100644 --- a/src/pages/academy/grading/subcomponents/GradingEditor.tsx +++ b/src/pages/academy/grading/subcomponents/GradingEditor.tsx @@ -10,9 +10,15 @@ import { Pre } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import ReactMde, { ReactMdeProps } from 'react-mde'; +import { useDispatch } from 'react-redux'; +import { + reautogradeAnswer, + submitGrading, + submitGradingAndContinue +} from '../../../../commons/application/actions/SessionActions'; import ControlButton from '../../../../commons/ControlButton'; import Markdown from '../../../../commons/Markdown'; import { Prompt } from '../../../../commons/ReactRouterPrompt'; @@ -24,8 +30,6 @@ import { } from '../../../../commons/utils/notifications/NotificationsHelper'; import { convertParamToInt } from '../../../../commons/utils/ParamParseHelper'; -type GradingEditorProps = DispatchProps & OwnProps; - type GradingSaveFunction = ( submissionId: number, questionId: number, @@ -33,13 +37,7 @@ type GradingSaveFunction = ( comments?: string ) => void; -export type DispatchProps = { - handleGradingSave: GradingSaveFunction; - handleGradingSaveAndContinue: GradingSaveFunction; - handleReautogradeAnswer: (submissionId: number, questionId: number) => void; -}; - -type OwnProps = { +type Props = { solution: number | string | null; questionId: number; submissionId: number; @@ -55,7 +53,22 @@ type OwnProps = { const gradingEditorButtonClass = 'grading-editor-button'; -const GradingEditor: React.FC = props => { +const GradingEditor: React.FC = props => { + const dispatch = useDispatch(); + const { handleGradingSave, handleGradingSaveAndContinue, handleReautogradeAnswer } = useMemo( + () => + ({ + handleGradingSave: (...args) => dispatch(submitGrading(...args)), + handleGradingSaveAndContinue: (...args) => dispatch(submitGradingAndContinue(...args)), + handleReautogradeAnswer: (...args) => dispatch(reautogradeAnswer(...args)) + }) satisfies { + handleGradingSave: GradingSaveFunction; + handleGradingSaveAndContinue: GradingSaveFunction; + handleReautogradeAnswer: (submissionId: number, questionId: number) => void; + }, + [dispatch] + ); + /** * A potentially null string which defines the * result for the number XP input. This property being null @@ -140,7 +153,7 @@ const GradingEditor: React.FC = props => { comments?: string ) => { const callback = (): void => { - props.handleGradingSaveAndContinue(submissionId, questionId, xpAdjustment, comments!); + handleGradingSaveAndContinue(submissionId, questionId, xpAdjustment, comments!); }; setCurrentlySaving(true); // TODO: Check (not sure how) if this results in a regression. @@ -159,7 +172,7 @@ const GradingEditor: React.FC = props => { positiveIntent: 'danger' }); if (confirm) { - props.handleReautogradeAnswer(props.submissionId, props.questionId); + handleReautogradeAnswer(props.submissionId, props.questionId); } }; @@ -300,7 +313,7 @@ const GradingEditor: React.FC = props => { diff --git a/src/pages/academy/grading/subcomponents/GradingEditorContainer.ts b/src/pages/academy/grading/subcomponents/GradingEditorContainer.ts deleted file mode 100644 index 1a4b8d3e21..0000000000 --- a/src/pages/academy/grading/subcomponents/GradingEditorContainer.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { connect, MapDispatchToProps } from 'react-redux'; -import { bindActionCreators, Dispatch } from 'redux'; - -import { - reautogradeAnswer, - submitGrading, - submitGradingAndContinue -} from '../../../../commons/application/actions/SessionActions'; -import GradingEditor, { DispatchProps } from './GradingEditor'; - -const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch) => - bindActionCreators( - { - handleGradingSave: submitGrading, - handleGradingSaveAndContinue: submitGradingAndContinue, - handleReautogradeAnswer: reautogradeAnswer - }, - dispatch - ); - -const GradingEditorContainer = connect(null, mapDispatchToProps)(GradingEditor); - -export default GradingEditorContainer; diff --git a/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx b/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx index 8c15ef3c1d..3797712db1 100644 --- a/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx +++ b/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx @@ -57,7 +57,7 @@ import { SideContentTab, SideContentType } from '../../../../commons/sideContent import Workspace, { WorkspaceProps } from '../../../../commons/workspace/Workspace'; import { WorkspaceLocation, WorkspaceState } from '../../../../commons/workspace/WorkspaceTypes'; import { AnsweredQuestion } from '../../../../features/grading/GradingTypes'; -import GradingEditor from './GradingEditorContainer'; +import GradingEditor from './GradingEditor'; type GradingWorkspaceProps = { submissionId: number; diff --git a/src/pages/academy/groundControl/GroundControl.tsx b/src/pages/academy/groundControl/GroundControl.tsx index 06a09c2af8..3f0b31577a 100644 --- a/src/pages/academy/groundControl/GroundControl.tsx +++ b/src/pages/academy/groundControl/GroundControl.tsx @@ -12,7 +12,7 @@ import { AssessmentOverview } from '../../../commons/assessment/AssessmentTypes'; import ContentDisplay from '../../../commons/ContentDisplay'; -import DefaultChapterSelect from './subcomponents/DefaultChapterSelectContainer'; +import DefaultChapterSelect from './subcomponents/DefaultChapterSelect'; import DeleteCell from './subcomponents/GroundControlDeleteCell'; import Dropzone from './subcomponents/GroundControlDropzone'; import EditCell from './subcomponents/GroundControlEditCell'; diff --git a/src/pages/academy/groundControl/subcomponents/DefaultChapterSelect.tsx b/src/pages/academy/groundControl/subcomponents/DefaultChapterSelect.tsx index d7e876a0c1..e0ba2022a2 100644 --- a/src/pages/academy/groundControl/subcomponents/DefaultChapterSelect.tsx +++ b/src/pages/academy/groundControl/subcomponents/DefaultChapterSelect.tsx @@ -1,8 +1,11 @@ import { Button, Classes, Dialog, Intent, Menu, MenuItem } from '@blueprintjs/core'; import { IconNames } from '@blueprintjs/icons'; import { ItemListRenderer, ItemRenderer, Select } from '@blueprintjs/select'; -import { Chapter, Variant } from 'js-slang/dist/types'; -import * as React from 'react'; +import { Variant } from 'js-slang/dist/types'; +import React, { useCallback, useState } from 'react'; +import { useDispatch } from 'react-redux'; +import Constants from 'src/commons/utils/Constants'; +import { useSession } from 'src/commons/utils/Hooks'; import { SALanguage, @@ -10,48 +13,47 @@ import { styliseSublanguage } from '../../../../commons/application/ApplicationTypes'; import ControlButton from '../../../../commons/ControlButton'; +import { changeSublanguage } from '../../../../commons/workspace/WorkspaceActions'; -export type DefaultChapterSelectProps = DispatchProps & StateProps; +const DefaultChapterSelect: React.FC = () => { + const [chosenSublang, setSublanguage] = useState(sourceLanguages[0]); + const [isDialogOpen, setDialogState] = useState(false); -export type DispatchProps = { - handleUpdateSublanguage: (sublang: SALanguage) => void; -}; - -export type StateProps = { - sourceChapter: Chapter; - sourceVariant: Variant; -}; + const { + // Temporarily load the defaults when the course configuration fetch has yet to return + sourceChapter = Constants.defaultSourceChapter, + sourceVariant = Constants.defaultSourceVariant + } = useSession(); -const DefaultChapterSelect: React.FunctionComponent = props => { - const { handleUpdateSublanguage } = props; - const { sourceChapter, sourceVariant } = props; - - const [chosenSublang, setSublanguage] = React.useState(sourceLanguages[0]); - const [isDialogOpen, setDialogState] = React.useState(false); + const dispatch = useDispatch(); + const handleUpdateSublanguage = useCallback( + (sublang: SALanguage) => dispatch(changeSublanguage(sublang)), + [dispatch] + ); - const handleOpenDialog = React.useCallback( + const handleOpenDialog = useCallback( (choice: SALanguage) => { setDialogState(true); setSublanguage(choice); }, [setDialogState, setSublanguage] ); - const handleCloseDialog = React.useCallback(() => { + const handleCloseDialog = useCallback(() => { setDialogState(false); }, [setDialogState]); - const handleConfirmDialog = React.useCallback(() => { + const handleConfirmDialog = useCallback(() => { setDialogState(false); handleUpdateSublanguage(chosenSublang); }, [chosenSublang, setDialogState, handleUpdateSublanguage]); - const chapterRenderer: ItemRenderer = React.useCallback( + const chapterRenderer: ItemRenderer = useCallback( (lang, { handleClick }) => ( ), [] ); - const chapterListRenderer: ItemListRenderer = React.useCallback( + const chapterListRenderer: ItemListRenderer = useCallback( ({ itemsParentRef, renderItem, items }) => { const defaultChoices = items.filter(({ variant }) => variant === Variant.DEFAULT); const variantChoices = items.filter(({ variant }) => variant !== Variant.DEFAULT); diff --git a/src/pages/academy/groundControl/subcomponents/DefaultChapterSelectContainer.ts b/src/pages/academy/groundControl/subcomponents/DefaultChapterSelectContainer.ts deleted file mode 100644 index d1b6116973..0000000000 --- a/src/pages/academy/groundControl/subcomponents/DefaultChapterSelectContainer.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'; -import { bindActionCreators, Dispatch } from 'redux'; -import Constants from 'src/commons/utils/Constants'; - -import { OverallState } from '../../../../commons/application/ApplicationTypes'; -import { changeSublanguage } from '../../../../commons/workspace/WorkspaceActions'; -import DefaultChapterSelect, { DispatchProps, StateProps } from './DefaultChapterSelect'; - -const mapStateToProps: MapStateToProps = state => ({ - // Temporarily load the defaults when the course configuration fetch has yet to return - sourceChapter: state.session.sourceChapter || Constants.defaultSourceChapter, - sourceVariant: state.session.sourceVariant || Constants.defaultSourceVariant -}); - -const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch) => - bindActionCreators( - { - handleUpdateSublanguage: changeSublanguage - }, - dispatch - ); - -const DefaultChapterSelectContainer = connect( - mapStateToProps, - mapDispatchToProps -)(DefaultChapterSelect); - -export default DefaultChapterSelectContainer; diff --git a/src/pages/achievement/Achievement.tsx b/src/pages/achievement/Achievement.tsx index 26bfd2689d..08ad713e80 100644 --- a/src/pages/achievement/Achievement.tsx +++ b/src/pages/achievement/Achievement.tsx @@ -4,8 +4,8 @@ import { useTypedSelector } from 'src/commons/utils/Hooks'; import { Role } from '../../commons/application/ApplicationTypes'; import NotFound from '../notFound/NotFound'; -import AchievementControl from './control/AchievementControlContainer'; -import AchievementDashboard from './subcomponents/AchievementDashboardContainer'; +import AchievementControl from './control/AchievementControl'; +import AchievementDashboard from './subcomponents/AchievementDashboard'; const Achievement: React.FC = () => { const role = useTypedSelector(state => state.session.role!); diff --git a/src/pages/achievement/control/AchievementControl.tsx b/src/pages/achievement/control/AchievementControl.tsx index 23e1c39854..c83b00162b 100644 --- a/src/pages/achievement/control/AchievementControl.tsx +++ b/src/pages/achievement/control/AchievementControl.tsx @@ -1,44 +1,56 @@ -import { useEffect, useReducer, useState } from 'react'; +import React, { useEffect, useMemo, useReducer, useState } from 'react'; +import { useDispatch } from 'react-redux'; +import { useTypedSelector } from 'src/commons/utils/Hooks'; import AchievementEditor from '../../../commons/achievement/control/AchievementEditor'; import AchievementPreview from '../../../commons/achievement/control/AchievementPreview'; import GoalEditor from '../../../commons/achievement/control/GoalEditor'; import AchievementInferencer from '../../../commons/achievement/utils/AchievementInferencer'; import { Prompt } from '../../../commons/ReactRouterPrompt'; +import { + bulkUpdateAchievements, + bulkUpdateGoals, + getAchievements, + getOwnGoals, + removeAchievement, + removeGoal +} from '../../../features/achievement/AchievementActions'; import { AchievementContext } from '../../../features/achievement/AchievementConstants'; import { AchievementItem, GoalDefinition } from '../../../features/achievement/AchievementTypes'; -export type DispatchProps = { - bulkUpdateAchievements: (achievements: AchievementItem[]) => void; - bulkUpdateGoals: (goals: GoalDefinition[]) => void; - getAchievements: () => void; - getOwnGoals: () => void; - removeAchievement: (uuid: string) => void; - removeGoal: (uuid: string) => void; -}; - -export type StateProps = { - inferencer: AchievementInferencer; -}; - -function AchievementControl(props: DispatchProps & StateProps) { +const AchievementControl: React.FC = () => { + const dispatch = useDispatch(); const { - bulkUpdateAchievements, - bulkUpdateGoals, - getAchievements, - getOwnGoals, - removeAchievement, - removeGoal, - inferencer - } = props; + handleBulkUpdateAchievements, + handleBulkUpdateGoals, + handleGetAchievements, + handleGetOwnGoals, + handleRemoveAchievement, + handleRemoveGoal + } = useMemo( + () => ({ + handleBulkUpdateAchievements: (achievement: AchievementItem[]) => + dispatch(bulkUpdateAchievements(achievement)), + handleBulkUpdateGoals: (goals: GoalDefinition[]) => dispatch(bulkUpdateGoals(goals)), + handleGetAchievements: () => dispatch(getAchievements()), + handleGetOwnGoals: () => dispatch(getOwnGoals()), + handleRemoveAchievement: (uuid: string) => dispatch(removeAchievement(uuid)), + handleRemoveGoal: (uuid: string) => dispatch(removeGoal(uuid)) + }), + [dispatch] + ); + + const inferencer = useTypedSelector( + state => new AchievementInferencer(state.achievement.achievements, state.achievement.goals) + ); /** * Fetch the latest achievements and goals from backend when the page is rendered */ useEffect(() => { - getAchievements(); - getOwnGoals(); - }, [getAchievements, getOwnGoals]); + handleGetAchievements(); + handleGetOwnGoals(); + }, [handleGetAchievements, handleGetOwnGoals]); /** * Monitors changes that are awaiting publish @@ -46,10 +58,10 @@ function AchievementControl(props: DispatchProps & StateProps) { const [awaitPublish, setAwaitPublish] = useState(false); const publishChanges = () => { // NOTE: Goals and achievements must exist in the backend before the association can be built - bulkUpdateGoals(inferencer.getAllGoals()); - bulkUpdateAchievements(inferencer.getAllAchievements()); - inferencer.getGoalsToDelete().forEach(removeGoal); - inferencer.getAchievementsToDelete().forEach(removeAchievement); + handleBulkUpdateGoals(inferencer.getAllGoals()); + handleBulkUpdateAchievements(inferencer.getAllAchievements()); + inferencer.getGoalsToDelete().forEach(handleRemoveGoal); + inferencer.getAchievementsToDelete().forEach(handleRemoveAchievement); inferencer.resetToDelete(); setAwaitPublish(false); }; @@ -82,13 +94,11 @@ function AchievementControl(props: DispatchProps & StateProps) {
- -
); -} +}; export default AchievementControl; diff --git a/src/pages/achievement/control/AchievementControlContainer.ts b/src/pages/achievement/control/AchievementControlContainer.ts deleted file mode 100644 index 10ee15c2e3..0000000000 --- a/src/pages/achievement/control/AchievementControlContainer.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'; -import { bindActionCreators, Dispatch } from 'redux'; - -import AchievementInferencer from '../../../commons/achievement/utils/AchievementInferencer'; -import { OverallState } from '../../../commons/application/ApplicationTypes'; -import { - bulkUpdateAchievements, - bulkUpdateGoals, - getAchievements, - getOwnGoals, - removeAchievement, - removeGoal -} from '../../../features/achievement/AchievementActions'; -import AchievementControl, { DispatchProps, StateProps } from './AchievementControl'; - -const mapStateToProps: MapStateToProps = state => ({ - inferencer: new AchievementInferencer(state.achievement.achievements, state.achievement.goals) -}); - -const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch) => - bindActionCreators( - { - bulkUpdateAchievements, - bulkUpdateGoals, - getAchievements, - getOwnGoals, - removeAchievement, - removeGoal - }, - dispatch - ); - -const AchievementControlContainer = connect( - mapStateToProps, - mapDispatchToProps -)(AchievementControl); - -export default AchievementControlContainer; diff --git a/src/pages/achievement/subcomponents/AchievementDashboard.tsx b/src/pages/achievement/subcomponents/AchievementDashboard.tsx index 6b3d2bb492..688ad717ae 100644 --- a/src/pages/achievement/subcomponents/AchievementDashboard.tsx +++ b/src/pages/achievement/subcomponents/AchievementDashboard.tsx @@ -1,10 +1,8 @@ import { IconNames } from '@blueprintjs/icons'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; +import { useDispatch } from 'react-redux'; import { Role } from 'src/commons/application/ApplicationTypes'; -import { - AssessmentConfiguration, - AssessmentOverview -} from 'src/commons/assessment/AssessmentTypes'; +import { useSession, useTypedSelector } from 'src/commons/utils/Hooks'; import AchievementFilter from '../../../commons/achievement/AchievementFilter'; import AchievementManualEditor from '../../../commons/achievement/AchievementManualEditor'; @@ -13,6 +11,15 @@ import AchievementTask from '../../../commons/achievement/AchievementTask'; import AchievementView from '../../../commons/achievement/AchievementView'; import AchievementInferencer from '../../../commons/achievement/utils/AchievementInferencer'; import insertFakeAchievements from '../../../commons/achievement/utils/InsertFakeAchievements'; +import { fetchAssessmentOverviews } from '../../../commons/application/actions/SessionActions'; +import { + getAchievements, + getGoals, + getOwnGoals, + getUserAssessmentOverviews, + getUsers, + updateGoalProgress +} from '../../../features/achievement/AchievementActions'; import { AchievementContext } from '../../../features/achievement/AchievementConstants'; import { AchievementUser, @@ -20,28 +27,6 @@ import { GoalProgress } from '../../../features/achievement/AchievementTypes'; -export type DispatchProps = { - fetchAssessmentOverviews: () => void; - getAchievements: () => void; - getGoals: (studentCourseRegId: number) => void; - getOwnGoals: () => void; - getUserAssessmentOverviews: (studentCourseRegId: number) => void; - getUsers: () => void; - updateGoalProgress: (studentCourseRegId: number, progress: GoalProgress) => void; -}; - -export type StateProps = { - group: string | null; - inferencer: AchievementInferencer; - id?: number; - name?: string; - role?: Role; - assessmentConfigs?: AssessmentConfiguration[]; - assessmentOverviews?: AssessmentOverview[]; - achievementAssessmentOverviews: AssessmentOverview[]; - users: AchievementUser[]; -}; - /** * Generates components * @@ -63,49 +48,67 @@ export const generateAchievementTasks = ( /> )); -type DashboardProps = DispatchProps & StateProps; +const AchievementDashboard: React.FC = () => { + // default nothing selected + const userIdState = useState(undefined); + const [selectedUser] = userIdState; -const Dashboard: React.FC = props => { const { - getAchievements, - getOwnGoals, - getGoals, - getUserAssessmentOverviews, - getUsers, - updateGoalProgress, - fetchAssessmentOverviews, group, - inferencer, name, role, - assessmentConfigs, assessmentOverviews, - achievementAssessmentOverviews, - users - } = props; + assessmentConfigurations: assessmentConfigs + } = useSession(); - // default nothing selected - const userIdState = useState(undefined); - const [selectedUser] = userIdState; + const { assessmentOverviews: achievementAssessmentOverviews, users } = useTypedSelector( + state => state.achievement + ); + const inferencer = useTypedSelector( + state => new AchievementInferencer(state.achievement.achievements, state.achievement.goals) + ); + + const dispatch = useDispatch(); + const { + handleFetchAssessmentOverviews, + handleGetAchievements, + handleGetGoals, + handleGetOwnGoals, + handleGetUserAssessmentOverviews, + handleGetUsers, + handleUpdateGoalProgress + } = useMemo(() => { + return { + handleFetchAssessmentOverviews: () => dispatch(fetchAssessmentOverviews()), + handleGetAchievements: () => dispatch(getAchievements()), + handleGetGoals: (studentCourseRegId: number) => dispatch(getGoals(studentCourseRegId)), + handleGetOwnGoals: () => dispatch(getOwnGoals()), + handleGetUserAssessmentOverviews: (studentCourseRegId: number) => + dispatch(getUserAssessmentOverviews(studentCourseRegId)), + handleGetUsers: () => dispatch(getUsers()), + handleUpdateGoalProgress: (studentCourseRegId: number, progress: GoalProgress) => + dispatch(updateGoalProgress(studentCourseRegId, progress)) + }; + }, [dispatch]); /** * Fetch the latest achievements and goals from backend when the page is rendered */ useEffect(() => { - selectedUser ? getGoals(selectedUser.courseRegId) : getOwnGoals(); + selectedUser ? handleGetGoals(selectedUser.courseRegId) : handleGetOwnGoals(); selectedUser - ? getUserAssessmentOverviews(selectedUser.courseRegId) - : fetchAssessmentOverviews(); + ? handleGetUserAssessmentOverviews(selectedUser.courseRegId) + : handleFetchAssessmentOverviews(); - getAchievements(); + handleGetAchievements(); }, [ - selectedUser, - getAchievements, - getGoals, - getOwnGoals, - getUserAssessmentOverviews, - fetchAssessmentOverviews + handleFetchAssessmentOverviews, + handleGetAchievements, + handleGetGoals, + handleGetOwnGoals, + handleGetUserAssessmentOverviews, + selectedUser ]); const userAssessmentOverviews = selectedUser @@ -149,8 +152,8 @@ const Dashboard: React.FC = props => { hiddenState={hiddenState} studio={group || 'Staff'} users={users} - getUsers={getUsers} - updateGoalProgress={updateGoalProgress} + getUsers={handleGetUsers} + updateGoalProgress={handleUpdateGoalProgress} /> )} @@ -192,4 +195,4 @@ const Dashboard: React.FC = props => { ); }; -export default Dashboard; +export default AchievementDashboard; diff --git a/src/pages/achievement/subcomponents/AchievementDashboardContainer.ts b/src/pages/achievement/subcomponents/AchievementDashboardContainer.ts deleted file mode 100644 index c18c1ab475..0000000000 --- a/src/pages/achievement/subcomponents/AchievementDashboardContainer.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'; -import { bindActionCreators, Dispatch } from 'redux'; - -import AchievementInferencer from '../../../commons/achievement/utils/AchievementInferencer'; -import { fetchAssessmentOverviews } from '../../../commons/application/actions/SessionActions'; -import { OverallState } from '../../../commons/application/ApplicationTypes'; -import { - getAchievements, - getGoals, - getOwnGoals, - getUserAssessmentOverviews, - getUsers, - updateGoalProgress -} from '../../../features/achievement/AchievementActions'; -import Dashboard, { DispatchProps, StateProps } from './AchievementDashboard'; - -const mapStateToProps: MapStateToProps = state => ({ - group: state.session.group, - inferencer: new AchievementInferencer(state.achievement.achievements, state.achievement.goals), - name: state.session.name, - role: state.session.role, - assessmentOverviews: state.session.assessmentOverviews, - achievementAssessmentOverviews: state.achievement.assessmentOverviews, - users: state.achievement.users, - assessmentConfigs: state.session.assessmentConfigurations -}); - -const mapDispatchToProps: MapDispatchToProps = (dispatch: Dispatch) => - bindActionCreators( - { - fetchAssessmentOverviews, - getAchievements, - getGoals, - getOwnGoals, - getUserAssessmentOverviews, - getUsers, - updateGoalProgress - }, - dispatch - ); - -const DashboardContainer = connect(mapStateToProps, mapDispatchToProps)(Dashboard); - -export default DashboardContainer; diff --git a/src/pages/missionControl/MissionControl.tsx b/src/pages/missionControl/MissionControl.tsx index 6098769f10..40270bb2ab 100644 --- a/src/pages/missionControl/MissionControl.tsx +++ b/src/pages/missionControl/MissionControl.tsx @@ -11,7 +11,7 @@ import { EditingOverviewCard } from '../../commons/editingOverviewCard/EditingOv import EditingWorkspace, { EditingWorkspaceProps } from '../../commons/editingWorkspace/EditingWorkspace'; -import MissionCreator from '../../commons/missionCreator/MissionCreatorContainer'; +import MissionCreator from '../../commons/missionCreator/MissionCreator'; import Constants from '../../commons/utils/Constants'; import { convertParamToInt } from '../../commons/utils/ParamParseHelper'; import { retrieveLocalAssessmentOverview } from '../../commons/XMLParser/XMLParserHelper';