Skip to content

Commit

Permalink
feat: implement generic marking page
Browse files Browse the repository at this point in the history
  • Loading branch information
procaconsul committed Jun 13, 2024
1 parent 7993542 commit 6ef7f93
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 20 deletions.
5 changes: 5 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Navigate, RouterProvider, createBrowserRouter } from 'react-router-dom'

import ExamRoot from './pages/ExamRoot'
import FrontCover from './pages/FrontCover'
import MarkingPage from './pages/MarkingPage'
import QuestionPage from './pages/QuestionPage'

const router = createBrowserRouter([
Expand All @@ -22,6 +23,10 @@ const router = createBrowserRouter([
path: 'questions/:questionId',
element: <QuestionPage />,
},
{
path: 'marking',
element: <MarkingPage />,
},
],
},
])
Expand Down
1 change: 1 addition & 0 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const routes = {
summary: '/summary',
questions: '/questions',
question: (number: number) => `/questions/${number}`,
questionAnswers: (number: number) => `/questions/${number}/answer`,
}
Expand Down
18 changes: 0 additions & 18 deletions src/hooks/assessmentSummary.ts

This file was deleted.

27 changes: 26 additions & 1 deletion src/hooks/exam.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { plainToInstance } from 'class-transformer'
import { mapValues } from 'lodash'
import { useCallback, useEffect, useMemo, useState } from 'react'

import axiosInstance from '../api/axiosInstance'
import routes from '../api/routes'
import { Answer, Question, QuestionAnswersLookup } from '../types/exam'
import { Answer, Question, QuestionAnswersLookup, Summary } from '../types/exam'
import { buildAnswerLookupTable } from '../utils/answers'

export const useQuestion = (number: number | undefined) => {
Expand Down Expand Up @@ -43,3 +44,27 @@ export const useQuestionAnswers = (number: number | undefined) => {

return { lookupAnswer, answersAreLoaded }
}

export const useAssessmentSummary = () => {
const [summary, setSummary] = useState<Summary>()
const [summaryIsLoaded, setSummaryIsLoaded] = useState(false)
useEffect(() => {
axiosInstance
.get(routes.summary)
.then(({ data }) => setSummary(plainToInstance(Summary, data)))
.finally(() => setSummaryIsLoaded(true))
}, [])
return { summary, summaryIsLoaded }
}

export const useQuestions = () => {
const [questions, setQuestions] = useState<Record<number, Question>>()
const [questionsAreLoaded, setQuestionsAreLoaded] = useState(false)
useEffect(() => {
axiosInstance
.get(routes.questions)
.then(({ data }) => setQuestions(mapValues(data, (q) => plainToInstance(Question, q))))
.finally(() => setQuestionsAreLoaded(true))
}, [])
return { questions, questionsAreLoaded }
}
2 changes: 1 addition & 1 deletion src/pages/FrontCover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import LogisticsBanner from '../components/LogisticsBanner'
import Markdown from '../components/Markdown'
import Body from '../components/pageStructure/Body'
import Header from '../components/pageStructure/Header'
import { useAssessmentSummary } from '../hooks/assessmentSummary'
import { useAssessmentSummary } from '../hooks/exam'

const FrontCover: FC = () => {
const { summary, summaryIsLoaded } = useAssessmentSummary()
Expand Down
90 changes: 90 additions & 0 deletions src/pages/MarkingPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { Container, Flex, Heading, Section as LayoutSection, Separator } from '@radix-ui/themes'
import { instanceToPlain } from 'class-transformer'
import { map, sum } from 'lodash'
import React, { FC, useEffect } from 'react'

import Body from '../components/pageStructure/Body'
import Part from '../components/questionStructure/Part'
import Question from '../components/questionStructure/Question'
import Section from '../components/questionStructure/Section'
import { TaskFactory, TaskProps } from '../components/questionStructure/Task'
import { useQuestions } from '../hooks/exam'

const MarkingPage: FC = () => {
const { questions, questionsAreLoaded } = useQuestions()

useEffect(() => console.log({ questions }), [questions])

const handler = (v) => {}

if (!questionsAreLoaded) return <div>Loading...</div>
if (!questions) return <div>Failed to load questions</div>

return (
<>
{Object.entries(questions).map(([questionIDString, question]) => {
return (
<>
<LayoutSection pb="0" mb="-6">
<Container size="4" px="6">
<Flex direction="column" gap="4">
<Flex gap="2">
<Heading size="8">{`Question ${questionIDString}`}</Heading>
{question.title && (
<Heading size="8" as="h2" color="gray" weight="medium">
{question.title}
</Heading>
)}
</Flex>
<Separator size="4" />
</Flex>
</Container>
</LayoutSection>
<Body>
<Question instructions={question.instructions}>
{Object.entries(question.parts).map(([partIDString, part]) => {
const partID = Number(partIDString)
return (
<Part
key={partID}
partId={partID}
description={part.instructions}
marksContribution={sum(map(part.sections, 'maximumMark'))}
onSave={handler}
>
{Object.entries(part.sections).map(([sectionIDString, section], i) => {
const sectionID = Number(sectionIDString)
return (
<Section
key={sectionID}
sectionId={sectionID}
description={section.instructions}
>
{section.tasks.map((task, i) => {
return (
<TaskFactory
key={i}
{...({
onAnswerUpdate: handler,
...instanceToPlain(task),
} as TaskProps)}
/>
)
})}
{i + 1 !== Object.keys(part.sections).length && <Separator size="4" />}
</Section>
)
})}
</Part>
)
})}
</Question>
</Body>
</>
)
})}
</>
)
}

export default MarkingPage

0 comments on commit 6ef7f93

Please sign in to comment.