Skip to content

Commit

Permalink
Implement frontend tracking
Browse files Browse the repository at this point in the history
  • Loading branch information
AymericBethencourt committed Jun 22, 2020
1 parent e99a8f7 commit 0901201
Show file tree
Hide file tree
Showing 32 changed files with 256 additions and 729 deletions.
1 change: 1 addition & 0 deletions src/api/src/resolvers/user/addProgress/addProgress.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ describe('User', () => {

expect(ctx.body.user).toBeDefined()
expect(ctx.body.user.progress).toContain('/pascal/chapter-polymorphism')

done()
})

Expand Down
3 changes: 2 additions & 1 deletion src/api/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ router.post('/user/sign-up', signUp)
router.post('/user/login', login)
router.post('/user/verify-email', verifyEmail)
router.post('/user/resend-email-verification', resendEmailVerification)
router.post('/user/get-public-user', getPublicUser)
router.post('/user/add-progress', addProgress)
router.post('/user/reset-password', resetPassword)
router.post('/user/forgot-password', forgotPassword)
router.post('/user/change-password', changePassword)

router.post('/page/get-user', getPublicUser)

export { router }
2 changes: 1 addition & 1 deletion src/api/src/sanitize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ResponseError } from './shared/mongo/ResponseError'

export const sanitize = () => async (ctx: Context, next: Next): Promise<void> => {
const body = JSON.stringify(ctx.request.body)
const forbidden = new RegExp('<|/|>', 'i')
const forbidden = new RegExp('<|>', 'i')
if (forbidden.test(body)) throw new ResponseError(400, 'Forbidden characters')
else await next()
}
30 changes: 30 additions & 0 deletions src/frontend/public/icons/badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/frontend/public/icons/check.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 8 additions & 10 deletions src/frontend/public/images/badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 1 addition & 6 deletions src/frontend/src/app/App.components/Header/Header.view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,7 @@ function loggedOutHeader() {
function loggedInHeader({ user, removeAuthUserCallback }: HeaderViewProps) {
return (
<HeaderLoggedIn>
<Link
to={`/user/${user?.username}`}
onClick={() => {
removeAuthUserCallback()
}}
>
<Link to={`/user/${user?.username}`}>
<HeaderMenuItem>{user?.username}</HeaderMenuItem>
</Link>
<Link
Expand Down
10 changes: 4 additions & 6 deletions src/frontend/src/app/App.routes.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { ChangePassword } from 'pages/ChangePassword/ChangePassword.controller'
import { Chapter } from 'pages/Chapter/Chapter.controller'
import { ChapterAbout as ChapterAboutCamel } from 'pages/Chapters/Camel/ChapterAbout/ChapterAbout.controller'
import { ChapterAbout as ChapterAboutPascal } from 'pages/Chapters/Pascal/ChapterAbout/ChapterAbout.controller'
import { ChapterAbout as ChapterAboutReason } from 'pages/Chapters/Reason/ChapterAbout/ChapterAbout.controller'
import { ChapterAbout } from 'pages/ChapterAbout/ChapterAbout.controller'
import { Error404 } from 'pages/Error404/Error404.controller'
import { ForgotPassword } from 'pages/ForgotPassword/ForgotPassword.controller'
import { Home } from 'pages/Home/Home.controller'
Expand Down Expand Up @@ -38,13 +36,13 @@ export const AppRoutes = ({ location }: any) => (
<ChangePassword />
</Route>
<Route exact path="/pascal/chapter-about">
<ChapterAboutPascal />
<ChapterAbout />
</Route>
<Route exact path="/reason/chapter-about">
<ChapterAboutReason />
<ChapterAbout />
</Route>
<Route exact path="/camel/chapter-about">
<ChapterAboutCamel />
<ChapterAbout />
</Route>
<Route path="/pascal/chapter-*">
<Chapter />
Expand Down
25 changes: 25 additions & 0 deletions src/frontend/src/pages/Chapter/Chapter.actions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { store } from 'index'
import { AddProgressInputs } from 'shared/user/AddProgress'

export const ADD_PROGRESS_REQUEST = 'ADD_PROGRESS_REQUEST'
export const ADD_PROGRESS_COMMIT = 'ADD_PROGRESS_COMMIT'
export const ADD_PROGRESS_ROLLBACK = 'ADD_PROGRESS_ROLLBACK'

export const addProgress = ({ chapterDone }: AddProgressInputs) => (dispatch: any) => {
dispatch({
type: ADD_PROGRESS_REQUEST,
payload: {},
meta: {
offline: {
effect: {
url: `${process.env.REACT_APP_BACKEND_URL}/user/add-progress`,
method: 'POST',
headers: { Authorization: `Bearer ${store.getState().auth.jwt}` },
json: { chapterDone },
},
commit: { type: ADD_PROGRESS_COMMIT, meta: {} },
rollback: { type: ADD_PROGRESS_ROLLBACK, meta: {} },
},
},
})
}
17 changes: 11 additions & 6 deletions src/frontend/src/pages/Chapter/Chapter.controller.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import * as React from 'react'
import { useEffect } from 'react'
import { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useLocation } from 'react-router-dom'
import { State } from 'reducers'

import { PENDING, RIGHT, WRONG } from '../Chapters/Pascal/ChapterAbout/ChapterAbout.constants'

import { ChapterView } from './Chapter.view'
import { chapterData } from './Chapter.data'
import { PENDING, RIGHT, WRONG } from '../ChapterAbout/ChapterAbout.constants'
import { addProgress } from './Chapter.actions'
import { Footer } from './Chapter.components/Footer/Footer.controller'
import { chapterData } from './Chapter.data'
import { ChapterView } from './Chapter.view'

export const Chapter = () => {
const [validatorState, setValidatorState] = useState(PENDING)
const [showDiff, setShowDiff] = useState(false)
const { pathname } = useLocation()
const [data, setData] = useState({ course: undefined, exercise: undefined, solution: undefined })
const dispatch = useDispatch()
const user = useSelector((state: State) => state.auth.user)

useEffect(() => {
chapterData.forEach((chapter) => {
Expand All @@ -34,9 +38,10 @@ export const Chapter = () => {
data.exercise.replace(/\s+|\/\/ Type your solution below/g, '') ===
// @ts-ignore
data.solution.replace(/\s+|\/\/ Type your solution below/g, '')
)
) {
setValidatorState(RIGHT)
else setValidatorState(WRONG)
if (user) dispatch(addProgress({ chapterDone: pathname }))
} else setValidatorState(WRONG)
} else setValidatorState(WRONG)
}
}
Expand Down
34 changes: 34 additions & 0 deletions src/frontend/src/pages/Chapter/Chapter.reducers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ADD_PROGRESS_REQUEST, ADD_PROGRESS_COMMIT, ADD_PROGRESS_ROLLBACK } from './Chapter.actions'

export interface ProgressState {
loading?: boolean
}

const progressState: ProgressState = {
loading: false,
}

export function progress(state = progressState, action: any): ProgressState {
switch (action.type) {
case ADD_PROGRESS_REQUEST: {
return {
...state,
loading: true,
}
}
case ADD_PROGRESS_COMMIT: {
return {
...state,
loading: false,
}
}
case ADD_PROGRESS_ROLLBACK: {
return {
...state,
loading: false,
}
}
default:
return state
}
}
4 changes: 2 additions & 2 deletions src/frontend/src/pages/Chapter/Chapter.view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import Markdown from 'markdown-to-jsx'
import * as PropTypes from 'prop-types'
import * as React from 'react'

import { PENDING, RIGHT, WRONG } from '../Chapters/Pascal/ChapterAbout/ChapterAbout.constants'
import { PENDING, RIGHT, WRONG } from '../ChapterAbout/ChapterAbout.constants'
//prettier-ignore
import { Button, ButtonBorder, ButtonText, ChapterCourse, ChapterGrid, ChapterH1, ChapterH2, ChapterItalic, ChapterMonaco, ChapterStyled, ChapterValidator, ChapterValidatorContent, ChapterValidatorContentWrapper, ChapterValidatorInside, ChapterValidatorTitle } from "../Chapters/Pascal/ChapterAbout/ChapterAbout.style";
import { Button, ButtonBorder, ButtonText, ChapterCourse, ChapterGrid, ChapterH1, ChapterH2, ChapterItalic, ChapterMonaco, ChapterStyled, ChapterValidator, ChapterValidatorContent, ChapterValidatorContentWrapper, ChapterValidatorInside, ChapterValidatorTitle } from "../ChapterAbout/ChapterAbout.style";
import { CardBottomCorners, CardTopCorners } from './Chapter.components/Card/Card.style'
import { Dialog } from './Chapter.components/Dialog/Dialog.controller'
import { Light } from './Chapter.components/Light/Light.view'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
import { addProgress } from 'pages/Chapter/Chapter.actions'
import { Footer } from 'pages/Chapter/Chapter.components/Footer/Footer.controller'
import * as React from 'react'
import { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { useLocation } from 'react-router-dom'
import { State } from 'reducers'

import { ChapterAboutView } from './ChapterAbout.view'
import { PENDING, RIGHT, WRONG } from './ChapterAbout.constants'
import { Footer } from 'pages/Chapter/Chapter.components/Footer/Footer.controller'
import { ChapterAboutView } from './ChapterAbout.view'

export const ChapterAbout = () => {
const [validatorState, setValidatorState] = useState(PENDING)
const dispatch = useDispatch()
const user = useSelector((state: State) => state.auth.user)
const { pathname } = useLocation()

const validateCallback = () => {
const shipId = document.getElementById('ship-id')?.textContent
if (shipId === '020433') setValidatorState(RIGHT)
else setValidatorState(WRONG)
if (shipId === '020433') {
setValidatorState(RIGHT)
if (user) dispatch(addProgress({ chapterDone: pathname }))
} else setValidatorState(WRONG)
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export const data = `#Chapter 1 : Tezos Academy
Tezos Academy is a fun interactive tutorial developed by <a href="https://octo.com/" target="_blank">OCTO Technology</a> on how to code smart contracts for <a href="https://tezos.com/" target="_blank">Tezos</a>. You are about to create spaceship battles smart contracts!
By default, Tezos smart contracts are written in <a href="https://tezos.gitlab.io/whitedoc/michelson.html" target="_blank">Michelson</a>, but it is an hard to learn low level formal language. For this tutorial, we will use <a href="https://ligolang.org/" target="_blank">PascaLIGO</a> instead. The syntax is high level, close to Pascal and transpiles to Michelson.
By default, Tezos smart contracts are written in <a href="https://tezos.gitlab.io/whitedoc/michelson.html" target="_blank">Michelson</a>, but it is an hard to learn low level formal language. For this tutorial, we will use <a href="https://ligolang.org/" target="_blank">LIGO</a> instead. The syntax is high level and transpiles to Michelson.
## Objectives
Expand All @@ -31,4 +31,4 @@ For our tutorial, we only designed a few parts for each attribute, 3 different c
On the top-right panel, go ahead and move the slider of each attribute. Notice the Ship ID above the ship image and how the different numerical values correspond to the different ship appearance. Your mission is to select the ship with ID 020433 and click _Validate Mission_ below.
Next, we will start coding some LIGO!
`;
`

This file was deleted.

Loading

0 comments on commit 0901201

Please sign in to comment.