[+] Complete course
This commit is contained in:
Executable
BIN
Binary file not shown.
Binary file not shown.
@@ -1,20 +1,18 @@
|
|||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
import { useState, useEffect, useRef, useCallback } from '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 ClipLoader from "react-spinners/ClipLoader";
|
||||||
|
import {VerbalPronunciation} from "../logic/CourseData";
|
||||||
|
|
||||||
interface VerbalPronunciationProps {
|
interface VerbalPronunciationProps {
|
||||||
question: string;
|
q: VerbalPronunciation;
|
||||||
expected: string;
|
|
||||||
chapter: string;
|
chapter: string;
|
||||||
language: string;
|
onSubmit: Function;
|
||||||
onQuestionSubmit: Function;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function VerbalPronunciationExercise({question, expected, chapter, language, onQuestionSubmit}: VerbalPronunciationProps) {
|
export default function VerbalPronunciationExercise({q, chapter, onSubmit}: VerbalPronunciationProps) {
|
||||||
const location = useLocation();
|
const language = getLanguage().name;
|
||||||
const navigate = useNavigate();
|
|
||||||
const [answered, setAnswered] = useState(false);
|
const [answered, setAnswered] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [correct, setCorrect] = useState("");
|
const [correct, setCorrect] = useState("");
|
||||||
@@ -23,11 +21,10 @@ export default function VerbalPronunciationExercise({question, expected, chapter
|
|||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
if (answered) {
|
if (answered) {
|
||||||
setAnswered(false);
|
setAnswered(false);
|
||||||
onQuestionSubmit();
|
onSubmit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Message = { text: string, sender: string };
|
|
||||||
const [isRecording, setIsRecording] = useState(false);
|
const [isRecording, setIsRecording] = useState(false);
|
||||||
|
|
||||||
let chunks = [] as any;
|
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 audioFile = new File([blob], "audio.wav", { type: 'audio/wav' });
|
||||||
|
|
||||||
const text = await speechToText(audioFile);
|
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);
|
setAnswered(true);
|
||||||
setCorrect(aiMark.correct);
|
setCorrect(aiMark.correct);
|
||||||
setReason(aiMark.reason);
|
setReason(aiMark.reason);
|
||||||
@@ -102,10 +99,10 @@ export default function VerbalPronunciationExercise({question, expected, chapter
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="v-layout p-10 flex justify-center">
|
<div className="v-layout page-pad flex justify-center">
|
||||||
<h1 className="text-center">Say the following</h1>
|
<h1 className="text-center">Say the following</h1>
|
||||||
<div className='round box h-min no-shadow relative min-h-[60px] flex items-center justify-center'>
|
<div className='round box h-min no-shadow relative min-h-[60px] flex items-center justify-center'>
|
||||||
{question}
|
{q.question}
|
||||||
</div>
|
</div>
|
||||||
{ResponseSection(correct, reason)}
|
{ResponseSection(correct, reason)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,26 +1,23 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { getAIMarking } from '../logic/sdk';
|
import {getAIMarking, getLanguage} from '../logic/sdk';
|
||||||
import ClipLoader from "react-spinners/ClipLoader";
|
import ClipLoader from "react-spinners/ClipLoader";
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
|
import {VerbalQuestion} from "../logic/CourseData";
|
||||||
|
|
||||||
interface VerbalQuestionProps {
|
interface VerbalQuestionProps {
|
||||||
question: string;
|
q: VerbalQuestion
|
||||||
wordBank: string[];
|
|
||||||
expected: string;
|
|
||||||
chapter: string;
|
chapter: string;
|
||||||
language: string;
|
onSubmit: Function;
|
||||||
onQuestionSubmit: 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<string[]>([]);
|
const [selectedWords, setSelectedWords] = useState<string[]>([]);
|
||||||
const [remainingWords, setRemainingWords] = useState(wordBank);
|
const [remainingWords, setRemainingWords] = useState(q.wordBank);
|
||||||
const lastPunctuation = question[question.length - 1];
|
|
||||||
const [answered, setAnswered] = useState(false);
|
const [answered, setAnswered] = useState(false);
|
||||||
const [correct, setCorrect] = useState("");
|
const [correct, setCorrect] = useState("");
|
||||||
const [reason, setReason] = useState("");
|
const [reason, setReason] = useState("");
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [isListening, setListening] = useState(false);
|
|
||||||
|
|
||||||
const handleWordBankClick = (word: string) => {
|
const handleWordBankClick = (word: string) => {
|
||||||
setSelectedWords([...selectedWords, word]);
|
setSelectedWords([...selectedWords, word]);
|
||||||
@@ -33,16 +30,16 @@ export default function VerbalQuestionsExercise({question, wordBank, expected, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
const userAnswer = selectedWords.join(" ") + lastPunctuation;
|
const userAnswer = selectedWords.join("");
|
||||||
if (answered) {
|
if (answered) {
|
||||||
setAnswered(false);
|
setAnswered(false);
|
||||||
onQuestionSubmit();
|
onSubmit();
|
||||||
} else {
|
} else {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
getAIMarking(
|
getAIMarking(
|
||||||
question,
|
q.question,
|
||||||
userAnswer.toLowerCase(),
|
userAnswer.toLowerCase(),
|
||||||
expected.toLowerCase(),
|
q.expected.toLowerCase(),
|
||||||
chapter,
|
chapter,
|
||||||
language
|
language
|
||||||
).then((res) => {
|
).then((res) => {
|
||||||
@@ -58,7 +55,7 @@ export default function VerbalQuestionsExercise({question, wordBank, expected, c
|
|||||||
const ResponseSection = (correct: string | null, reason: string | null) => {
|
const ResponseSection = (correct: string | null, reason: string | null) => {
|
||||||
if (!answered) {
|
if (!answered) {
|
||||||
return (
|
return (
|
||||||
<div className='w-full h-36'>
|
<div className='w-full h-32'>
|
||||||
<div className=' flex-row flex-wrap w-full h-full'>
|
<div className=' flex-row flex-wrap w-full h-full'>
|
||||||
{remainingWords.map((word, index) => (
|
{remainingWords.map((word, index) => (
|
||||||
<span
|
<span
|
||||||
@@ -91,18 +88,11 @@ export default function VerbalQuestionsExercise({question, wordBank, expected, c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleListenToQuestion = () => {
|
|
||||||
// TODO: Play static file that holds the audio for the current
|
|
||||||
console.log("Heard you");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='v-layout space-y-8 items-center w-full'>
|
<div className='v-layout space-y-8 items-center w-full'>
|
||||||
<h1>What do you hear?</h1>
|
<h2>What do you hear?</h2>
|
||||||
<div className='round box h-min no-shadow relative min-h-[60px] flex items-center justify-center mx-5'>
|
<audio src={q.url} controls className='audio-player'></audio>
|
||||||
<Icon icon="mdi:volume-high" className="volume-high h-16 w-16" onClick={handleListenToQuestion} />
|
<div className='flex-row flex-wrap border-b-4 w-full h-32'>
|
||||||
</div>
|
|
||||||
<div className='flex-row flex-wrap border-b-4 w-full h-36'>
|
|
||||||
{selectedWords.map((word, index) => (
|
{selectedWords.map((word, index) => (
|
||||||
<span
|
<span
|
||||||
key={index}
|
key={index}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { getAIMarking, getLanguage } from '../logic/sdk';
|
||||||
|
import ClipLoader from "react-spinners/ClipLoader";
|
||||||
|
import { VideoQuestion } from "../logic/CourseData";
|
||||||
|
|
||||||
|
interface VideoQuestionProps {
|
||||||
|
q: VideoQuestion;
|
||||||
|
chapter: string;
|
||||||
|
onSubmit: Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function VideoExercise({q, chapter, onSubmit}: VideoQuestionProps) {
|
||||||
|
const language = getLanguage();
|
||||||
|
const [userAnswer, setUserAnswer] = useState("");
|
||||||
|
const [answered, setAnswered] = useState(false);
|
||||||
|
const [correct, setCorrect] = useState("");
|
||||||
|
const [reason, setReason] = useState("");
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
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 (
|
||||||
|
<div className='v-layout space-y-8 items-center w-full'>
|
||||||
|
<div className="box">
|
||||||
|
{q.question}
|
||||||
|
</div>
|
||||||
|
<video src={q.clipUrl} controls className='video-player'></video>
|
||||||
|
<div className='flex-col w-full'>
|
||||||
|
{!answered ?
|
||||||
|
<div className="v-layout">
|
||||||
|
<input type="text" value={userAnswer} onChange={handleChange}
|
||||||
|
placeholder="Type your answer here"/>
|
||||||
|
<button className='green' onClick={() => handleSubmit()}>
|
||||||
|
{loading ? <ClipLoader color="white" loading={loading} /> : "Submit"}
|
||||||
|
</button>
|
||||||
|
</div> :
|
||||||
|
<div className='flex-col w-full'>
|
||||||
|
<h3>{correct}</h3>
|
||||||
|
<p>{reason}</p>
|
||||||
|
<button className='green w-full' onClick={() => handleSubmit()}>Continue</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,20 +1,18 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { getAIMarking } from '../logic/sdk';
|
import {getAIMarking, getLanguage} from '../logic/sdk';
|
||||||
import ClipLoader from "react-spinners/ClipLoader";
|
import ClipLoader from "react-spinners/ClipLoader";
|
||||||
|
import {WrittenQuestion} from "../logic/CourseData";
|
||||||
|
|
||||||
interface WrittenQuestionProps {
|
interface WrittenQuestionProps {
|
||||||
question: string;
|
q: WrittenQuestion
|
||||||
wordBank: string[];
|
|
||||||
expected: string;
|
|
||||||
chapter: string;
|
chapter: string;
|
||||||
language: string;
|
onSubmit: Function;
|
||||||
onQuestionSubmit: 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<string[]>([]);
|
const [selectedWords, setSelectedWords] = useState<string[]>([]);
|
||||||
const [remainingWords, setRemainingWords] = useState(wordBank);
|
const [remainingWords, setRemainingWords] = useState(q.wordBank);
|
||||||
const lastPunctuation = question[question.length - 1];
|
|
||||||
const [answered, setAnswered] = useState(false);
|
const [answered, setAnswered] = useState(false);
|
||||||
const [correct, setCorrect] = useState("");
|
const [correct, setCorrect] = useState("");
|
||||||
const [reason, setReason] = useState("");
|
const [reason, setReason] = useState("");
|
||||||
@@ -31,18 +29,22 @@ export default function WrittenQuestionExercise({question, wordBank, expected, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmit = () => {
|
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) {
|
if (answered) {
|
||||||
setAnswered(false);
|
setAnswered(false);
|
||||||
onQuestionSubmit();
|
onSubmit();
|
||||||
} else {
|
} else {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
getAIMarking(
|
getAIMarking(
|
||||||
question,
|
q.question,
|
||||||
userAnswer.toLowerCase(),
|
userAnswer.toLowerCase(),
|
||||||
expected.toLowerCase(),
|
q.expected.toLowerCase(),
|
||||||
chapter,
|
chapter,
|
||||||
language
|
language.name
|
||||||
).then((res) => {
|
).then((res) => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setAnswered(true);
|
setAnswered(true);
|
||||||
@@ -50,7 +52,6 @@ export default function WrittenQuestionExercise({question, wordBank, expected, c
|
|||||||
setReason(res.reason);
|
setReason(res.reason);
|
||||||
}).catch((e) => console.error(e));
|
}).catch((e) => console.error(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ResponseSection = (correct: string | null, reason: string | null) => {
|
const ResponseSection = (correct: string | null, reason: string | null) => {
|
||||||
@@ -62,12 +63,12 @@ export default function WrittenQuestionExercise({question, wordBank, expected, c
|
|||||||
<span
|
<span
|
||||||
key={index}
|
key={index}
|
||||||
className="border-gray-300 border-2 m-1 p-1 px-3 rounded-xl inline-block cursor-pointer"
|
className="border-gray-300 border-2 m-1 p-1 px-3 rounded-xl inline-block cursor-pointer"
|
||||||
onClick={(event) => handleWordBankClick(word)}>
|
onClick={() => handleWordBankClick(word)}>
|
||||||
{word}
|
{word}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<button className='green' onClick={(e) => handleSubmit()}>
|
<button className='green' onClick={() => handleSubmit()}>
|
||||||
{loading ? <ClipLoader
|
{loading ? <ClipLoader
|
||||||
color="white"
|
color="white"
|
||||||
loading={loading}
|
loading={loading}
|
||||||
@@ -83,7 +84,7 @@ export default function WrittenQuestionExercise({question, wordBank, expected, c
|
|||||||
<div className=' flex-row flex-wrap border-b-4 w-full h-36'>
|
<div className=' flex-row flex-wrap border-b-4 w-full h-36'>
|
||||||
<h3>{correct}</h3>
|
<h3>{correct}</h3>
|
||||||
<p>{reason}</p>
|
<p>{reason}</p>
|
||||||
<button className='green w-full' onClick={(e) => handleSubmit()}>{!answered ? "Submit" : "Continue"}</button>
|
<button className='green w-full' onClick={() => handleSubmit()}>{!answered ? "Submit" : "Continue"}</button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -92,14 +93,14 @@ export default function WrittenQuestionExercise({question, wordBank, expected, c
|
|||||||
return (
|
return (
|
||||||
<div className='v-layout space-y-8 items-center w-full'>
|
<div className='v-layout space-y-8 items-center w-full'>
|
||||||
<div className='round box h-min no-shadow relative min-h-[60px] flex items-center justify-center mx-5'>
|
<div className='round box h-min no-shadow relative min-h-[60px] flex items-center justify-center mx-5'>
|
||||||
{question}
|
{q.question}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex-row flex-wrap border-b-4 w-full h-36'>
|
<div className='flex-row flex-wrap border-b-4 w-full h-36'>
|
||||||
{selectedWords.map((word, index) => (
|
{selectedWords.map((word, index) => (
|
||||||
<span
|
<span
|
||||||
key={index}
|
key={index}
|
||||||
className="border-gray-300 border-b-2 border-2 m-1 p-1 px-3 rounded-xl inline-block cursor-pointer"
|
className="border-gray-300 border-b-2 border-2 m-1 p-1 px-3 rounded-xl inline-block cursor-pointer"
|
||||||
onClick={(e) => handleSelectedWordClick(word)}>
|
onClick={() => handleSelectedWordClick(word)}>
|
||||||
{word}
|
{word}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,37 +1,34 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import {VocabularyQuestion} from "../logic/CourseData";
|
||||||
|
|
||||||
interface WrittenVocabularyProps {
|
interface WrittenVocabularyProps {
|
||||||
question: string;
|
q: VocabularyQuestion
|
||||||
pronunciation: string;
|
onSubmit: Function;
|
||||||
definition: string;
|
|
||||||
example: string;
|
|
||||||
onQuestionSubmit: Function;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function WrittenQuestionExercise({question, pronunciation, definition, example, onQuestionSubmit}: WrittenVocabularyProps) {
|
export default function WrittenQuestionExercise({q, onSubmit}: WrittenVocabularyProps) {
|
||||||
const [answered, setAnswered] = useState(false);
|
const [answered, setAnswered] = useState(false);
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
if (answered) {
|
if (answered) {
|
||||||
setAnswered(false);
|
setAnswered(false);
|
||||||
onQuestionSubmit();
|
onSubmit();
|
||||||
} else {
|
} else {
|
||||||
setAnswered(true);
|
setAnswered(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='v-layout space-y-8 items-center w-full'>
|
<div className='v-layout space-y-8 items-center w-full'>
|
||||||
<div className='round box h-min no-shadow relative min-h-[60px] flex items-center justify-center mx-5'>
|
<div className='round box h-min no-shadow relative min-h-[60px] flex items-center justify-center mx-5'>
|
||||||
{question}
|
{q.question}
|
||||||
</div>
|
</div>
|
||||||
<div className=' flex-col flex-wrap w-full'>
|
<div className=' flex-col flex-wrap w-full'>
|
||||||
{answered ?
|
{answered ?
|
||||||
<div className='flex-col w-full'>
|
<div className='flex-col w-full'>
|
||||||
<h3>{pronunciation}</h3>
|
<h3>{q.pronunciation}</h3>
|
||||||
<p>{definition}</p>
|
<p>{q.description}</p>
|
||||||
<p>{example}</p>
|
<p>{q.example}</p>
|
||||||
<div className='flex-row'>
|
<div className='flex-row'>
|
||||||
<button className='green my-4' onClick={(e) => handleSubmit()}>I got it!</button>
|
<button className='green my-4' onClick={(e) => handleSubmit()}>I got it!</button>
|
||||||
<button className='red' onClick={(e) => handleSubmit()}>I forgot</button>
|
<button className='red' onClick={(e) => handleSubmit()}>I forgot</button>
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ h2
|
|||||||
.page-pad
|
.page-pad
|
||||||
padding: 2em 1em
|
padding: 2em 1em
|
||||||
height: 100%
|
height: 100%
|
||||||
|
@extend .non-center
|
||||||
|
|
||||||
.navbar
|
.navbar
|
||||||
position: fixed
|
position: fixed
|
||||||
|
|||||||
@@ -1,27 +1,57 @@
|
|||||||
|
|
||||||
|
|
||||||
export interface Chapter
|
export interface Chapter
|
||||||
{
|
{
|
||||||
name: string;
|
name: string
|
||||||
steps: Step[];
|
steps: Step[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Step
|
export interface Step
|
||||||
{
|
{
|
||||||
questions: Question[];
|
questions: Question[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Question
|
export interface _Question
|
||||||
{
|
{
|
||||||
question: string;
|
question: string
|
||||||
wordBank?: string[];
|
type: string
|
||||||
expected?: string;
|
}
|
||||||
type: string;
|
export type Question = WrittenQuestion | VocabularyQuestion | VerbalQuestion | VerbalPronunciation | VideoQuestion
|
||||||
pronunciation?: string;
|
|
||||||
description?: string;
|
export interface WrittenQuestion extends _Question
|
||||||
example?: string;
|
{
|
||||||
translation?: string;
|
wordBank: string[]
|
||||||
clipUrl?: 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[] = [
|
export const chapters_jp: Chapter[] = [
|
||||||
@@ -29,6 +59,14 @@ export const chapters_jp: Chapter[] = [
|
|||||||
name: 'Order food',
|
name: 'Order food',
|
||||||
steps: [{
|
steps: [{
|
||||||
questions: [
|
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: すしをください',
|
question: 'Translate this sentence: すしをください',
|
||||||
wordBank: ['I', 'sushi', 'cookies', 'want', 'please', 'give', 'rice', 'some', 'yesterday'],
|
wordBank: ['I', 'sushi', 'cookies', 'want', 'please', 'give', 'rice', 'some', 'yesterday'],
|
||||||
@@ -43,60 +81,59 @@ export const chapters_jp: Chapter[] = [
|
|||||||
type: 'written-vocabulary',
|
type: 'written-vocabulary',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
question: 'Transcribe this sentence',
|
question: 'What do you hear?',
|
||||||
wordBank: ['刺身', 'ケーキ', 'の', 'は', 'おいしい', 'おもい', 'すし', '中', 'です'],
|
wordBank: ['刺身', 'ケーキ', 'の', 'は', 'おいしい', 'おもい', 'すし', '中', 'です'],
|
||||||
expected: 'ケーキはおいしいです',
|
expected: 'ケーキはおいしいです',
|
||||||
translation: 'Cake is delicious',
|
translation: 'Cake is delicious',
|
||||||
|
url: window.location.origin + '/audio/jp_1_1_3.mp3',
|
||||||
type: 'verbal-question',
|
type: 'verbal-question',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
question: 'すしおたくさんあります',
|
question: 'Please say: すしをたくさんあります',
|
||||||
translation: 'There is a lot of sushi',
|
translation: 'There is a lot of sushi',
|
||||||
type: 'verbal-pronunciation',
|
type: 'verbal-pronunciation',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
question: 'What food does Yui like?',
|
|
||||||
clipUrl: "https://fdjsakfjs.com",
|
|
||||||
expected: 'ケーキ',
|
|
||||||
type: 'video',
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
questions: [
|
questions: [
|
||||||
{
|
{
|
||||||
question: 'Translate this sentence: 水をください',
|
// question: "Translate this sentence: Water please",
|
||||||
wordBank: ['I', 'sushi', 'water', 'want', 'please', 'give', 'rice', 'some', 'yesterday'],
|
// wordBank: ['水', 'を', 'ください', 'おいしい', 'おもい', 'すし', '中', 'です'],
|
||||||
expected: 'Water please / I want water',
|
question: 'Translate this sentence: 水をください',
|
||||||
type: "written-question"
|
wordBank: ['I', 'sushi', 'cookies', 'want', 'please', 'give', 'rice', 'some', 'yesterday'],
|
||||||
},
|
// expected: '水をください',
|
||||||
{
|
expected: 'Water please',
|
||||||
question: '団子',
|
type: "written-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)',
|
question: '団子',
|
||||||
type: 'written-vocabulary',
|
pronunciation: 'だんご (dango)',
|
||||||
},
|
description: 'Dango is a kind of mochi, Japanese dumpling and sweet made from mochiko (rice flour).',
|
||||||
{
|
example: '大きいな団子です。(It is a big dango)',
|
||||||
question: 'Transcribe this sentence',
|
type: 'written-vocabulary',
|
||||||
wordBank: ['刺身', '水', 'は', 'おいしい', 'おもい', 'すし', '中', 'です'],
|
},
|
||||||
expected: '水です',
|
{
|
||||||
translation: 'It is water',
|
question: 'What do you hear?',
|
||||||
type: 'verbal-question',
|
wordBank: ['刺身', '水', 'は', 'おいしい', 'おもい', 'すし', '中', 'です'],
|
||||||
},
|
expected: '水です',
|
||||||
{
|
translation: 'It is water',
|
||||||
question: '刺身おください',
|
url: window.location.origin + '/audio/jp_1_2_3.mp3',
|
||||||
translation: 'Please give me sashimi',
|
type: 'verbal-question',
|
||||||
type: 'verbal-pronunciation',
|
},
|
||||||
},
|
{
|
||||||
{
|
question: 'Please say: 刺身おください',
|
||||||
question: 'What is the following song about?',
|
translation: 'Please give me sashimi',
|
||||||
clipUrl: "https://www.youtube.com/watch?v=qfgyKPQO9g8",
|
type: 'verbal-pronunciation',
|
||||||
description: "This is the song 'Dango Daikazoku' from the anime 'Clannad'. It is about a family of dango.",
|
},
|
||||||
expected: '団子',
|
{
|
||||||
type: 'video',
|
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',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import {Icon} from "@iconify/react";
|
|||||||
import {getLanguage, isLoggedIn, isStepCompleted} from "../logic/sdk";
|
import {getLanguage, isLoggedIn, isStepCompleted} from "../logic/sdk";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import "./Course.sass"
|
import "./Course.sass"
|
||||||
|
import {Step} from "../logic/CourseData";
|
||||||
|
import {useLocation, useNavigate} from "react-router-dom";
|
||||||
|
|
||||||
function CourseButton(props: {state: 'active' | 'locked' | 'completed', index: number})
|
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
|
// Calculate the horizontal translation
|
||||||
const translateXValue = amplitude * Math.sin(props.index * frequency);
|
const translateXValue = amplitude * Math.sin(props.index * frequency);
|
||||||
|
|
||||||
return (
|
return <div className={cs} style={{transform: `translateX(${translateXValue}px)`}}>
|
||||||
<div className={cs} style={{transform: `translateX(${translateXValue}px)`}}>
|
<Icon icon={icon}/>
|
||||||
<Icon icon={icon}/>
|
</div>
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default function Course()
|
export default function Course()
|
||||||
{
|
{
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
if (!isLoggedIn())
|
if (!isLoggedIn())
|
||||||
{
|
{
|
||||||
window.location.href = '/login';
|
window.location.href = '/login';
|
||||||
@@ -36,6 +39,11 @@ export default function Course()
|
|||||||
// Get language
|
// Get language
|
||||||
const lang = getLanguage();
|
const lang = getLanguage();
|
||||||
|
|
||||||
|
function click(step: Step)
|
||||||
|
{
|
||||||
|
navigate('/lesson', {state: {questions: step.questions, home: location.pathname}})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="v-layout page-pad non-center">
|
<div className="v-layout page-pad non-center">
|
||||||
<div className="flex items-center justify-between font-bold">
|
<div className="flex items-center justify-between font-bold">
|
||||||
@@ -46,20 +54,21 @@ export default function Course()
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{lang.data.map((chapter, i) => <>
|
{lang.data.map((chapter, i) => <div key={i}>
|
||||||
<div className="box green">
|
<div className="box green mb-10">
|
||||||
<div>Chapter 1, Section 1</div>
|
<div>Chapter 1, Section 1</div>
|
||||||
<div className="font-bold">{chapter.name}</div>
|
<div className="font-bold">{chapter.name}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="v-layout non-center items-center gap2">
|
<div className="v-layout non-center items-center gap2">
|
||||||
{chapter.steps.map((step, i) =>
|
{chapter.steps.map((step, i) => <div key={i} onClick={() => click(step)}>
|
||||||
<CourseButton state={isStepCompleted(chapter.name, i) ? 'completed' : i == 0 || isStepCompleted(chapter.name, i - 1) ? 'active' : 'locked'}
|
<CourseButton state={isStepCompleted(chapter.name, i) ? 'completed' : i == 0 || isStepCompleted(chapter.name, i - 1) ? 'active' : 'locked'}
|
||||||
index={i} key={i}/>)}
|
index={i}/></div>)}
|
||||||
|
{[...Array(5)].map((_, i) => <CourseButton state={'locked'} index={i + chapter.steps.length} key={i}/>)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NavBar/>
|
<NavBar/>
|
||||||
</>)}
|
</div>)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,91 +1,63 @@
|
|||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import {useLocation, useNavigate} from 'react-router-dom';
|
||||||
import { useState } from 'react';
|
import React, {useState} from 'react';
|
||||||
import WrittenQuestionExercise from "../components/WrittenQuestionExercise"
|
import WrittenQuestionExercise from "../components/WrittenQuestionExercise"
|
||||||
import WrittenVocabularyExercise from "../components/WrittenVocabularyExercise"
|
import WrittenVocabularyExercise from "../components/WrittenVocabularyExercise"
|
||||||
import VerbalQuestionsExercise from "../components/VerbalQuestionsExercise"
|
import VerbalQuestionsExercise from "../components/VerbalQuestionsExercise"
|
||||||
import Progress from '../components/Progress';
|
import Progress from '../components/Progress';
|
||||||
import VerbalPronunciationExercise from '../components/VerbalPronunciationExercise';
|
import VerbalPronunciationExercise from '../components/VerbalPronunciationExercise';
|
||||||
|
import {_Question, chapters_jp, Question} from "../logic/CourseData";
|
||||||
|
import VideoExercise from "../components/VideoExercise";
|
||||||
|
|
||||||
export default function Lesson() {
|
export default function Lesson()
|
||||||
const location = useLocation();
|
{
|
||||||
const navigate = useNavigate();
|
const location = useLocation();
|
||||||
const { questions, home } = location.state;
|
const navigate = useNavigate();
|
||||||
const [currQuestion, setCurrQuestion] = useState<number>(0);
|
const {questions, home} = location.state;
|
||||||
|
const [currQuestion, setCurrQuestion] = useState<number>(0);
|
||||||
|
|
||||||
const handleNavigateBack = () => {
|
console.log(questions)
|
||||||
navigate(-1);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleQuestionSubmit = () => {
|
const handleNavigateBack = () =>
|
||||||
if (currQuestion < questions.length - 1) {
|
{
|
||||||
setCurrQuestion(currQuestion + 1);
|
navigate(-1);
|
||||||
} else {
|
};
|
||||||
navigate(home);
|
|
||||||
}
|
const onSubmit = () =>
|
||||||
|
{
|
||||||
|
if (currQuestion < questions.length - 1)
|
||||||
|
setCurrQuestion(currQuestion + 1);
|
||||||
|
else navigate(home);
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderQuestion = (currIndex: number) => {
|
const renderQuestion = (currIndex: number) =>
|
||||||
const question = questions[currQuestion];
|
{
|
||||||
switch (question.type) {
|
const chapter = 'Ordering food';
|
||||||
case 'written':
|
const question: Question = questions[currQuestion];
|
||||||
switch (question.exercise) {
|
switch (question.type)
|
||||||
case 'questions':
|
{
|
||||||
return <WrittenQuestionExercise
|
case 'written-question':
|
||||||
key={currQuestion}
|
return <WrittenQuestionExercise q={question} chapter={chapter} onSubmit={onSubmit}/>;
|
||||||
question={question.question}
|
case 'written-vocabulary':
|
||||||
wordBank={question.wordBank}
|
return <WrittenVocabularyExercise q={question} onSubmit={onSubmit}/>;
|
||||||
expected={question.expected}
|
case 'verbal-question':
|
||||||
chapter={"Travel"}
|
return <VerbalQuestionsExercise q={question} chapter={chapter} onSubmit={onSubmit}/>;
|
||||||
language={"Japanese"}
|
case 'verbal-pronunciation':
|
||||||
onQuestionSubmit={handleQuestionSubmit}
|
return <VerbalPronunciationExercise q={question} chapter={chapter} onSubmit={onSubmit}/>;
|
||||||
/>;
|
case 'video':
|
||||||
case 'vocabulary':
|
return <VideoExercise q={question} chapter={chapter} onSubmit={onSubmit}/>;
|
||||||
return <WrittenVocabularyExercise
|
default:
|
||||||
key={currQuestion}
|
return null;
|
||||||
question={question.question}
|
}
|
||||||
pronunciation={question.pronunciation}
|
};
|
||||||
definition={question.definition}
|
|
||||||
example={question.example}
|
return (
|
||||||
onQuestionSubmit={handleQuestionSubmit}
|
<div className="v-layout page-pad non-center">
|
||||||
/>;
|
<Progress percent={currQuestion / questions.length * 100} back={handleNavigateBack}/>
|
||||||
default:
|
<div className="p-5">
|
||||||
return null;
|
<div className="flex flex-col flex-1 mb-8 items-center">
|
||||||
}
|
{renderQuestion(currQuestion)}
|
||||||
case 'verbal':
|
|
||||||
switch (question.exercise) {
|
|
||||||
case 'questions':
|
|
||||||
return <VerbalQuestionsExercise
|
|
||||||
key={currQuestion}
|
|
||||||
question={question.question}
|
|
||||||
wordBank={question.wordBank}
|
|
||||||
expected={question.expected}
|
|
||||||
chapter={"Travel"}
|
|
||||||
language={"Japanese"}
|
|
||||||
onQuestionSubmit={handleQuestionSubmit}
|
|
||||||
/>;
|
|
||||||
case 'pronunciation':
|
|
||||||
return <VerbalPronunciationExercise
|
|
||||||
key={currQuestion}
|
|
||||||
question={question.question}
|
|
||||||
expected={question.expected}
|
|
||||||
chapter={"Travel"}
|
|
||||||
language={"Japanese"}
|
|
||||||
onQuestionSubmit={handleQuestionSubmit}
|
|
||||||
/>;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="v-layout p-10">
|
|
||||||
<Progress percent={currQuestion / questions.length * 100} back={handleNavigateBack}/>
|
|
||||||
<div className="flex flex-col flex-1 mb-8 items-center">
|
|
||||||
{renderQuestion(currQuestion)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
</div>
|
||||||
}
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
+10
-127
@@ -1,142 +1,28 @@
|
|||||||
import { useLocation, useNavigate } from "react-router-dom"
|
import { useLocation, useNavigate } from "react-router-dom"
|
||||||
import NavBar from "../components/NavBar"
|
import NavBar from "../components/NavBar"
|
||||||
import { getLanguage, getUsername } from "../logic/sdk";
|
import { getLanguage, getUsername } from "../logic/sdk";
|
||||||
|
import {_Question, WrittenQuestion} from "../logic/CourseData";
|
||||||
|
|
||||||
export default function Review() {
|
export default function Review() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
const writtenReview = ["Questions", "Vocabulary"];
|
const writtenReview = ["Question", "Vocabulary"];
|
||||||
const verbalReview = ["Questions", "Pronunciation"];
|
const verbalReview = ["Question", "Pronunciation"];
|
||||||
const language = getLanguage().name;
|
|
||||||
|
|
||||||
const handleReviewLessonClick = (reviewType: string, lesson: string) => {
|
const handleReviewLessonClick = (reviewType: string, lesson: string) => {
|
||||||
type WrittenQuestion = { question: string, wordBank: string[], expected: string, type: string, exercise: string };
|
const lessons: _Question[] = getLanguage().data.flatMap(chapter => chapter.steps).flatMap(step => step.questions);
|
||||||
type VocabularyQuestion = { question: string, pronunciation: string, definition: string, example: string, type: string, exercise: string };
|
navigate('/lesson', { state: { questions: lessons.filter(it => it.type == `${reviewType}-${lesson}`), home: location.pathname } });
|
||||||
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 } });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="layout-v p-10">
|
<div className="layout-v page-pad">
|
||||||
<h1>Review Page</h1>
|
<h1>Review Page</h1>
|
||||||
<h2>Written</h2>
|
<h2>Written</h2>
|
||||||
<div className="flex flex-col flex-1 mb-8 gap-3">
|
<div className="flex flex-col flex-1 mb-8 gap-3">
|
||||||
{writtenReview.map(lesson => (
|
{writtenReview.map(lesson => (
|
||||||
<button
|
<button className="white" key={lesson}
|
||||||
className="white"
|
onClick={() => handleReviewLessonClick("written", lesson.toLowerCase())}>
|
||||||
key={"written-" + lesson}
|
|
||||||
onClick={() => handleReviewLessonClick("written", lesson.toLowerCase())}
|
|
||||||
>
|
|
||||||
{lesson}
|
{lesson}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
@@ -144,11 +30,8 @@ export default function Review() {
|
|||||||
<h2>Verbal/Listening</h2>
|
<h2>Verbal/Listening</h2>
|
||||||
<div className="flex flex-col flex-1 gap-3">
|
<div className="flex flex-col flex-1 gap-3">
|
||||||
{verbalReview.map(lesson => (
|
{verbalReview.map(lesson => (
|
||||||
<button
|
<button className="white" key={lesson}
|
||||||
className="white"
|
onClick={() => handleReviewLessonClick("verbal", lesson.toLowerCase())}>
|
||||||
key={"verbal" + lesson}
|
|
||||||
onClick={() => handleReviewLessonClick("verbal", lesson.toLowerCase())}
|
|
||||||
>
|
|
||||||
{lesson}
|
{lesson}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user