Skip to content

Commit 21cdbce

Browse files
committed
add progress to tutorial pages and link each tutorial page to the next chapter
1 parent f283191 commit 21cdbce

File tree

6 files changed

+125
-89
lines changed

6 files changed

+125
-89
lines changed

packages/gatsby-theme/src/components/courses/CourseCard.tsx

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,34 +29,31 @@ const CourseCard: React.FunctionComponent<CourseCardProps> = ({
2929
<h3>{tutorialTitle}</h3>
3030
<Query query={getTutorialbyGatsbyID} variables={{ gatsbyID: gatsbyID }}>
3131
{({ data }) => {
32-
let buttonText = 'Continue Tutorial';
32+
let buttonText = 'Start Tutorial';
33+
let percentage = 0;
3334
if (
3435
optionalChaining(
3536
() =>
3637
data.getTutorialbyGatsbyID.viewerUserTutorial.currentChapter,
3738
)
3839
) {
39-
let percentage = percent(
40+
percentage = percent(
4041
data.getTutorialbyGatsbyID.numberofChapters,
4142
data.getTutorialbyGatsbyID.viewerUserTutorial.currentChapter,
4243
);
44+
buttonText = 'Continue Tutorial';
4345
if (percentage === 100) {
4446
buttonText = 'Take Again';
4547
}
46-
return (
47-
<div>
48-
<ProgressBar percentage={percentage} />
49-
<a href={path}>
50-
<TutorialButton>{buttonText}</TutorialButton>
51-
</a>
52-
</div>
53-
);
54-
} else
55-
return (
48+
}
49+
return (
50+
<div>
51+
{!!percentage && <ProgressBar percentage={percentage} />}
5652
<a href={path}>
57-
<TutorialButton>Start Tutorial</TutorialButton>
53+
<TutorialButton>{buttonText}</TutorialButton>
5854
</a>
59-
);
55+
</div>
56+
);
6057
}}
6158
</Query>
6259
</Flex>

packages/gatsby-theme/src/components/templates/Tutorial.tsx

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { HideOnTablet, ShowOnTablet } from '../../utils/responsive';
99
import { Flex, Box } from '../shared/base';
1010
import { optionalChaining } from '../../utils/helpers';
1111
import ChapterMutation from '../tutorial/ChapterMutation';
12+
import { getTutorialSlug } from '../../utils/getTutorialSlug';
1213

1314
type TutorialLayoutProps = { data: TutorialMdxQuery } & RouterProps;
1415

@@ -19,20 +20,40 @@ const TutorialLayout: React.FunctionComponent<TutorialLayoutProps> = ({
1920
if (!data) {
2021
return null;
2122
}
22-
const { pageTitle } = data!.mdx!.frontmatter!;
23-
const gatsbyID = optionalChaining(
24-
() => data!.tutorialTitle!.frontmatter!.id!,
25-
);
2623

24+
//title of the chapter
25+
const { pageTitle } = optionalChaining(() => data.mdx.frontmatter);
26+
//title of the tutorial
2727
const tutorialTitle = optionalChaining(
28-
() => data!.tutorialTitle!.frontmatter!.tutorialTitle!,
28+
() => data.tutorialTitle.frontmatter.tutorialTitle,
29+
);
30+
//gatsbyID to fetch user information about tutorial
31+
const gatsbyID = optionalChaining(() => data.tutorialTitle.frontmatter.id);
32+
//all chapters in this tutorial
33+
const chapters = optionalChaining(() =>
34+
data.pageTitles.edges.map(a => {
35+
return {
36+
chapterTitle: optionalChaining(() => a.node.frontmatter.pageTitle),
37+
chapterPath: optionalChaining(() => a.node.fileAbsolutePath),
38+
};
39+
}),
2940
);
30-
const chapters =
31-
optionalChaining(() =>
32-
data!.pageTitles!.edges!.map(a => a.node!.frontmatter!.pageTitle!),
33-
) || [];
41+
3442
const { location } = props;
35-
const currentChapter = chapters.indexOf(pageTitle) + 1;
43+
44+
//all titles of chapters in this tutorial
45+
const chapterTitles = chapters.map(chapter => chapter.chapterTitle);
46+
//the number of this current chapter
47+
const currentChapterIndex = chapterTitles.indexOf(pageTitle);
48+
const findNextChapterPath = () => {
49+
if (currentChapterIndex + 1 === chapters.length) {
50+
return null;
51+
} else {
52+
return getTutorialSlug(chapters[currentChapterIndex + 1].chapterPath);
53+
}
54+
};
55+
56+
const nextChapterPath = findNextChapterPath();
3657

3758
return (
3859
<Layout location={location}>
@@ -42,14 +63,17 @@ const TutorialLayout: React.FunctionComponent<TutorialLayoutProps> = ({
4263
<h1>{pageTitle}</h1>
4364
</Box>
4465
<Box width={1 / 8} m={2}>
45-
<TabletSidebar chapters={chapters} tutorialTitle={tutorialTitle} />
66+
<TabletSidebar
67+
chapters={chapterTitles}
68+
tutorialTitle={tutorialTitle}
69+
/>
4670
</Box>
4771
</Flex>
4872
</ShowOnTablet>
4973
<HideOnTablet>
5074
<Flex>
5175
<Box width={1 / 4} m={2}>
52-
<Sidebar chapters={chapters} tutorialTitle={tutorialTitle} />
76+
<Sidebar chapters={chapterTitles} tutorialTitle={tutorialTitle} />
5377
</Box>
5478
<Box width={3 / 4} m={2}>
5579
<h1>{pageTitle}</h1>
@@ -60,7 +84,12 @@ const TutorialLayout: React.FunctionComponent<TutorialLayoutProps> = ({
6084
<ShowOnTablet>
6185
<MDXRenderer>{data!.mdx!.code!.body}</MDXRenderer>
6286
</ShowOnTablet>
63-
<ChapterMutation gatsbyID={gatsbyID} currentChapter={currentChapter} />
87+
<a href={nextChapterPath}>
88+
<ChapterMutation
89+
gatsbyID={gatsbyID}
90+
currentChapter={currentChapterIndex + 1}
91+
/>
92+
</a>
6493
</Layout>
6594
);
6695
};
@@ -89,6 +118,7 @@ export const pageQuery = graphql`
89118
edges {
90119
node {
91120
id
121+
fileAbsolutePath
92122
frontmatter {
93123
pageTitle
94124
}

packages/gatsby-theme/src/components/templates/TutorialOverview.tsx

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,19 @@ import React from 'react';
22
import Layout from '../shared/layout';
33
import Chapter from '../tutorial/Chapter';
44
import { TutorialOverviewQuery } from 'src/graphqlTypes';
5-
import AuthorList from '../tutorial/AuthorList';
65
import TutorialHeader from '../tutorial/TutorialHeader';
76
import { Heading, Flex, Box } from '../shared/base';
8-
import ProgressBar from '../shared/ProgressBar';
9-
import {
10-
GithubButton,
11-
SpectrumButton,
12-
TutorialButton,
13-
} from '../shared/buttons';
7+
import AuthorsProgressBox from '../tutorial/AuthorsProgressBox';
148
import { Content } from '../shared/styledHelpers';
15-
import { authors } from '../../utils/sampleData';
169
import { graphql } from 'gatsby';
10+
import { optionalChaining } from '../../utils/helpers';
1711

1812
interface PageTemplateProps {
1913
data: TutorialOverviewQuery;
2014
}
2115

2216
const PageTemplate: React.FunctionComponent<PageTemplateProps> = ({ data }) => {
17+
let gatsbyID = optionalChaining(() => data.overview.frontmatter.id);
2318
return (
2419
<Layout>
2520
<Content>
@@ -33,15 +28,7 @@ const PageTemplate: React.FunctionComponent<PageTemplateProps> = ({ data }) => {
3328
/>
3429
</Box>
3530
<Box width={1 / 4} m={3}>
36-
<TutorialButton>Continue Tutorial</TutorialButton>
37-
<Box m={3}>
38-
<ProgressBar percentage={33} width={100} />
39-
</Box>
40-
<Flex>
41-
<GithubButton>Github</GithubButton>
42-
<SpectrumButton>Spectrum</SpectrumButton>
43-
</Flex>
44-
<AuthorList authors={authors} />
31+
<AuthorsProgressBox gatsbyID={gatsbyID} />
4532
</Box>
4633
</Flex>
4734
<div>
@@ -87,6 +74,7 @@ export const query = graphql`
8774
) {
8875
id
8976
frontmatter {
77+
id
9078
tutorialTitle
9179
banner
9280
description
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import * as React from 'react';
2+
import AuthorList from './AuthorList';
3+
import { authors } from '../../utils/sampleData';
4+
import ProgressBar from '../shared/ProgressBar';
5+
import { getTutorialbyGatsbyID } from '../../utils/queries';
6+
import { Query } from 'react-apollo';
7+
import {
8+
GithubButton,
9+
SpectrumButton,
10+
TutorialButton,
11+
} from '../shared/buttons';
12+
import { Flex, Box } from '../shared/base';
13+
import { optionalChaining, percent } from '../../utils/helpers';
14+
15+
const AuthorsProgressBox = ({ gatsbyID }) => (
16+
<Query query={getTutorialbyGatsbyID} variables={{ gatsbyID: gatsbyID }}>
17+
{({ data }) => {
18+
let buttonText = 'Start Tutorial';
19+
let percentage = 0;
20+
21+
if (
22+
optionalChaining(
23+
() => data.getTutorialbyGatsbyID.viewerUserTutorial.currentChapter,
24+
)
25+
) {
26+
percentage = percent(
27+
data.getTutorialbyGatsbyID.numberofChapters,
28+
data.getTutorialbyGatsbyID.viewerUserTutorial.currentChapter,
29+
);
30+
buttonText = 'Continue Tutorial';
31+
if (percentage === 100) {
32+
buttonText = 'Take Again';
33+
}
34+
}
35+
return (
36+
<div>
37+
<TutorialButton>{buttonText}</TutorialButton>
38+
{!!percentage && (
39+
<Box m={3}>
40+
<ProgressBar percentage={percentage} />
41+
</Box>
42+
)}
43+
<Flex>
44+
<GithubButton>Github</GithubButton>
45+
<SpectrumButton>Spectrum</SpectrumButton>
46+
</Flex>
47+
<AuthorList authors={authors} />
48+
</div>
49+
);
50+
}}
51+
</Query>
52+
);
53+
54+
export default AuthorsProgressBox;

packages/gatsby-theme/src/utils/helpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ export function optionalChaining(func: Function) {
77
}
88

99
export const percent = (numberofChapters, currentChapter) =>
10-
currentChapter ? Math.floor((currentChapter / numberofChapters) * 100) : 0;
10+
currentChapter ? Math.round((currentChapter / numberofChapters) * 100) : 0;

packages/server/src/schema.graphql

Lines changed: 11 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
### This file was autogenerated by Nexus 0.11.6
22
### Do not make changes to this file directly
33

4+
45
type AuthenticateUserPayload implements PayloadInterface {
56
code: String
67
message: String
@@ -13,40 +14,24 @@ scalar DateTime
1314

1415
type Mutation {
1516
authenticate(
16-
"""
17-
GitHub OAuth Token from the client.
18-
"""
17+
"""GitHub OAuth Token from the client."""
1918
githubCode: String!
2019
): AuthenticateUserPayload
2120

22-
"""
23-
An authenticated user can bookmark a tutorial.
24-
"""
21+
"""An authenticated user can bookmark a tutorial."""
2522
bookmarkTutorial(tutorialId: ID!): UserTutorialPayload!
2623

27-
"""
28-
An authenticated user can update their current chapter in a tutorial.
29-
"""
24+
"""An authenticated user can update their current chapter in a tutorial."""
3025
upsertCurrentChapter(chapter: Int!, gatsbyID: String!): UserTutorialPayload!
3126

32-
"""
33-
Create tutorials from the MDX files
34-
"""
35-
upsertTutorial(
36-
gatsbyID: String!
37-
name: String!
38-
numberofChapters: Int!
39-
): Tutorial!
27+
"""Create tutorials from the MDX files"""
28+
upsertTutorial(gatsbyID: String!, name: String!, numberofChapters: Int!): Tutorial!
4029

41-
"""
42-
An authenticated user can upvote a tutorial.
43-
"""
30+
"""An authenticated user can upvote a tutorial."""
4431
upvoteTutorial(tutorialId: ID!): UserTutorialPayload!
4532
}
4633

47-
"""
48-
The standard interface for all mutation responses
49-
"""
34+
"""The standard interface for all mutation responses"""
5035
interface PayloadInterface {
5136
code: String
5237
message: String
@@ -70,19 +55,9 @@ type Tutorial {
7055
numberOfStudents: Int!
7156
updatedAt: DateTime!
7257
upvotes: Int!
73-
userTutorials(
74-
after: String
75-
before: String
76-
first: Int
77-
last: Int
78-
orderBy: UserTutorialOrderByInput
79-
skip: Int
80-
where: UserTutorialWhereInput
81-
): [UserTutorial!]
58+
userTutorials(after: String, before: String, first: Int, last: Int, orderBy: UserTutorialOrderByInput, skip: Int, where: UserTutorialWhereInput): [UserTutorial!]
8259

83-
"""
84-
The UserTutorial for the current user associated with this Tutorial.
85-
"""
60+
"""The UserTutorial for the current user associated with this Tutorial."""
8661
viewerUserTutorial: UserTutorial
8762
}
8863

@@ -189,15 +164,7 @@ type User {
189164
id: ID!
190165
name: String!
191166
updatedAt: DateTime!
192-
userTutorials(
193-
after: String
194-
before: String
195-
first: Int
196-
last: Int
197-
orderBy: UserTutorialOrderByInput
198-
skip: Int
199-
where: UserTutorialWhereInput
200-
): [UserTutorial!]
167+
userTutorials(after: String, before: String, first: Int, last: Int, orderBy: UserTutorialOrderByInput, skip: Int, where: UserTutorialWhereInput): [UserTutorial!]
201168
}
202169

203170
type UserTutorial {

0 commit comments

Comments
 (0)