diff --git a/src/actions/actionTypes.ts b/src/actions/actionTypes.ts index aa1e2b5fd9..81bc4cacd3 100644 --- a/src/actions/actionTypes.ts +++ b/src/actions/actionTypes.ts @@ -34,8 +34,10 @@ export const INTERRUPT_EXECUTION = 'INTERRUPT_EXECUTION' export const CHANGE_TOKEN = 'CHANGE_TOKEN' export const FETCH_ANNOUNCEMENTS = 'FETCH_ANNOUNCEMENTS' export const FETCH_ASSESSMENT = 'FETCH_ASSESSMENT' -export const FETCH_MISSIONS_INFO = 'FETCH_MISSIONS_INFO' +export const FETCH_ASSESSMENT_OVERVIEWS = 'FETCH_ASSESSMENT_OVERVIEWS' export const FETCH_USERNAME = 'FETCH_USERNAME' export const LOGIN = 'LOGIN' export const SET_USERNAME = 'SET_USERNAME' export const UPDATE_HISTORY_HELPERS = 'UPDATE_HISTORY_HELPERS' +export const UPDATE_ASSESSMENT_OVERVIEWS = 'UPDATE_ASSESSMENT_OVERVIEWS' +export const UPDATE_ASSESSMENT = 'UPDATE_ASSESSMENT' diff --git a/src/actions/session.ts b/src/actions/session.ts index dde42a6017..9bee2f8781 100644 --- a/src/actions/session.ts +++ b/src/actions/session.ts @@ -1,4 +1,6 @@ import { ActionCreator } from 'redux' + +import { IAssessment, IAssessmentOverview } from '../components/assessment/assessmentShape' import * as actionTypes from './actionTypes' export const changeToken: ActionCreator = (token: string) => ({ @@ -10,13 +12,13 @@ export const fetchAnnouncements = () => ({ type: actionTypes.FETCH_ANNOUNCEMENTS }) -export const fetchAssessment = (missionId: number) => ({ +export const fetchAssessment = (id: number) => ({ type: actionTypes.FETCH_ASSESSMENT, - payload: missionId + payload: id }) -export const fetchMissionsInfo = () => ({ - type: actionTypes.FETCH_MISSIONS_INFO +export const fetchAssessmentOverviews = () => ({ + type: actionTypes.FETCH_ASSESSMENT_OVERVIEWS }) export const fetchUsername = () => ({ @@ -36,3 +38,13 @@ export const updateHistoryHelpers: ActionCreator = (loc: st type: actionTypes.UPDATE_HISTORY_HELPERS, payload: loc }) + +export const updateAssessmentOverviews = (overviews: IAssessmentOverview[]) => ({ + type: actionTypes.UPDATE_ASSESSMENT_OVERVIEWS, + payload: overviews +}) + +export const updateAssessment = (assessment: IAssessment) => ({ + type: actionTypes.UPDATE_ASSESSMENT, + payload: assessment +}) diff --git a/src/components/academy/Missions.tsx b/src/components/academy/Missions.tsx index 5fc9da72fd..f7412603bb 100644 --- a/src/components/academy/Missions.tsx +++ b/src/components/academy/Missions.tsx @@ -5,26 +5,21 @@ import { RouteComponentProps } from 'react-router' import { NavLink } from 'react-router-dom' import AssessmentContainer from '../../containers/AssessmentContainer' -import { OwnProps as AssessmentProps } from '../Assessment' +import { OwnProps as AssessmentProps } from '../assessment' +import { IAssessmentOverview } from '../assessment/assessmentShape' import ContentDisplay, { IContentDisplayProps } from '../commons/ContentDisplay' -export type MissionInfo = { - id: number - title: string - description: string -} - export interface IMissionParams { missionId?: string } export interface IMissionsProps extends RouteComponentProps { - missionsInfo?: MissionInfo[] - handleMissionsInfoFetch: () => void + assessmentOverviews?: IAssessmentOverview[] + handleAssessmentOverviewFetch: () => void } -export type StateProps = Pick -export type DispatchProps = Pick +export type StateProps = Pick +export type DispatchProps = Pick class Missions extends React.Component { public render() { @@ -39,8 +34,8 @@ class Missions extends React.Component { // if there is no mission specified, Render only information. if (missionIdParam === null) { const props: IContentDisplayProps = { - display: , - loadContentDispatch: this.props.handleMissionsInfoFetch + display: , + loadContentDispatch: this.props.handleAssessmentOverviewFetch } return (
@@ -56,17 +51,17 @@ class Missions extends React.Component { } } -interface IMissionInfoCardProps { - missionsInfo?: MissionInfo[] +interface IAssessmentOverviewCardProps { + assessmentOverviews?: IAssessmentOverview[] } -export const MissionInfoCard: React.SFC = props => { - if (props.missionsInfo === undefined) { - return } /> - } else if (props.missionsInfo.length === 0) { - return +export const AssessmentOverviewCard: React.SFC = props => { + if (props.assessmentOverviews === undefined) { + return } /> + } else if (props.assessmentOverviews.length === 0) { + return } - const cards = props.missionsInfo.map((mission, index) => ( + const cards = props.assessmentOverviews.map((mission, index) => (
PICTURE
@@ -78,7 +73,7 @@ export const MissionInfoCard: React.SFC = props => {
Mission 0 : 123123 XP (hardcoded)
-

{mission.description}

+

{mission.shortSummary}

diff --git a/src/components/academy/__tests__/Missions.tsx b/src/components/academy/__tests__/Missions.tsx index 1e4ecdfa7e..7299eed477 100644 --- a/src/components/academy/__tests__/Missions.tsx +++ b/src/components/academy/__tests__/Missions.tsx @@ -2,37 +2,25 @@ import { mount } from 'enzyme' import * as React from 'react' import { MemoryRouter } from 'react-router' +import { mockAssessmentOverviews } from '../../../mocks/api' import { mockRouterProps } from '../../../mocks/components' import Missions, { IMissionsProps } from '../Missions' const mockUndefinedMissions: IMissionsProps = { ...mockRouterProps('/academy/missions', {}), - handleMissionsInfoFetch: () => {} + handleAssessmentOverviewFetch: () => {} } const mockEmptyMissions: IMissionsProps = { ...mockRouterProps('/academy/missions', {}), - missionsInfo: [], - handleMissionsInfoFetch: () => {} + assessmentOverviews: [], + handleAssessmentOverviewFetch: () => {} } const mockPresentMissions: IMissionsProps = { ...mockRouterProps('/academy/missions', {}), - missionsInfo: [ - { - id: 0, - title: 'An Odessey to Runes', - description: - 'Once upon a time, Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin nec vulputate sapien. Fusce vel lacus fermentum, efficitur ipsum in' - }, - { - id: 1, - title: 'The Secret to Streams', - description: - 'Once upon a time, () => { Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin nec vulputate sapien. Fusce vel lacus fermentum, efficitur ipsum in }' - } - ], - handleMissionsInfoFetch: () => {} + assessmentOverviews: mockAssessmentOverviews, + handleAssessmentOverviewFetch: () => {} } test('Missions page "loading" content renders correctly', () => { diff --git a/src/components/academy/__tests__/__snapshots__/Missions.tsx.snap b/src/components/academy/__tests__/__snapshots__/Missions.tsx.snap index 5bb196b7bd..f486527f38 100644 --- a/src/components/academy/__tests__/__snapshots__/Missions.tsx.snap +++ b/src/components/academy/__tests__/__snapshots__/Missions.tsx.snap @@ -3,15 +3,15 @@ exports[`Missions page "loading" content renders correctly 1`] = ` " - +
- +
- - + +
@@ -26,7 +26,7 @@ exports[`Missions page "loading" content renders correctly 1`] = `
- Fetching missions... + Fetching assessment...
@@ -45,15 +45,15 @@ exports[`Missions page "loading" content renders correctly 1`] = ` exports[`Missions page with 0 missions renders correctly 1`] = ` " - +
- +
- - + +
@@ -66,7 +66,7 @@ exports[`Missions page with 0 missions renders correctly 1`] = `

- There are no missions. + There are no assessments.

@@ -85,14 +85,14 @@ exports[`Missions page with 0 missions renders correctly 1`] = ` exports[`Missions page with multiple loaded missions renders correctly 1`] = ` " - +
- +
- +
@@ -112,7 +112,7 @@ exports[`Missions page with multiple loaded missions renders correctly 1`] = `

- Once upon a time, Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin nec vulputate sapien. Fusce vel lacus fermentum, efficitur ipsum in + Once upon a time, Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin nec vulputate sapien. Fusce vel lacus fermentum, efficitur ipsum.

@@ -181,7 +181,7 @@ exports[`Missions page with multiple loaded missions renders correctly 1`] = `

- Once upon a time, () => { Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin nec vulputate sapien. Fusce vel lacus fermentum, efficitur ipsum in } + Once upon a time, Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin nec vulputate sapien. Fusce vel lacus fermentum, efficitur ipsum.

@@ -231,6 +231,213 @@ exports[`Missions page with multiple loaded missions renders correctly 1`] = `
+
+ +
+
+ PICTURE +
+
+
+

+ A sample Sidequest +

+
+
+
+ Mission 0 : 123123 XP (hardcoded) +
+
+
+

+ Once upon a time, Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin nec vulputate sapien. Fusce vel lacus fermentum, efficitur ipsum. +

+
+
+
+ +
+ + + + time + + + + + Due: 12/12/12 +
+
+
+ +
+
+
+
+
+
+ +
+
+ PICTURE +
+
+
+

+ A closed Mission +

+
+
+
+ Mission 0 : 123123 XP (hardcoded) +
+
+
+

+ Once upon a time, Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin nec vulputate sapien. Fusce vel lacus fermentum, efficitur ipsum. +

+
+
+
+ +
+ + + + time + + + + + Due: 12/12/12 +
+
+
+ +
+
+
+
+
+
+ +
+
+ PICTURE +
+
+
+

+ A closed sidequest +

+
+
+
+ Mission 0 : 123123 XP (hardcoded) +
+
+
+

+ Once upon a time, Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin nec vulputate sapien. Fusce vel lacus fermentum, efficitur ipsum. +

+
+
+
+ +
+ + + + time + + + + + Due: 12/12/12 +
+
+
+ +
+
+
+
+
diff --git a/src/components/assessment/assessmentShape.ts b/src/components/assessment/assessmentShape.ts new file mode 100644 index 0000000000..c1b82840cf --- /dev/null +++ b/src/components/assessment/assessmentShape.ts @@ -0,0 +1,67 @@ +/* + * Used to display information regarding an assessment in the UI. + */ +export interface IAssessmentOverview { + category: AssessmentCategory + closeAt: string + coverImage: string + id: number + maximumEXP: number + openAt: string + order: number + shortSummary: string + title: string +} + +/* + * Used when an assessment is being actively attempted/graded. + */ +export interface IAssessment { + category: AssessmentCategory + id: number + longSummary: string + missionPDF: string + questions: IQuestion[] + title: string +} + +/* The different kinds of Assessments available */ +export type AssessmentCategory = 'Contest' | 'Mission' | 'Path' | 'Sidequest' +export enum AssessmentCategories { + CONTEST = 'Contest', + MISSION = 'Mission', + PATH = 'Path', + SIDEQUEST = 'Sidequest' +} + +export interface IProgrammingQuestion extends IQuestion { + library: Library + solutionTemplate: string + type: 'programming' +} + +export interface IMCQQuestion extends IQuestion { + choices?: MCQChoice[] + type: 'mcq' +} + +export interface IQuestion { + content: string + id: number + type: QuestionType +} + +type MCQChoice = { + content: string + hint: string +} + +/* The two kinds of Questions available */ +type QuestionType = 'programming' | 'mcq' + +export type Library = { + chapter: number + externals: string[] + files: string[] + globals: string[] +} diff --git a/src/components/Assessment.tsx b/src/components/assessment/index.tsx similarity index 68% rename from src/components/Assessment.tsx rename to src/components/assessment/index.tsx index 1c44bf002a..c923086512 100644 --- a/src/components/Assessment.tsx +++ b/src/components/assessment/index.tsx @@ -2,34 +2,32 @@ import { Button, Card, Dialog, NonIdealState, Spinner, Text } from '@blueprintjs import { IconNames } from '@blueprintjs/icons' import * as React from 'react' -import Workspace from '../containers/workspace' -import { OwnProps as ControlBarOwnProps } from './workspace/ControlBar' -import { SideContentTab } from './workspace/side-content' - -export type AssessmentInfo = { - longSummary: string - dueDate: string - studentBriefed: boolean -} +import Workspace from '../../containers/workspace' +import { OwnProps as ControlBarOwnProps } from '../workspace/ControlBar' +import { SideContentTab } from '../workspace/side-content' +import { IAssessment } from './assessmentShape' export type AssessmentProps = DispatchProps & OwnProps & StateProps export type StateProps = { - assessmentInfo?: AssessmentInfo + assessment?: IAssessment } export type OwnProps = { missionId: number } export type DispatchProps = { - handleAssessmentInfoFetch: (missionId: number) => void + handleAssessmentFetch: (missionId: number) => void } class Assessment extends React.Component { public state = { showOverlay: true } + public componentWillMount() { + this.props.handleAssessmentFetch(this.props.missionId) + } + public render() { - if (this.props.assessmentInfo === undefined) { - this.props.handleAssessmentInfoFetch(this.props.missionId) + if (this.props.assessment === undefined) { return ( ) } - const briefing = {this.props.assessmentInfo.longSummary} + const briefing = {this.props.assessment.longSummary} const overlay = ( - + {briefing}