diff --git a/frontend/public/video/cake.mp4 b/frontend/public/video/cake.mp4 new file mode 100755 index 0000000..32950f9 Binary files /dev/null and b/frontend/public/video/cake.mp4 differ diff --git a/frontend/public/video/dango.mp4 b/frontend/public/video/dango.mp4 new file mode 100644 index 0000000..46bc0be Binary files /dev/null and b/frontend/public/video/dango.mp4 differ diff --git a/frontend/src/components/VerbalPronunciationExercise.tsx b/frontend/src/components/VerbalPronunciationExercise.tsx index 1a209e7..ec591a9 100644 --- a/frontend/src/components/VerbalPronunciationExercise.tsx +++ b/frontend/src/components/VerbalPronunciationExercise.tsx @@ -1,20 +1,18 @@ import { useLocation, useNavigate } from 'react-router-dom'; import { Icon } from '@iconify/react'; import { useState, useEffect, useRef, useCallback } from 'react'; -import { speechToText, getAIMarking } from '../logic/sdk'; +import {speechToText, getAIMarking, getLanguage} from '../logic/sdk'; import ClipLoader from "react-spinners/ClipLoader"; +import {VerbalPronunciation} from "../logic/CourseData"; interface VerbalPronunciationProps { - question: string; - expected: string; + q: VerbalPronunciation; chapter: string; - language: string; - onQuestionSubmit: Function; + onSubmit: Function; } -export default function VerbalPronunciationExercise({question, expected, chapter, language, onQuestionSubmit}: VerbalPronunciationProps) { - const location = useLocation(); - const navigate = useNavigate(); +export default function VerbalPronunciationExercise({q, chapter, onSubmit}: VerbalPronunciationProps) { + const language = getLanguage().name; const [answered, setAnswered] = useState(false); const [loading, setLoading] = useState(false); const [correct, setCorrect] = useState(""); @@ -23,11 +21,10 @@ export default function VerbalPronunciationExercise({question, expected, chapter const handleSubmit = () => { if (answered) { setAnswered(false); - onQuestionSubmit(); + onSubmit(); } } - type Message = { text: string, sender: string }; const [isRecording, setIsRecording] = useState(false); let chunks = [] as any; @@ -49,7 +46,7 @@ export default function VerbalPronunciationExercise({question, expected, chapter const audioFile = new File([blob], "audio.wav", { type: 'audio/wav' }); const text = await speechToText(audioFile); - const aiMark = await getAIMarking(question, text.toLowerCase(), expected.toLowerCase(), chapter, language); + const aiMark = await getAIMarking(q.question, text.toLowerCase(), "", chapter, language); setAnswered(true); setCorrect(aiMark.correct); setReason(aiMark.reason); @@ -102,10 +99,10 @@ export default function VerbalPronunciationExercise({question, expected, chapter return (
-
+

Say the following

- {question} + {q.question}
{ResponseSection(correct, reason)}
diff --git a/frontend/src/components/VerbalQuestionsExercise.tsx b/frontend/src/components/VerbalQuestionsExercise.tsx index 1d8d01f..872edce 100644 --- a/frontend/src/components/VerbalQuestionsExercise.tsx +++ b/frontend/src/components/VerbalQuestionsExercise.tsx @@ -1,26 +1,23 @@ import { useState } from 'react'; -import { getAIMarking } from '../logic/sdk'; +import {getAIMarking, getLanguage} from '../logic/sdk'; import ClipLoader from "react-spinners/ClipLoader"; import { Icon } from '@iconify/react'; +import {VerbalQuestion} from "../logic/CourseData"; interface VerbalQuestionProps { - question: string; - wordBank: string[]; - expected: string; + q: VerbalQuestion chapter: string; - language: string; - onQuestionSubmit: Function; + onSubmit: Function; } -export default function VerbalQuestionsExercise({question, wordBank, expected, chapter, language, onQuestionSubmit}: VerbalQuestionProps) { +export default function VerbalQuestionsExercise({q, chapter, onSubmit}: VerbalQuestionProps) { + const language = getLanguage().name; const [selectedWords, setSelectedWords] = useState([]); - const [remainingWords, setRemainingWords] = useState(wordBank); - const lastPunctuation = question[question.length - 1]; + const [remainingWords, setRemainingWords] = useState(q.wordBank); const [answered, setAnswered] = useState(false); const [correct, setCorrect] = useState(""); const [reason, setReason] = useState(""); const [loading, setLoading] = useState(false); - const [isListening, setListening] = useState(false); const handleWordBankClick = (word: string) => { setSelectedWords([...selectedWords, word]); @@ -33,16 +30,16 @@ export default function VerbalQuestionsExercise({question, wordBank, expected, c } const handleSubmit = () => { - const userAnswer = selectedWords.join(" ") + lastPunctuation; + const userAnswer = selectedWords.join(""); if (answered) { setAnswered(false); - onQuestionSubmit(); + onSubmit(); } else { setLoading(true); getAIMarking( - question, + q.question, userAnswer.toLowerCase(), - expected.toLowerCase(), + q.expected.toLowerCase(), chapter, language ).then((res) => { @@ -58,7 +55,7 @@ export default function VerbalQuestionsExercise({question, wordBank, expected, c const ResponseSection = (correct: string | null, reason: string | null) => { if (!answered) { return ( -
+
{remainingWords.map((word, index) => ( { - // TODO: Play static file that holds the audio for the current - console.log("Heard you"); - } - return (
-

What do you hear?

-
- -
-
+

What do you hear?

+ +
{selectedWords.map((word, index) => ( ) => { + setUserAnswer(e.target.value); + } + + const handleSubmit = () => { + if (answered) { + setAnswered(false); + onSubmit(); + } else { + setLoading(true); + getAIMarking( + `Please watch the video and answer the question: "${q.question}".`, + userAnswer.toLowerCase(), + `${q.expected} (For the grader, here is the video description: ${q.description}. And please accept any reasonable answer in both English and ${language.name}).`, + chapter, + language.name + ).then((res) => { + setLoading(false); + setAnswered(true); + setCorrect(res.correct); + setReason(res.reason); + }).catch((e) => console.error(e)); + } + } + + return ( +
+
+ {q.question} +
+ +
+ {!answered ? +
+ + +
: +
+

{correct}

+

{reason}

+ +
+ } +
+
+ ); +} diff --git a/frontend/src/components/WrittenQuestionExercise.tsx b/frontend/src/components/WrittenQuestionExercise.tsx index ed3803d..b8f05a3 100644 --- a/frontend/src/components/WrittenQuestionExercise.tsx +++ b/frontend/src/components/WrittenQuestionExercise.tsx @@ -1,20 +1,18 @@ import { useState } from 'react'; -import { getAIMarking } from '../logic/sdk'; +import {getAIMarking, getLanguage} from '../logic/sdk'; import ClipLoader from "react-spinners/ClipLoader"; +import {WrittenQuestion} from "../logic/CourseData"; interface WrittenQuestionProps { - question: string; - wordBank: string[]; - expected: string; + q: WrittenQuestion chapter: string; - language: string; - onQuestionSubmit: Function; + onSubmit: Function; } -export default function WrittenQuestionExercise({question, wordBank, expected, chapter, language, onQuestionSubmit}: WrittenQuestionProps) { +export default function WrittenQuestionExercise({q, chapter, onSubmit}: WrittenQuestionProps) { + const language = getLanguage(); const [selectedWords, setSelectedWords] = useState([]); - const [remainingWords, setRemainingWords] = useState(wordBank); - const lastPunctuation = question[question.length - 1]; + const [remainingWords, setRemainingWords] = useState(q.wordBank); const [answered, setAnswered] = useState(false); const [correct, setCorrect] = useState(""); const [reason, setReason] = useState(""); @@ -31,18 +29,22 @@ export default function WrittenQuestionExercise({question, wordBank, expected, c } const handleSubmit = () => { - const userAnswer = selectedWords.join(" ") + lastPunctuation; + // TODO: This space join isn't correct. + // When the word bank is Japanese or Chinese, the words are not separated by spaces. + // When the langauge is English, the words are separated by spaces. + // We need to figure out how to handle this. + const userAnswer = selectedWords.join(" "); if (answered) { setAnswered(false); - onQuestionSubmit(); + onSubmit(); } else { setLoading(true); getAIMarking( - question, + q.question, userAnswer.toLowerCase(), - expected.toLowerCase(), + q.expected.toLowerCase(), chapter, - language + language.name ).then((res) => { setLoading(false); setAnswered(true); @@ -50,7 +52,6 @@ export default function WrittenQuestionExercise({question, wordBank, expected, c setReason(res.reason); }).catch((e) => console.error(e)); } - } const ResponseSection = (correct: string | null, reason: string | null) => { @@ -62,12 +63,12 @@ export default function WrittenQuestionExercise({question, wordBank, expected, c handleWordBankClick(word)}> + onClick={() => handleWordBankClick(word)}> {word} ))}
- +
) } @@ -92,14 +93,14 @@ export default function WrittenQuestionExercise({question, wordBank, expected, c return (
- {question} + {q.question}
{selectedWords.map((word, index) => ( handleSelectedWordClick(word)}> + onClick={() => handleSelectedWordClick(word)}> {word} ))} diff --git a/frontend/src/components/WrittenVocabularyExercise.tsx b/frontend/src/components/WrittenVocabularyExercise.tsx index c1916c4..c9f5515 100644 --- a/frontend/src/components/WrittenVocabularyExercise.tsx +++ b/frontend/src/components/WrittenVocabularyExercise.tsx @@ -1,37 +1,34 @@ import { useState } from 'react'; +import {VocabularyQuestion} from "../logic/CourseData"; interface WrittenVocabularyProps { - question: string; - pronunciation: string; - definition: string; - example: string; - onQuestionSubmit: Function; + q: VocabularyQuestion + onSubmit: Function; } -export default function WrittenQuestionExercise({question, pronunciation, definition, example, onQuestionSubmit}: WrittenVocabularyProps) { +export default function WrittenQuestionExercise({q, onSubmit}: WrittenVocabularyProps) { const [answered, setAnswered] = useState(false); const handleSubmit = () => { if (answered) { setAnswered(false); - onQuestionSubmit(); + onSubmit(); } else { setAnswered(true); } - } return (
- {question} + {q.question}
{answered ?
-

{pronunciation}

-

{definition}

-

{example}

+

{q.pronunciation}

+

{q.description}

+

{q.example}

diff --git a/frontend/src/index.sass b/frontend/src/index.sass index 50acb6b..442c5f8 100644 --- a/frontend/src/index.sass +++ b/frontend/src/index.sass @@ -139,6 +139,7 @@ h2 .page-pad padding: 2em 1em height: 100% + @extend .non-center .navbar position: fixed diff --git a/frontend/src/logic/CourseData.ts b/frontend/src/logic/CourseData.ts index 736369e..d0015e9 100644 --- a/frontend/src/logic/CourseData.ts +++ b/frontend/src/logic/CourseData.ts @@ -1,27 +1,57 @@ - - export interface Chapter { - name: string; - steps: Step[]; + name: string + steps: Step[] } export interface Step { - questions: Question[]; + questions: Question[] } -export interface Question +export interface _Question { - question: string; - wordBank?: string[]; - expected?: string; - type: string; - pronunciation?: string; - description?: string; - example?: string; - translation?: string; - clipUrl?: string; + question: string + type: string +} +export type Question = WrittenQuestion | VocabularyQuestion | VerbalQuestion | VerbalPronunciation | VideoQuestion + +export interface WrittenQuestion extends _Question +{ + wordBank: string[] + expected: string + type: 'written-question' +} + +export interface VocabularyQuestion extends _Question +{ + pronunciation: string + description: string + example: string + type: 'written-vocabulary' +} + +export interface VerbalQuestion extends _Question +{ + wordBank: string[] + expected: string + translation: string + url: string + type: 'verbal-question' +} + +export interface VerbalPronunciation extends _Question +{ + translation: string + type: 'verbal-pronunciation' +} + +export interface VideoQuestion extends _Question +{ + clipUrl: string + description: string + expected: string + type: 'video' } export const chapters_jp: Chapter[] = [ @@ -29,6 +59,14 @@ export const chapters_jp: Chapter[] = [ name: 'Order food', steps: [{ questions: [ + + { + question: 'What did Yui come to the club meeting for?', + clipUrl: window.location.origin + "/video/cake.mp4", + description: "This is a clip from the anime 'K-On'. Yui is a member of the light music club. She came to the club meeting to eat cake.", + expected: 'ケーキ or cake', + type: 'video', + }, { question: 'Translate this sentence: すしをください', wordBank: ['I', 'sushi', 'cookies', 'want', 'please', 'give', 'rice', 'some', 'yesterday'], @@ -43,60 +81,59 @@ export const chapters_jp: Chapter[] = [ type: 'written-vocabulary', }, { - question: 'Transcribe this sentence', + question: 'What do you hear?', wordBank: ['刺身', 'ケーキ', 'の', 'は', 'おいしい', 'おもい', 'すし', '中', 'です'], expected: 'ケーキはおいしいです', translation: 'Cake is delicious', + url: window.location.origin + '/audio/jp_1_1_3.mp3', type: 'verbal-question', }, { - question: 'すしおたくさんあります', + question: 'Please say: すしをたくさんあります', translation: 'There is a lot of sushi', type: 'verbal-pronunciation', }, - { - question: 'What food does Yui like?', - clipUrl: "https://fdjsakfjs.com", - expected: 'ケーキ', - type: 'video', - } ] }, - { - questions: [ - { - question: 'Translate this sentence: 水をください', - wordBank: ['I', 'sushi', 'water', 'want', 'please', 'give', 'rice', 'some', 'yesterday'], - expected: 'Water please / I want water', - type: "written-question" - }, - { - question: '団子', - pronunciation: 'だんご (dango)', - description: 'Dango is a kind of mochi, Japanese dumpling and sweet made from mochiko (rice flour).', - example: '大きいな団子です。(It is a big dango)', - type: 'written-vocabulary', - }, - { - question: 'Transcribe this sentence', - wordBank: ['刺身', '水', 'は', 'おいしい', 'おもい', 'すし', '中', 'です'], - expected: '水です', - translation: 'It is water', - type: 'verbal-question', - }, - { - question: '刺身おください', - translation: 'Please give me sashimi', - type: 'verbal-pronunciation', - }, - { - question: 'What is the following song about?', - clipUrl: "https://www.youtube.com/watch?v=qfgyKPQO9g8", - description: "This is the song 'Dango Daikazoku' from the anime 'Clannad'. It is about a family of dango.", - expected: '団子', - type: 'video', - } - ] - }] + { + questions: [ + { + // question: "Translate this sentence: Water please", + // wordBank: ['水', 'を', 'ください', 'おいしい', 'おもい', 'すし', '中', 'です'], + question: 'Translate this sentence: 水をください', + wordBank: ['I', 'sushi', 'cookies', 'want', 'please', 'give', 'rice', 'some', 'yesterday'], + // expected: '水をください', + expected: 'Water please', + type: "written-question" + }, + { + question: '団子', + pronunciation: 'だんご (dango)', + description: 'Dango is a kind of mochi, Japanese dumpling and sweet made from mochiko (rice flour).', + example: '大きいな団子です。(It is a big dango)', + type: 'written-vocabulary', + }, + { + question: 'What do you hear?', + wordBank: ['刺身', '水', 'は', 'おいしい', 'おもい', 'すし', '中', 'です'], + expected: '水です', + translation: 'It is water', + url: window.location.origin + '/audio/jp_1_2_3.mp3', + type: 'verbal-question', + }, + { + question: 'Please say: 刺身おください', + translation: 'Please give me sashimi', + type: 'verbal-pronunciation', + }, + { + question: 'What is the following song about?', + clipUrl: window.location.origin + "/video/dango.mp4", + description: "This is the song 'Dango Daikazoku' from the anime 'Clannad'. It is about a family of dango.", + expected: '団子 or dango', + type: 'video', + } + ] + }] } ] diff --git a/frontend/src/pages/Course.tsx b/frontend/src/pages/Course.tsx index 85f4d5c..5a71a8c 100644 --- a/frontend/src/pages/Course.tsx +++ b/frontend/src/pages/Course.tsx @@ -3,6 +3,8 @@ import {Icon} from "@iconify/react"; import {getLanguage, isLoggedIn, isStepCompleted} from "../logic/sdk"; import React from "react"; import "./Course.sass" +import {Step} from "../logic/CourseData"; +import {useLocation, useNavigate} from "react-router-dom"; function CourseButton(props: {state: 'active' | 'locked' | 'completed', index: number}) { @@ -17,16 +19,17 @@ function CourseButton(props: {state: 'active' | 'locked' | 'completed', index: n // Calculate the horizontal translation const translateXValue = amplitude * Math.sin(props.index * frequency); - return ( -
- -
- ) + return
+ +
} export default function Course() { + const location = useLocation(); + const navigate = useNavigate(); + if (!isLoggedIn()) { window.location.href = '/login'; @@ -36,6 +39,11 @@ export default function Course() // Get language const lang = getLanguage(); + function click(step: Step) + { + navigate('/lesson', {state: {questions: step.questions, home: location.pathname}}) + } + return (
@@ -46,20 +54,21 @@ export default function Course()
- {lang.data.map((chapter, i) => <> -
+ {lang.data.map((chapter, i) =>
+
Chapter 1, Section 1
{chapter.name}
- {chapter.steps.map((step, i) => + {chapter.steps.map((step, i) =>
click(step)}> )} + index={i}/>
)} + {[...Array(5)].map((_, i) => )}
- )} +
)}
) diff --git a/frontend/src/pages/Lesson.tsx b/frontend/src/pages/Lesson.tsx index 7f32fb5..78fe5f3 100644 --- a/frontend/src/pages/Lesson.tsx +++ b/frontend/src/pages/Lesson.tsx @@ -1,91 +1,63 @@ -import { useLocation, useNavigate } from 'react-router-dom'; -import { useState } from 'react'; +import {useLocation, useNavigate} from 'react-router-dom'; +import React, {useState} from 'react'; import WrittenQuestionExercise from "../components/WrittenQuestionExercise" import WrittenVocabularyExercise from "../components/WrittenVocabularyExercise" import VerbalQuestionsExercise from "../components/VerbalQuestionsExercise" import Progress from '../components/Progress'; import VerbalPronunciationExercise from '../components/VerbalPronunciationExercise'; +import {_Question, chapters_jp, Question} from "../logic/CourseData"; +import VideoExercise from "../components/VideoExercise"; -export default function Lesson() { - const location = useLocation(); - const navigate = useNavigate(); - const { questions, home } = location.state; - const [currQuestion, setCurrQuestion] = useState(0); +export default function Lesson() +{ + const location = useLocation(); + const navigate = useNavigate(); + const {questions, home} = location.state; + const [currQuestion, setCurrQuestion] = useState(0); - const handleNavigateBack = () => { - navigate(-1); - }; + console.log(questions) - const handleQuestionSubmit = () => { - if (currQuestion < questions.length - 1) { - setCurrQuestion(currQuestion + 1); - } else { - navigate(home); - } + const handleNavigateBack = () => + { + navigate(-1); + }; + + const onSubmit = () => + { + if (currQuestion < questions.length - 1) + setCurrQuestion(currQuestion + 1); + else navigate(home); } - const renderQuestion = (currIndex: number) => { - const question = questions[currQuestion]; - switch (question.type) { - case 'written': - switch (question.exercise) { - case 'questions': - return ; - case 'vocabulary': - return ; - default: - return null; - } - case 'verbal': - switch (question.exercise) { - case 'questions': - return ; - case 'pronunciation': - return ; - default: - return null; - } - default: - return null; - } - }; - - return ( -
- -
- {renderQuestion(currQuestion)} -
+ const renderQuestion = (currIndex: number) => + { + const chapter = 'Ordering food'; + const question: Question = questions[currQuestion]; + switch (question.type) + { + case 'written-question': + return ; + case 'written-vocabulary': + return ; + case 'verbal-question': + return ; + case 'verbal-pronunciation': + return ; + case 'video': + return ; + default: + return null; + } + }; + + return ( +
+ +
+
+ {renderQuestion(currQuestion)}
- ) -} \ No newline at end of file +
+
+ ) +} diff --git a/frontend/src/pages/Review.tsx b/frontend/src/pages/Review.tsx index b03cb60..962f35a 100644 --- a/frontend/src/pages/Review.tsx +++ b/frontend/src/pages/Review.tsx @@ -1,142 +1,28 @@ import { useLocation, useNavigate } from "react-router-dom" import NavBar from "../components/NavBar" import { getLanguage, getUsername } from "../logic/sdk"; +import {_Question, WrittenQuestion} from "../logic/CourseData"; export default function Review() { const navigate = useNavigate(); const location = useLocation(); - const writtenReview = ["Questions", "Vocabulary"]; - const verbalReview = ["Questions", "Pronunciation"]; - const language = getLanguage().name; + const writtenReview = ["Question", "Vocabulary"]; + const verbalReview = ["Question", "Pronunciation"]; const handleReviewLessonClick = (reviewType: string, lesson: string) => { - type WrittenQuestion = { question: string, wordBank: string[], expected: string, type: string, exercise: string }; - type VocabularyQuestion = { question: string, pronunciation: string, definition: string, example: string, type: string, exercise: string }; - type VerbalQuestion = { question: string, wordBank: string[], expected: string, type: string, exercise: string }; - if (language === "Japanese") { - if (reviewType === "written" && lesson === "questions") { - let reviewQuestions: WrittenQuestion[] = []; - reviewQuestions = [ - { - question: 'Translate this sentence: 新幹線で東京に行きます.', - wordBank: ['Explore', 'Train', 'Will', 'City', 'I', 'Go', 'Bullet', 'To', 'Tokyo', 'By', 'Mountain'], - expected: 'I will go to Tokyo by bullet train.', - type: reviewType, - exercise: lesson - }, - { - question: 'Translate this sentence: 空港で荷物を受け取ります.', - wordBank: ['Sunset', 'Will', 'Ocean', 'At', 'Adventure', 'Airport', 'The', 'I', 'Receive', 'Luggage'], - expected: 'I will receive luggage at the airport.', - type: reviewType, - exercise: lesson - }, - { - question: 'Translate this sentence: ホテルでチェックインします.', - wordBank: ['Culture', 'At', 'Jungle', 'Will', 'The', 'Check-In', 'Hotel', 'I', 'Historic'], - expected: 'I will check in at the hotel.', - type: reviewType, - exercise: lesson - } - ] - navigate('/lesson', { state: { questions: reviewQuestions } }); - } else if (reviewType === "written" && lesson === "vocabulary") { - let reviewQuestions: VocabularyQuestion[] = []; - reviewQuestions = [ - { - question: '飛行機', - pronunciation: 'ひこうき (Hikouki)', - definition: '飛行機 (Hikouki) is the Japanese word for airplane. It refers to a powered flying vehicle with fixed wings and a weight greater than that of the air it displaces.', - example: '飛行機で旅行します。 (I will travel by airplane.)', - type: 'written', - exercise: 'vocabulary', - }, - { - question: '観光', - pronunciation: 'かんこう (Kankou)', - definition: '観光 (Kankou) is the Japanese word for sightseeing. It refers to the activity of visiting places of interest in a particular location, typically as part of a vacation.', - example: '観光地を訪れます。 (I will visit tourist attractions.)', - type: 'written', - exercise: 'vocabulary', - }, - { - question: '予約', - pronunciation: 'よやく (Yoyaku)', - definition: '予約 (Yoyaku) is the Japanese word for reservation. It refers to an arrangement for a seat, room, etc. to be kept for a customer at a restaurant, hotel, or other place.', - example: 'ホテルを予約します。 (I will make a hotel reservation.)', - type: 'written', - exercise: 'vocabulary', - }, - ]; - navigate('/lesson', { state: { questions: reviewQuestions, home: location.pathname } }); - } else if (reviewType === "verbal" && lesson === "questions") { - let reviewQuestions: VerbalQuestion[] = []; - reviewQuestions = [ - { - question: '新幹線で東京に行きます.', - wordBank: ['新', '幹', '線', 'で', '東', '京', 'に', '行', 'き', 'ま', 'す'], - expected: '新幹線で東京に行きます.', - type: reviewType, - exercise: lesson - }, - { - question: '空港で荷物を受け取ります.', - wordBank: ['空', '港', 'で', '荷', '物', 'を', '受', 'け', '取', 'り', 'ます', '.'], - expected: '空港で荷物を受け取ります.', - type: reviewType, - exercise: lesson - }, - { - question: 'ホテルでチェックインします.', - wordBank: ['ホ', 'テ', 'ル', 'で', 'チ', 'ェ', 'ッ', 'ク', 'イ', 'ン', 'し', 'ま', 'す', '.'], - expected: 'ホテルでチェックインします.', - type: reviewType, - exercise: lesson - } - ]; - navigate('/lesson', { state: { questions: reviewQuestions, home: location.pathname } }); - } else if (reviewType === "verbal" && lesson === "pronunciation") { - let reviewQuestions: VerbalQuestion[] = []; - reviewQuestions = [ - { - question: '新幹線で東京に行きます.', - wordBank: ['新', '幹', '線', 'で', '東', '京', 'に', '行', 'き', 'ま', 'す'], - expected: '新幹線で東京に行きます.', - type: reviewType, - exercise: lesson - }, - { - question: '空港で荷物を受け取ります.', - wordBank: ['空', '港', 'で', '荷', '物', 'を', '受', 'け', '取', 'り', 'ます', '.'], - expected: '空港で荷物を受け取ります.', - type: reviewType, - exercise: lesson - }, - { - question: 'ホテルでチェックインします.', - wordBank: ['ホ', 'テ', 'ル', 'で', 'チ', 'ェ', 'ッ', 'ク', 'イ', 'ン', 'し', 'ま', 'す', '.'], - expected: 'ホテルでチェックインします.', - type: reviewType, - exercise: lesson - } - ]; - navigate('/lesson', { state: { questions: reviewQuestions, home: location.pathname } }); - } - } + const lessons: _Question[] = getLanguage().data.flatMap(chapter => chapter.steps).flatMap(step => step.questions); + navigate('/lesson', { state: { questions: lessons.filter(it => it.type == `${reviewType}-${lesson}`), home: location.pathname } }); } return ( -
+

Review Page

Written

{writtenReview.map(lesson => ( - ))} @@ -144,11 +30,8 @@ export default function Review() {

Verbal/Listening

{verbalReview.map(lesson => ( - ))}