diff --git a/typings/context.d.ts b/typings/context.d.ts deleted file mode 100644 index 27a42ded..00000000 --- a/typings/context.d.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as CR from './index' - -interface ProgressStatus { - active: boolean - complete: boolean -} - -export interface Step extends Exclude { - status: ProgressStatus -} - -export interface ReceivedEvent { - data: CR.Action -} - -export interface LevelWithStatus extends CR.TutorialLevel { - status: ProgressStatus -} diff --git a/typings/graphql.d.ts b/typings/graphql.d.ts index a95e6c6a..9e7af734 100644 --- a/typings/graphql.d.ts +++ b/typings/graphql.d.ts @@ -76,12 +76,15 @@ export type GithubUser = { export type Level = { __typename?: 'Level', id: Scalars['ID'], - index?: number title: Scalars['String'], + /** A summary of the level */ description: Scalars['String'], + /** The lesson content of the level, parsed as markdown */ + content: Scalars['String'], + /** A set of tasks for users linked to unit tests */ steps: Array, + /** Actions run on level start up for configuring setup */ setup?: Maybe, - status: ProgressStatus, }; export type Mutation = { @@ -102,11 +105,6 @@ export type MutationCreateTutorialVersionArgs = { input: CreateTutorialVersionInput }; -export type ProgressStatus = - 'ACTIVE' | - 'COMPLETE' | - 'INCOMPLETE'; - export type Query = { __typename?: 'Query', tutorial?: Maybe, @@ -135,10 +133,9 @@ export type Step = { __typename?: 'Step', id: Scalars['ID'], title: Scalars['String'], - description: Scalars['String'], + content: Scalars['String'], setup: StepActions, solution: StepActions, - status: ProgressStatus, }; /** Load commits, open files or run commands */ @@ -336,7 +333,6 @@ export type ResolversTypes = { Sha1: ResolverTypeWrapper, Level: ResolverTypeWrapper, Step: ResolverTypeWrapper, - ProgressStatus: ProgressStatus, Boolean: ResolverTypeWrapper, tutorialRepoInput: TutorialRepoInput, Commit: ResolverTypeWrapper, @@ -374,7 +370,6 @@ export type ResolversParentTypes = { Sha1: Scalars['Sha1'], Level: Level, Step: Step, - ProgressStatus: ProgressStatus, Boolean: Scalars['Boolean'], tutorialRepoInput: TutorialRepoInput, Commit: Commit, @@ -432,9 +427,9 @@ export type LevelResolvers, title?: Resolver, description?: Resolver, + content?: Resolver, steps?: Resolver, ParentType, ContextType>, setup?: Resolver, ParentType, ContextType>, - status?: Resolver, }; export type MutationResolvers = { @@ -456,10 +451,9 @@ export interface Sha1ScalarConfig extends GraphQLScalarTypeConfig = { id?: Resolver, title?: Resolver, - description?: Resolver, + content?: Resolver, setup?: Resolver, solution?: Resolver, - status?: Resolver, }; export type StepActionsResolvers = { @@ -583,5 +577,4 @@ export interface IntrospectionResultData { }[]; }[]; }; -} - +} \ No newline at end of file diff --git a/typings/index.d.ts b/typings/index.d.ts index 5f2bf1b7..038e3ac8 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2,75 +2,7 @@ import {send} from 'xstate' import Storage from '../src/services/storage' import * as G from './graphql' -export interface TutorialLevel { - stepList: string[] - content: { - title: string - text: string - } - actions?: { - setup: TutorialAction - } -} - -export interface TutorialHint { - text: string - displayed?: boolean -} - -export interface TutorialAction { - commits: string[] - commands?: string[] - files?: string[] -} - -export interface TutorialStepContent { - text: string - title?: string -} - -export interface TutorialStep { - content: TutorialStepContent - actions: { - setup: TutorialAction - solution: TutorialAction - } - hints?: TutorialHint[] -} - -export interface TutorialData { - summary: TutorialSummary - levels: { - [levelId: string]: TutorialLevel - } - steps: { - [stepId: string]: TutorialStep - } -} - -export interface TutorialMeta { - version: string - repo: string - createdBy: string - createdAt: string - updatedBy: string - updatedAt: string - contributors: string[] - languages: string[] - testRunner: string -} - -export interface TutorialSummary { - title: string - description: string - levelList: string[] -} - -export interface Tutorial { - id: string - meta: TutorialMeta - data: TutorialData -} +export type ProgressStatus = 'ACTIVE' | 'COMPLETE' | 'INCOMPLETE' export interface Progress { levels: { diff --git a/web-app/src/containers/Overview/OverviewPage.tsx b/web-app/src/containers/Overview/OverviewPage.tsx index 81672bb4..e737c2e9 100644 --- a/web-app/src/containers/Overview/OverviewPage.tsx +++ b/web-app/src/containers/Overview/OverviewPage.tsx @@ -2,6 +2,8 @@ import * as React from 'react' import { Button } from '@alifd/next' import * as G from 'typings/graphql' +import Markdown from '../../components/Markdown' + const styles = { summary: { padding: '0rem 1rem 1rem 1rem', @@ -55,7 +57,7 @@ const Summary = ({ title, description, levels, onNext }: Props) => (

{title}

-

{description}

+ {description}
diff --git a/web-app/src/containers/Tutorial/LevelPage/Level/index.tsx b/web-app/src/containers/Tutorial/LevelPage/Level/index.tsx index 96da427f..17c113e0 100644 --- a/web-app/src/containers/Tutorial/LevelPage/Level/index.tsx +++ b/web-app/src/containers/Tutorial/LevelPage/Level/index.tsx @@ -1,6 +1,7 @@ import { Button, Step } from '@alifd/next' import * as React from 'react' import * as G from 'typings/graphql' +import * as T from 'typings' import Markdown from '../../../../components/Markdown' import StepDescription from './StepDescription' @@ -45,7 +46,7 @@ const styles = { } interface Props { - level: G.Level + level: G.Level & { status: T.ProgressStatus; index: number; steps: Array } onContinue(): void onLoadSolution(): void } @@ -56,7 +57,7 @@ const Level = ({ level, onContinue, onLoadSolution }: Props) => { } // grab the active step - const activeIndex: number = level.steps.findIndex((step: G.Step | null) => { + const activeIndex: number = level.steps.findIndex((step: G.Step & { status: T.ProgressStatus } | null) => { return step && step.status === 'ACTIVE' }) @@ -68,7 +69,7 @@ const Level = ({ level, onContinue, onLoadSolution }: Props) => {

{level.title}

- {level.description || ''} + {level.content || ''}
@@ -76,7 +77,7 @@ const Level = ({ level, onContinue, onLoadSolution }: Props) => {
Tasks
- {level.steps.map((step: G.Step | null, index: number) => { + {level.steps.map((step: G.Step & { status: T.ProgressStatus } | null, index: number) => { if (!step) { return null } @@ -84,9 +85,7 @@ const Level = ({ level, onContinue, onLoadSolution }: Props) => { - } + content={} /> ) })} diff --git a/web-app/src/containers/Tutorial/LevelPage/index.tsx b/web-app/src/containers/Tutorial/LevelPage/index.tsx index a3f7af6c..ecd089c2 100644 --- a/web-app/src/containers/Tutorial/LevelPage/index.tsx +++ b/web-app/src/containers/Tutorial/LevelPage/index.tsx @@ -1,19 +1,20 @@ import * as React from 'react' -import * as CR from 'typings' +import * as T from 'typings' import * as G from 'typings/graphql' import * as selectors from '../../../services/selectors' import Level from './Level' interface PageProps { - context: CR.MachineContext - send(action: CR.Action): void + context: T.MachineContext + send(action: T.Action): void } const LevelSummaryPageContainer = (props: PageProps) => { const { position, progress } = props.context - const level: G.Level = selectors.currentLevel(props.context) + const version = selectors.currentVersion(props.context) + const levelData: G.Level = selectors.currentLevel(props.context) const onContinue = (): void => { props.send({ @@ -28,16 +29,25 @@ const LevelSummaryPageContainer = (props: PageProps) => { props.send({ type: 'STEP_SOLUTION_LOAD' }) } - level.steps.forEach((step: G.Step) => { - if (progress.steps[step.id]) { - step.status = 'COMPLETE' - } else if (step.id === position.stepId) { - step.status = 'ACTIVE' - } else { - step.status = 'INCOMPLETE' - } - }) - level.status = progress.levels[position.levelId] ? 'COMPLETE' : 'ACTIVE' + const level: G.Level & { + status: T.ProgressStatus + index: number + steps: Array + } = { + ...levelData, + index: version.data.levels.findIndex((l: G.Level) => l.id === position.levelId), + status: progress.levels[position.levelId] ? 'COMPLETE' : 'ACTIVE', + steps: levelData.steps.map((step: G.Step) => { + // label step status for step component + let status: T.ProgressStatus = 'INCOMPLETE' + if (progress.steps[step.id]) { + status = 'COMPLETE' + } else if (step.id === position.stepId) { + status = 'ACTIVE' + } + return { ...step, status } + }), + } return } diff --git a/web-app/src/containers/Tutorial/LevelsPage/Level/LevelStageSummary.tsx b/web-app/src/containers/Tutorial/LevelsPage/Level/LevelStageSummary.tsx deleted file mode 100644 index 2a45df98..00000000 --- a/web-app/src/containers/Tutorial/LevelsPage/Level/LevelStageSummary.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import * as React from 'react' -import * as G from 'typings/graphql' - -import Markdown from '../../../../components/Markdown' - -const styles = { - card: { - display: 'grid', - gridTemplateAreas: 'Content Icon', - gridTemplateColumns: '1fr 1.5rem', - gridTemplateRows: '1fr', - marginRight: '1.5rem', - }, - continueIcon: { - color: '#1890ff', - }, - left: {}, - right: { - alignSelf: 'center', - justifySelf: 'center', - marginBottom: '1rem', - }, -} - -interface Props { - level: G.Level - onNext(): void -} - -const LevelStageSummary = (props: Props) => { - const { level, onNext } = props - const active = level.status === 'ACTIVE' - return ( -
-
- {level.description || ''} -
-
- ) -} - -export default LevelStageSummary diff --git a/web-app/src/containers/Tutorial/LevelsPage/Level/index.tsx b/web-app/src/containers/Tutorial/LevelsPage/Level/index.tsx deleted file mode 100644 index be881e91..00000000 --- a/web-app/src/containers/Tutorial/LevelsPage/Level/index.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { Step } from '@alifd/next' -import * as React from 'react' -import * as G from 'typings/graphql' - -import Markdown from '../../../../components/Markdown' -import LevelStageSummary from './LevelStageSummary' - -const styles = { - card: {}, - content: { - padding: '0rem 1rem', - paddingBottom: '1rem', - }, - list: { - padding: '0rem', - }, - options: { - padding: '0rem 1rem', - }, - steps: { - padding: '1rem 0.5rem', - }, - title: {}, -} - -interface Props { - level: G.Level - onNext(): void -} - -const Level = ({ level, onNext }: Props) => { - return
Level
- // if (!level || !level.stages) { - // throw new Error('No level stages found') - // } - // const activeIndex = level.stages.findIndex((stage: G.Stage | null) => stage && stage.status === 'ACTIVE') || 0 - // return ( - //
- //
- //

{level.title}

- // {level.text || ''} - //
- //
- // - // {level.stages.map((stage: G.Stage | null, index: number) => { - // if (!stage) { - // return null - // } - // const active = stage.status === 'ACTIVE' - // const clickHandler = active - // ? onNext - // : () => { - // /* empty */ - // } - // // note - must add click handler to title, content & step.item - // // as all are separated components - // return ( - // - // {stage.title || `Stage ${index + 1}`} - // - // } - // content={} - // onClick={clickHandler} - // /> - // ) - // })} - // - //
- //
- // ) -} - -export default Level diff --git a/web-app/src/containers/Tutorial/LevelsPage/index.tsx b/web-app/src/containers/Tutorial/LevelsPage/index.tsx deleted file mode 100644 index 947a36fa..00000000 --- a/web-app/src/containers/Tutorial/LevelsPage/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import * as React from 'react' -import * as CR from 'typings' -import * as G from 'typings/graphql' - -import Level from './Level' - -interface LevelProps { - level: G.Level - send(action: string): void -} - -export const LevelsSummaryPage = (props: LevelProps) => { - const onNext = (): void => { - props.send('NEXT') - } - return -} - -interface ContainerProps { - context: CR.MachineContext - send(action: string): void -} - -const LevelsPageContainer = (props: ContainerProps) => { - return
LevelsPage
- - // const { tutorial, position, progress } = props.context - - // if (!tutorial) { - // throw new Error('Tutorial not found in LevelSummaryPageContainer') - // } - - // const level: G.Level | undefined = tutorial.version.data.levels.find((l: G.Level) => l.id === position.levelId) - - // if (!level) { - // throw new Error('Level not found in LevelSummaryPageContainer') - // } - - // level.stages.forEach((stage: G.Stage) => { - // if (stage.id === position.stageId) { - // stage.status = 'ACTIVE' - // } else if (progress.stages[stage.id]) { - // stage.status = 'COMPLETE' - // } else { - // stage.status = 'INCOMPLETE' - // } - // }) - - // return -} - -export default LevelsPageContainer diff --git a/web-app/src/services/apollo/queries/tutorial.ts b/web-app/src/services/apollo/queries/tutorial.ts index 27746e76..f30fe4d9 100644 --- a/web-app/src/services/apollo/queries/tutorial.ts +++ b/web-app/src/services/apollo/queries/tutorial.ts @@ -28,6 +28,7 @@ export default gql` id title description + content setup { commits commands @@ -36,7 +37,7 @@ export default gql` steps { id title - description + content setup { commits commands diff --git a/web-app/src/services/selectors/tutorial.ts b/web-app/src/services/selectors/tutorial.ts index 7c453521..f34de7d8 100644 --- a/web-app/src/services/selectors/tutorial.ts +++ b/web-app/src/services/selectors/tutorial.ts @@ -30,7 +30,6 @@ export const currentLevel = (context: MachineContext): G.Level => createSelector throw new Error('Level not found when selecting level') } const level: G.Level = levels[levelIndex] - level.index = levelIndex return level })(context) diff --git a/web-app/stories/Level.stories.tsx b/web-app/stories/Level.stories.tsx index 79800b44..d7de75a4 100644 --- a/web-app/stories/Level.stories.tsx +++ b/web-app/stories/Level.stories.tsx @@ -16,14 +16,15 @@ storiesOf('Level', module) id: 'L1', index: 2, title: 'A Title', - description: 'Some description', + description: 'A summary of the level', + content: 'Some content here in markdown', setup: null, status: 'ACTIVE', steps: [ { id: 'L1:S1', title: 'First Step', - description: 'First step description', + content: 'First step description', setup: { id: 'L1:S1:SETUP', commits: ['abcdefg'], @@ -37,7 +38,7 @@ storiesOf('Level', module) { id: 'L1:S2', title: 'Second Step', - description: 'Second step description', + content: 'Second step description', setup: { id: 'L1:S2:SETUP', commits: ['abcdefg'], @@ -51,7 +52,7 @@ storiesOf('Level', module) { id: 'L1:S3', title: 'Third Step', - description: 'Third step description', + content: 'Third step description', setup: { id: 'L1:S3:SETUP', commits: ['abcdefg'],