diff --git a/002_source/ionic_mobile/src/RouteConfig.tsx b/002_source/ionic_mobile/src/RouteConfig.tsx index e60b472..eda18ef 100644 --- a/002_source/ionic_mobile/src/RouteConfig.tsx +++ b/002_source/ionic_mobile/src/RouteConfig.tsx @@ -30,8 +30,8 @@ import WordPage from './pages/Lesson/WordPage'; import ListeningPractice from './pages/ListeningPractice'; import PracticeFinish from './pages/ListeningPractice/Finish'; import PracticeResult from './pages/ListeningPractice/Result'; -import QuestionRoute from './pages/ListeningPractice/Route'; -import CorrectionRoute from './pages/ListeningPractice/Route/CorrectionRoute'; +import QuestionRoute from './pages/ListeningPractice/QuestionRoute'; +import CorrectionRoute from './pages/ListeningPractice/QuestionRoute/CorrectionRoute'; import MatchingFrenzyMatchFinished from './pages/MatchingFrenzy/Finished'; import MatchingFrenzyMatchRun from './pages/MatchingFrenzy/MatchRun'; import MatchingFrenzyMatchResult from './pages/MatchingFrenzy/Result'; @@ -45,6 +45,7 @@ import Setting from './pages/Setting/indx'; import Tab1 from './pages/Tab1'; import Tab2 from './pages/Tab2'; import Tab3 from './pages/Tab3'; +// import WordPageWithLayout from './pages/Lesson/WordPageWithLayout.del'; function RouteConfig() { return ( @@ -81,16 +82,20 @@ function RouteConfig() { + + {/* http://localhost:5173/listening_practice */} - {/* */} + + + @@ -104,6 +109,7 @@ function RouteConfig() { + {/* http://localhost:5173/matching_frenzy/r/0 */} @@ -126,16 +132,23 @@ function RouteConfig() { {/* TODO: change lesson_idx to lesson_type_idx, need to modify LessonWordPage as well */} - {/* http://localhost:5173/lesson_word_page/v/000000000000001/000000000000003/0 */} + {/* + http://localhost:5173/lesson_word_page/v/000000000000001/000000000000003/0 + layout: v + */} - {/* */} + + {/* + http://localhost:5173/lesson_word_page/c/000000000000001/000000000000003/0 + layout: c + */} diff --git a/002_source/ionic_mobile/src/_GUIDELINE.md b/002_source/ionic_mobile/src/_GUIDELINE.md new file mode 100644 index 0000000..4a3bfc5 --- /dev/null +++ b/002_source/ionic_mobile/src/_GUIDELINE.md @@ -0,0 +1,3 @@ +# GUIDELINE + +`/002_source/ionic_mobile/src/RouteConfig.tsx` contains the route of the application. diff --git a/002_source/ionic_mobile/src/constants.tsx b/002_source/ionic_mobile/src/constants.tsx index 2316ef3..7c05a56 100644 --- a/002_source/ionic_mobile/src/constants.tsx +++ b/002_source/ionic_mobile/src/constants.tsx @@ -76,6 +76,7 @@ const TEST = process.env.NODE_ENV === 'test'; const MY_FAVORITE = 'My Favorite'; // +const POCKETBASE_URL = import.meta.env.VITE_POCKETBASE_URL; export { // @@ -133,6 +134,8 @@ export { WRONG_ANSWER_MESSAGE, WRONG_ANS_BG_COLOR, // + POCKETBASE_URL, + // hide_setting, }; diff --git a/002_source/ionic_mobile/src/hooks/_GUIDELINES.md b/002_source/ionic_mobile/src/hooks/_GUIDELINES.md new file mode 100644 index 0000000..919d305 --- /dev/null +++ b/002_source/ionic_mobile/src/hooks/_GUIDELINES.md @@ -0,0 +1,6 @@ +# GUIDELINE + +- single file contains single function only +- please refer to the `tsx` files already exist in this directory for + - styling + - naming convention diff --git a/002_source/ionic_mobile/src/hooks/useListCategoriesByLessonId.tsx b/002_source/ionic_mobile/src/hooks/useListCategoriesByLessonId.tsx new file mode 100644 index 0000000..9d41d3a --- /dev/null +++ b/002_source/ionic_mobile/src/hooks/useListCategoriesByLessonId.tsx @@ -0,0 +1,20 @@ +import { usePocketBase } from './usePocketBase'; +import { useQuery } from '@tanstack/react-query'; +import { CategoryType } from '../types/CategoryTypes'; + +const useListCategoriesByLessonId = (lessonId: string) => { + const { pb } = usePocketBase(); + + return useQuery({ + queryKey: ['categories', 'byLessonType', lessonId], + queryFn: async () => { + return await pb.collection('LessonsCategories').getList(1, 9999, { + filter: `lesson_id = "${lessonId}"`, + sort: 'pos', + }); + }, + staleTime: 60 * 1000 * 5, // 5 minutes + }); +}; + +export default useListCategoriesByLessonId; diff --git a/002_source/ionic_mobile/src/hooks/useListMatchingFrenzyContent.tsx b/002_source/ionic_mobile/src/hooks/useListMatchingFrenzyContent.tsx new file mode 100644 index 0000000..6ad340b --- /dev/null +++ b/002_source/ionic_mobile/src/hooks/useListMatchingFrenzyContent.tsx @@ -0,0 +1,19 @@ +import { usePocketBase } from './usePocketBase'; +import { useQuery } from '@tanstack/react-query'; + +const useListMatchingFrenzyCategories = () => { + const { user, pb } = usePocketBase(); + + return useQuery({ + queryKey: ['useListMatchingFrenzyCategories'], + staleTime: 60 * 1000, + queryFn: async () => { + return await pb.collection('QuizMFCategories').getList(1, 9999, { + $autoCancel: false, + }); + }, + // enabled: !!user?.id, + }); +}; + +export default useListMatchingFrenzyCategories; diff --git a/002_source/ionic_mobile/src/hooks/useListQuizLPQuestionByLPCategoryId.tsx b/002_source/ionic_mobile/src/hooks/useListQuizLPQuestionByLPCategoryId.tsx new file mode 100644 index 0000000..9afe476 --- /dev/null +++ b/002_source/ionic_mobile/src/hooks/useListQuizLPQuestionByLPCategoryId.tsx @@ -0,0 +1,26 @@ +import { usePocketBase } from './usePocketBase.tsx'; +import { useQuery } from '@tanstack/react-query'; +// import { QuizLPQuestion } from '../types/QuizLPQuestion'; + +const useListQuizLPQuestionByLPCategoryId = (LPCategoryId: string) => { + const { user, pb } = usePocketBase(); + return useQuery({ + queryKey: ['useListQuizLPQuestionByLPCategoryId', 'feeds', 'all', user?.id || '', LPCategoryId], + staleTime: 60 * 1000, + queryFn: async ({ + queryKey, + }: { + queryKey: ['useListQuizLPQuestionByLPCategoryId', 'feeds', 'all', string | null, string]; + }) => { + console.log('calling useListQuizLPQuestionByLPCategoryId'); + return await pb.collection('QuizLPQuestions').getList(1, 9999, { + filter: `cat_id = "${LPCategoryId}"`, + sort: 'id', + $autoCancel: false, + }); + }, + // enabled: !!user?.id && !!LPCategoryId, + }); +}; + +export default useListQuizLPQuestionByLPCategoryId; diff --git a/002_source/ionic_mobile/src/hooks/useListQuizListeningPracticeContent.tsx b/002_source/ionic_mobile/src/hooks/useListQuizListeningPracticeContent.tsx new file mode 100644 index 0000000..77ced27 --- /dev/null +++ b/002_source/ionic_mobile/src/hooks/useListQuizListeningPracticeContent.tsx @@ -0,0 +1,37 @@ +import { usePocketBase } from './usePocketBase.tsx'; +import { useQuery } from '@tanstack/react-query'; +import IListeningPracticeCategory from '../interfaces/IListeningPracticeCategory.tsx'; + +const useListQuizListeningPracticeContent = () => { + const { user, pb } = usePocketBase(); + return useQuery({ + queryKey: [ + 'useListQuizListeningPracticeContent', + 'feeds', + 'all', + user?.id || '', + // + ], + staleTime: 60 * 1000, + queryFn: async ({ + queryKey, + }: { + queryKey: [ + 'useListQuizListeningPracticeContent', + 'feeds', + 'all', + string | null, + // + ]; + }) => { + console.log('calling useListQuizListeningPracticeContent'); + return await pb.collection('LessonsCategories').getList(1, 9999, { + sort: 'pos', + $autoCancel: false, + }); + }, + // enabled: !!user?.id, + }); +}; + +export default useListQuizListeningPracticeContent; diff --git a/002_source/ionic_mobile/src/hooks/usePocketBase.tsx b/002_source/ionic_mobile/src/hooks/usePocketBase.tsx index fe292ed..85a48ab 100644 --- a/002_source/ionic_mobile/src/hooks/usePocketBase.tsx +++ b/002_source/ionic_mobile/src/hooks/usePocketBase.tsx @@ -1,5 +1,6 @@ import { useCallback, useState, useEffect, createContext, useContext } from 'react'; import PocketBase, { AuthRecord } from 'pocketbase'; +import { POCKETBASE_URL } from '../constants'; type PocketBaseContextValue = { pb: PocketBase; user: AuthRecord; @@ -7,7 +8,7 @@ type PocketBaseContextValue = { }; const PocketBaseContext = createContext(null); -const POCKETBASE_URL = import.meta.env.VITE_POCKETBASE_URL; +// const POCKETBASE_URL = import.meta.env.VITE_POCKETBASE_URL; export const PocketBaseProvider = ({ children }: { children: any }) => { const [pb, _] = useState(new PocketBase(POCKETBASE_URL)); diff --git a/002_source/ionic_mobile/src/interfaces/IListeningPracticeCategory.tsx b/002_source/ionic_mobile/src/interfaces/IListeningPracticeCategory.tsx index d0ada38..0b78672 100644 --- a/002_source/ionic_mobile/src/interfaces/IListeningPracticeCategory.tsx +++ b/002_source/ionic_mobile/src/interfaces/IListeningPracticeCategory.tsx @@ -1,6 +1,7 @@ import IListeningPracticeQuestion from './IListeningPracticeQuestion'; interface IListeningPracticeCategory { + id: string; test_s: string; test_i: number; cat_info: string; diff --git a/002_source/ionic_mobile/src/pages/Lesson/ConnectivesPage/index.tsx b/002_source/ionic_mobile/src/pages/Lesson/ConnectivesPage/index.tsx index 5878dda..8e6bfd7 100644 --- a/002_source/ionic_mobile/src/pages/Lesson/ConnectivesPage/index.tsx +++ b/002_source/ionic_mobile/src/pages/Lesson/ConnectivesPage/index.tsx @@ -25,28 +25,30 @@ import { listLessonContent } from '../../../public_data/listLessonContent'; // import { listLessonContent } from '../../../public_data/index.ts.del'; import { AudioControls } from '../../LessonWord/AudioControls'; import { getFavLessonConnectivesLink, getLessonConnectivesLink } from '../getLessonConnectivesLink'; +import useGetVocabularyRoute from '../../../hooks/useGetVocabularyRoute'; const ConnectivesPage: React.FC = () => { - let [loading, setLoading] = useState(true); + const [loading, setLoading] = useState(true); + + const router = useIonRouter(); + const modal = useRef(null); + const { lesson_idx, cat_idx, word_idx } = useParams<{ lesson_idx: string; cat_idx: string; word_idx: string }>(); + + const [open_remove_modal, setOpenRemoveModal] = useState(false); + const [lesson_info, setLessonInfo] = useState(undefined); + let [all_words_length, setAllWordsLength] = useState(0); let [show_word, setShowWord] = useState({ word: '', word_c: '', sample_e: '', sample_c: '', sound: '' }); let [cat_name, setCatName] = useState(''); - let [fav_active, setFavActive] = useState(false); - let { playing, play: sound_play } = useGlobalAudioPlayer(); + const [fav_active, setFavActive] = useState(false); - const router = useIonRouter(); + const { playing, play: sound_play } = useGlobalAudioPlayer(); - const [lesson_info, setLessonInfo] = useState(undefined); - - let { lesson_idx, cat_idx, word_idx } = useParams<{ lesson_idx: string; cat_idx: string; word_idx: string }>(); let i_lesson_idx = parseInt(lesson_idx); let i_cat_idx = parseInt(cat_idx); let i_word_idx = parseInt(word_idx); const [isOpen, setIsOpen] = useState(false); - const [open_remove_modal, setOpenRemoveModal] = useState(false); - const modal = useRef(null); - // const lesson_vocab_address = `/lesson_word_page/v/${lesson_idx}/${cat_idx}/${word_idx}`; let [favorite_address, setFavoriteAddress] = useState(getFavLessonConnectivesLink(lesson_idx, cat_idx, word_idx)); @@ -69,6 +71,8 @@ const ConnectivesPage: React.FC = () => { const { myIonStoreAddFavoriteConnectives, myIonStoreRemoveFavoriteConnectives, myIonStoreFindInFavoriteConnectives } = useMyIonFavorite(); + let { status, data: tempResult } = useGetVocabularyRoute('000000000000002', '000000000000021'); + let [in_fav, setInFav] = useState(false); const isInFavorite = async (string_to_search: string) => { let result = await myIonStoreFindInFavoriteConnectives(string_to_search); @@ -113,9 +117,16 @@ const ConnectivesPage: React.FC = () => { })(); }, [i_lesson_idx, i_cat_idx, i_word_idx]); - if (lesson_info === undefined) return ; + // if (lesson_info === undefined) return ; + // if (loading) return ; - if (loading) return ; + useEffect(() => { + if (status === 'success') { + console.log({ tempResult }); + } + }, [status]); + + return <>{JSON.stringify({ tempResult }, null, 2)}; return ( <> @@ -128,7 +139,7 @@ const ConnectivesPage: React.FC = () => { fill="clear" color={'dark'} // href={`${LESSON_LINK}/a/${lesson_info.name}`} - onClick={() => router.push(`${LESSON_LINK}/a/${lesson_info.name}`)} + onClick={() => router.push(`${'LESSON_LINK'}/a/${'lesson_info.name'}`)} > @@ -144,10 +155,10 @@ const ConnectivesPage: React.FC = () => { }} >
- Part {i_word_idx + 1}/{all_words_length} + Part {i_word_idx + 1}/{'all_words_length'}
- {/* */} + {/* */}
{ justifyContent: 'center', fontSize: '1.3rem', }} - dangerouslySetInnerHTML={{ __html: cat_name }} + dangerouslySetInnerHTML={{ __html: 'cat_name' }} >
@@ -177,7 +188,11 @@ const ConnectivesPage: React.FC = () => { in_fav ? handleUserRemoveFavorite() : addToFavorite(favorite_address); }} > - +
@@ -189,7 +204,6 @@ const ConnectivesPage: React.FC = () => { justifyContent: 'space-between', gap: '1rem', width: '100%', - // }} > @@ -217,10 +231,18 @@ const ConnectivesPage: React.FC = () => { fontWeight: 'bold', }} > -
-
+
+
- {/* */} +
{
- {/* */} +
{ }} >
- sound_play()}> + sound_play()} + // + >
{/*
*/}
- {show_word.sample_e} + {'show_word.sample_e'}
@@ -260,7 +289,7 @@ const ConnectivesPage: React.FC = () => { style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', margin: '1rem 1rem 0 1rem' }} >
- {show_word.sample_c} + {'show_word.sample_c'}
diff --git a/002_source/ionic_mobile/src/pages/Lesson/LessonContainer/index.tsx b/002_source/ionic_mobile/src/pages/Lesson/LessonContainer/index.tsx index 1a9846e..5049b62 100644 --- a/002_source/ionic_mobile/src/pages/Lesson/LessonContainer/index.tsx +++ b/002_source/ionic_mobile/src/pages/Lesson/LessonContainer/index.tsx @@ -7,41 +7,35 @@ import { useMyIonStore } from '../../../contexts/MyIonStore'; import { listLessonCategories } from '../../../public_data/listLessonCategories'; import { getLessonConnectivesLink } from '../getLessonConnectivesLink'; import { getLessonVocabularyLink } from '../getLessonWordLink'; +import useListCategoriesByLessonId from '../../../hooks/useListCategoriesByLessonId'; +import { getImage } from '../../../utils/getImage'; let lessonLinkProxy = [getLessonVocabularyLink, getLessonConnectivesLink]; interface ContainerProps { - test_active_lesson_idx: number; lesson_type_id: string; } -const LessonContainer: React.FC = ({ - test_active_lesson_idx = 1, - lesson_type_id = '000000000000001', -}) => { +const LessonContainer: React.FC = ({ lesson_type_id: lesson_type_id = '000000000000001' }) => { const router = useIonRouter(); const [loading, setLoading] = useState(true); const { lesson_contents, setLessonContent } = useMyIonStore(); const [active_lesson_idx, setActiveLessonIdx] = useState(0); const [selected_content, setSelectedContent] = useState([]); + const result = useListCategoriesByLessonId(lesson_type_id); useEffect(() => { - listLessonCategories().then((cats: any) => { - console.log({ cats }); - setLessonContent(cats); - setActiveLessonIdx(test_active_lesson_idx); + if (result.status === 'success') { + setSelectedContent(result.data.items); setLoading(false); - }); - }, []); + } + }, [result]); - useEffect(() => { - if (loading) return; - console.log('active_category changed', active_lesson_idx); - let selected_category = lesson_contents[test_active_lesson_idx]; - setSelectedContent(selected_category['content']); - }, [active_lesson_idx, loading, test_active_lesson_idx]); + // return <>helloworld; if (loading) return ; + if (!selected_content) return ; + if (selected_content.length == 0) return <>loading; return ( <> @@ -61,16 +55,16 @@ const LessonContainer: React.FC = ({ style={{ width: '45vw', height: '45vw' }} fill="clear" onClick={() => { + // TODO: review the layout type `v` and `c` router.push(`/lesson_word_page/v/${lesson_type_id}/${content.id}/0`, undefined, 'replace'); }} >
= ({ }; export default LessonContainer; + +// useEffect(() => { +// listLessonCategories().then((cats: any) => { +// console.log({ cats }); +// setLessonContent(cats); +// setActiveLessonIdx(test_active_lesson_idx); +// setLoading(false); +// }); +// }, []); + +// useEffect(() => { +// if (loading) return; +// console.log('active_category changed', active_lesson_idx); +// let selected_category = lesson_contents[test_active_lesson_idx]; +// setSelectedContent(selected_category['content']); +// }, [active_lesson_idx, loading, test_active_lesson_idx]); diff --git a/002_source/ionic_mobile/src/pages/Lesson/WordPage/index.tsx b/002_source/ionic_mobile/src/pages/Lesson/WordPage/index.tsx index 5bede82..e2af3e6 100644 --- a/002_source/ionic_mobile/src/pages/Lesson/WordPage/index.tsx +++ b/002_source/ionic_mobile/src/pages/Lesson/WordPage/index.tsx @@ -36,8 +36,8 @@ import { UseQueryResult } from '@tanstack/react-query'; import { ListResult } from 'pocketbase'; import LessonsTypes from '../../../types/LessonsTypes'; -function WordPage(): React.JSX.Element { - const [loading, setLoading] = useState(true); +const WordPage: React.FC = () => { + const [loading, setLoading] = useState(true); const router = useIonRouter(); const modal = useRef(null); @@ -45,6 +45,7 @@ function WordPage(): React.JSX.Element { const [open_remove_modal, setOpenRemoveModal] = useState(false); const [lesson_info, setLessonInfo] = useState(undefined); + const [cat_info, setCatInfo] = useState(undefined); const [word_info, setWordInfo] = useState(undefined); @@ -322,6 +323,6 @@ function WordPage(): React.JSX.Element { {/* */} ); -} +}; export default WordPage; diff --git a/002_source/ionic_mobile/src/pages/Lesson/index.tsx b/002_source/ionic_mobile/src/pages/Lesson/index.tsx index a189692..309fb14 100644 --- a/002_source/ionic_mobile/src/pages/Lesson/index.tsx +++ b/002_source/ionic_mobile/src/pages/Lesson/index.tsx @@ -28,48 +28,19 @@ import LessonContainer from './LessonContainer'; import useHelloworld from '../../hooks/useHelloworld'; import useListAllLessonTypes from '../../hooks/useListAllLessonTypes'; import LessonsTypes, { LessonsType } from '../../types/LessonsTypes'; +import useListCategoriesByLessonId from '../../hooks/useListCategoriesByLessonId'; const Lesson: React.FC = () => { const { act_category } = useParams<{ act_category: string }>(); const { t, i18n } = useTranslation(); // not passing any namespace will use the defaultNS (by default set to 'translation') - + let router = useIonRouter(); let [loading, setLoading] = useState(true); - let { lesson_contents: lesson_content, setLessonContent } = useMyIonStore(); let [active_lesson_idx, setActiveLessonIdx] = useState(0); let [selected_content, setSelectedContent] = useState([]); - // useEffect(() => { - // listLessonCategories().then((cats: any) => { - // console.log({ cats }); - // setLessonContent(cats); - // setActiveLessonIdx(0); - // setLoading(false); - // }); - // }, []); - - useEffect(() => { - if (loading) return; - console.log('active_category changed', active_lesson_idx); - let selected_category = lesson_content[active_lesson_idx]; - setSelectedContent(selected_category['content']); - }, [active_lesson_idx, loading]); - - let router = useIonRouter(); - useEffect(() => { - if (lesson_content.length > 0) { - if (act_category == undefined) { - router.push(`${LESSON_LINK}/a/${lesson_content[0].name}`, undefined, 'replace'); - } else { - setActiveLessonIdx( - lesson_content.findIndex((cat: any) => cat.name.toLowerCase() === act_category?.toLowerCase()), - ); - } - } else { - } - }, [act_category, lesson_content]); - const lessonTypesQuery = useListAllLessonTypes(); const [lessonTypes, setLessonTypes] = useState([]); + useEffect(() => { if (lessonTypesQuery.status === 'success') { const { data: tempLessonTypes } = lessonTypesQuery; @@ -82,10 +53,8 @@ const Lesson: React.FC = () => { } }, [lessonTypesQuery]); + // return
{JSON.stringify({ lessonTypes }, null, 2)}
; if (loading) return ; - if (lessonTypes.length === 0) return ; - - // return
{JSON.stringify({ t: lessonTypes }, null, 2)}
; return ( @@ -140,10 +109,12 @@ const Lesson: React.FC = () => {
{/* */} - + {/* FIX: */} + {lessonTypes[active_lesson_idx]?.id ? ( + + ) : ( + <>loading (id undefined) + )} {/* */} @@ -156,3 +127,58 @@ const Lesson: React.FC = () => { }; export default Lesson; + +// let { lesson_contents: lesson_content, setLessonContent } = useMyIonStore(); + +// // TODO: remove me +// // useEffect(() => { +// // listLessonCategories().then((cats: any) => { +// // console.log({ cats }); +// // setLessonContent(cats); +// // setActiveLessonIdx(0); +// // setLoading(false); +// // }); +// // }, []); + +// useEffect(() => { +// if (loading) return; +// console.log('active_category changed', lesson_content[active_lesson_idx]); +// // FIX: +// // let selected_category = lesson_content[active_lesson_idx]; +// // setSelectedContent(selected_category['content']); + +// // let categories = await getCategoryByLessonType(lessonType) +// // +// // useListCategoriesByLessonType(lessonType) +// }, [active_lesson_idx, loading]); + +// useEffect(() => { +// if (lesson_content.length > 0) { +// if (act_category == undefined) { +// router.push(`${LESSON_LINK}/a/${lesson_content[0].name}`, undefined, 'replace'); +// } else { +// setActiveLessonIdx( +// lesson_content.findIndex((cat: any) => cat.name.toLowerCase() === act_category?.toLowerCase()), +// ); +// } +// } else { +// } +// }, [act_category, lesson_content]); + +// const lessonTypesQuery = useListAllLessonTypes(); +// const [lessonTypes, setLessonTypes] = useState([]); +// useEffect(() => { +// if (lessonTypesQuery.status === 'success') { +// const { data: tempLessonTypes } = lessonTypesQuery; +// console.log({ tempLessonTypes }); +// if (tempLessonTypes) { +// setLessonTypes(tempLessonTypes); +// } + +// setLoading(false); +// } +// }, [lessonTypesQuery]); + +// if (loading) return ; +// // return
{JSON.stringify({ t: lessonTypes }, null, 2)}
; +// if (lessonTypes.length === 0) return ; diff --git a/002_source/ionic_mobile/src/pages/ListeningPractice/Route/AudioControls.tsx b/002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/AudioControls.tsx similarity index 100% rename from 002_source/ionic_mobile/src/pages/ListeningPractice/Route/AudioControls.tsx rename to 002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/AudioControls.tsx diff --git a/002_source/ionic_mobile/src/pages/ListeningPractice/Route/CorrectionCard.tsx b/002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/CorrectionCard.tsx similarity index 100% rename from 002_source/ionic_mobile/src/pages/ListeningPractice/Route/CorrectionCard.tsx rename to 002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/CorrectionCard.tsx diff --git a/002_source/ionic_mobile/src/pages/ListeningPractice/Route/CorrectionRoute.tsx b/002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/CorrectionRoute.tsx similarity index 98% rename from 002_source/ionic_mobile/src/pages/ListeningPractice/Route/CorrectionRoute.tsx rename to 002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/CorrectionRoute.tsx index b6a46f3..4714188 100644 --- a/002_source/ionic_mobile/src/pages/ListeningPractice/Route/CorrectionRoute.tsx +++ b/002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/CorrectionRoute.tsx @@ -16,6 +16,9 @@ import CorrectionCard from './CorrectionCard'; import './style.css'; const CorrectionRoute: React.FC = () => { + // TODO: not yet implemented + return <>not yet implemented; + const router = useIonRouter(); const { p_route } = useParams<{ p_route: string }>(); const i_p_route = parseInt(p_route); diff --git a/002_source/ionic_mobile/src/pages/ListeningPractice/Route/ListeningPracticeQuestionCard.tsx b/002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/ListeningPracticeQuestionCard.tsx similarity index 100% rename from 002_source/ionic_mobile/src/pages/ListeningPractice/Route/ListeningPracticeQuestionCard.tsx rename to 002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/ListeningPracticeQuestionCard.tsx diff --git a/002_source/ionic_mobile/src/pages/ListeningPractice/Route/index.tsx b/002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/index.tsx similarity index 83% rename from 002_source/ionic_mobile/src/pages/ListeningPractice/Route/index.tsx rename to 002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/index.tsx index ed3c47a..8459d09 100644 --- a/002_source/ionic_mobile/src/pages/ListeningPractice/Route/index.tsx +++ b/002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/index.tsx @@ -10,8 +10,10 @@ import IListeningPracticeQuestion from '../../../interfaces/IListeningPracticeQu import { listQuizListeningPracticeContent } from '../../../public_data/listQuizListeningPracticeContent'; import { shuffleArray } from '../../../utils/shuffleArray'; import ListeningPracticeQuestionCard from './ListeningPracticeQuestionCard'; +import useListQuizLPQuestionByLPCategoryId from '../../../hooks/useListQuizLPQuestionByLPCategoryId'; const QuestionRoute: React.FC = () => { + const router = useIonRouter(); const { p_route } = useParams<{ p_route: string }>(); const i_p_route = parseInt(p_route); const [question_list, setQuestionList] = useState([]); @@ -26,7 +28,7 @@ const QuestionRoute: React.FC = () => { const { listening_practice_correction_list, setListeningPracticeCorrectionList } = useMyIonQuizContext(); const [question_finished, setQuestionFinished] = useState(false); - const router = useIonRouter(); + let [answer_list, setAnswerList] = useState([]); const nextQuestion = () => { if (current_question_idx >= question_list.length - 1) { setQuestionFinished(true); @@ -64,21 +66,27 @@ const QuestionRoute: React.FC = () => { myIonMetricIncFullMarkCount(); }; - let [answer_list, setAnswerList] = useState([]); - + let [questionList, setLPQuestionList] = useState([]); useEffect(() => { - (async () => { - const res_json = await listQuizListeningPracticeContent(); - - let init_answer = res_json[i_p_route].init_answer; - let temp = res_json[i_p_route].content; + if (questionList.length > 0) { + let temp = questionList; + let init_answer = temp[0].init_answer; let shuffled_temp = shuffleArray(temp); - + // setQuestionList(shuffled_temp); - setAnswerList([...new Set([...init_answer, ...temp.map((t: IListeningPracticeQuestion) => t.word)])]); - setCurrentQuestionMeta(res_json[i_p_route].content[current_question_idx]); - })(); - }, []); + setAnswerList([...new Set([...init_answer, ...temp.map((t) => t.word)])]); + setCurrentQuestionMeta(temp[current_question_idx] as unknown as IListeningPracticeQuestion); + // console.log({ t: result.data.items[0] }); + } + }, [questionList]); + + let result = useListQuizLPQuestionByLPCategoryId(p_route); + useEffect(() => { + if (result.status === 'success') { + // + setLPQuestionList(result.data.items); + } + }, [result]); if (!current_question_meta) return ; diff --git a/002_source/ionic_mobile/src/pages/ListeningPractice/Route/style.css b/002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/style.css similarity index 100% rename from 002_source/ionic_mobile/src/pages/ListeningPractice/Route/style.css rename to 002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/style.css diff --git a/002_source/ionic_mobile/src/pages/ListeningPractice/Route/test.tsx b/002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/test.tsx similarity index 100% rename from 002_source/ionic_mobile/src/pages/ListeningPractice/Route/test.tsx rename to 002_source/ionic_mobile/src/pages/ListeningPractice/QuestionRoute/test.tsx diff --git a/002_source/ionic_mobile/src/pages/ListeningPractice/index.tsx b/002_source/ionic_mobile/src/pages/ListeningPractice/index.tsx index 8c90371..6c47532 100644 --- a/002_source/ionic_mobile/src/pages/ListeningPractice/index.tsx +++ b/002_source/ionic_mobile/src/pages/ListeningPractice/index.tsx @@ -8,6 +8,7 @@ import { useAppStateContext } from '../../contexts/AppState'; import IListeningPracticeCategory from '../../interfaces/IListeningPracticeCategory'; import { listQuizListeningPracticeContent } from '../../public_data/listQuizListeningPracticeContent'; import { useTranslation } from 'react-i18next'; +import useListQuizListeningPracticeContent from '../../hooks/useListQuizListeningPracticeContent'; // import { listQuizListeningPracticeContent } from '../../public_data/listQuizListeningPracticeContent'; @@ -17,19 +18,27 @@ const ListeningPractice: React.FC = () => { let [categories, setCategories] = useState([]); let { show_confirm_user_exit, setShowConfirmUserExit, setTabActive } = useAppStateContext(); + // useEffect(() => { + // listQuizListeningPracticeContent().then((res_json: any) => { + // setCategories(res_json); + // DEBUG ? console.log({ res_json }) : ''; + // setLoading(false); + // }); + // setTabActive(QUIZ_MAIN_MENU_LINK); + // }, []); + + let result = useListQuizListeningPracticeContent(); useEffect(() => { - listQuizListeningPracticeContent().then((res_json: any) => { - setCategories(res_json); - DEBUG ? console.log({ res_json }) : ''; + if (result.status == 'success') { + setCategories(result.data.items); setLoading(false); - }); - setTabActive(QUIZ_MAIN_MENU_LINK); - }, []); + } + }, [result]); let { setListeningPracticeInProgress } = useAppStateContext(); let router = useIonRouter(); function startListeningPractice(idx: number) { - let url = `${LISTENING_PRACTICE_LINK}/r/${idx}`; + let url = `${LISTENING_PRACTICE_LINK}/r/${categories[idx].id}`; setListeningPracticeInProgress(true); router.push(url, 'none', 'replace'); } @@ -38,6 +47,8 @@ const ListeningPractice: React.FC = () => { if (loading) return ; + // return <>{JSON.stringify({ categories }, null, 2)}; + return ( diff --git a/002_source/ionic_mobile/src/pages/MatchingFrenzy/SelectCategory.tsx b/002_source/ionic_mobile/src/pages/MatchingFrenzy/SelectCategory.tsx index 6a61477..cf3c7f8 100644 --- a/002_source/ionic_mobile/src/pages/MatchingFrenzy/SelectCategory.tsx +++ b/002_source/ionic_mobile/src/pages/MatchingFrenzy/SelectCategory.tsx @@ -6,23 +6,38 @@ import { DEBUG, MATCHING_FRENZY_LINK, QUIZ_MAIN_MENU_LINK } from '../../constant import { useAppStateContext } from '../../contexts/AppState'; import IMatchingFrenzyCategory from '../../interfaces/IMatchingFrenzyCategory'; import { listMatchingFrenzyContent } from '../../public_data/listMatchingFrenzyContent'; +import useListMatchingFrenzyCategories from '../../hooks/useListMatchingFrenzyContent'; function MatchingFrenzySelectCategory() { const PAGE_TITLE = 'Matching Frenzy'; const router = useIonRouter(); const [loading, setLoading] = useState(true); const [categories, setCategories] = useState([]); + const [loadedCategories, setLoadedCategories] = useState([]); const { setTabActive } = useAppStateContext(); - useEffect(() => { - listMatchingFrenzyContent().then((res_json: any) => { - setCategories(res_json); - DEBUG ? console.log({ res_json }) : ''; - setLoading(false); - }); + // useEffect(() => { + // listMatchingFrenzyContent().then((res_json: any) => { + // setCategories(res_json); + // DEBUG ? console.log({ res_json }) : ''; + // setLoading(false); + // }); - setTabActive(QUIZ_MAIN_MENU_LINK); - }, []); + // setTabActive(QUIZ_MAIN_MENU_LINK); + // }, []); + + let [temp, setTemp] = useState(); + let result = useListMatchingFrenzyCategories(); + useEffect(() => { + if (result.status === 'success') { + // setCategories(result.data.items); + // setTemp(result.data.items) + setLoadedCategories(result.data.items); + setLoading(false); + } + }, [result]); + + // return <>helloworld; if (loading) return ; @@ -75,23 +90,26 @@ function MatchingFrenzySelectCategory() {
Choose the Chapter you want to revise
- {categories + {loadedCategories .map((item) => item.cat_name) - .map((item_name, idx) => ( -
- { - router.push(`${MATCHING_FRENZY_LINK}/r/${idx}`); - }} - > - {item_name} - -
- ))} + .map((item_name, idx) => { + let mf_idx = loadedCategories[idx].id; + return ( +
+ { + router.push(`${MATCHING_FRENZY_LINK}/r/${mf_idx}`); + }} + > + {item_name} + +
+ ); + })}
diff --git a/002_source/ionic_mobile/src/types/CategoryTypes.ts b/002_source/ionic_mobile/src/types/CategoryTypes.ts new file mode 100644 index 0000000..2bd1b5d --- /dev/null +++ b/002_source/ionic_mobile/src/types/CategoryTypes.ts @@ -0,0 +1,8 @@ +export interface CategoryType { + id: string; + name: string; + lessonType: string; + created: string; + updated: string; + // Add other fields as needed based on actual database schema +} diff --git a/002_source/ionic_mobile/src/types/MatchingFrenzyCategory.d.ts b/002_source/ionic_mobile/src/types/MatchingFrenzyCategory.d.ts new file mode 100644 index 0000000..0f4538d --- /dev/null +++ b/002_source/ionic_mobile/src/types/MatchingFrenzyCategory.d.ts @@ -0,0 +1,16 @@ +type MatchingFrenzyCategory = { + // + id: string; + collectionId: string; + // + cat_name: string; + cat_image_url?: string; + cat_image?: string; + pos: number; + visible: string; + lesson_id: string; + description: string; + remarks: string; + slug: string; + init_answer: any; +}; diff --git a/002_source/ionic_mobile/src/types/QuizLPQuestion.d.ts b/002_source/ionic_mobile/src/types/QuizLPQuestion.d.ts new file mode 100644 index 0000000..0403535 --- /dev/null +++ b/002_source/ionic_mobile/src/types/QuizLPQuestion.d.ts @@ -0,0 +1,20 @@ +// QuizLPQuestion +type QuizLPQuestion = { + // + id: string; + collectionId: string; + // + word: string; + // + cat_name: string; + cat_image_url?: string; + cat_image?: string; + pos: number; + visible: string; + lesson_id: string; + description: string; + remarks: string; + slug: string; + init_answer: any; + // +}; diff --git a/002_source/ionic_mobile/src/types/QuizLPQuestion.dbml b/002_source/ionic_mobile/src/types/QuizLPQuestion.dbml new file mode 100644 index 0000000..89fcd57 --- /dev/null +++ b/002_source/ionic_mobile/src/types/QuizLPQuestion.dbml @@ -0,0 +1,22 @@ +Table QuizLPQuestion { + id string [primary key] + collectionId string + createdAt datetime + + questionText text + audioUrl string + options json + correctAnswer integer + LPCategoryId string [ref: > QuizLPCategory.id] + difficulty enum('easy', 'medium', 'hard') + explanation text [null] + status enum('draft', 'published', 'archived') + + indexes { + LPCategoryId + status + createdAt + } + + Note: 'Stores listening practice quiz questions' +} diff --git a/002_source/ionic_mobile/src/types/_GUIDELINES.md b/002_source/ionic_mobile/src/types/_GUIDELINES.md new file mode 100644 index 0000000..1324714 --- /dev/null +++ b/002_source/ionic_mobile/src/types/_GUIDELINES.md @@ -0,0 +1,3 @@ +# GUIDELINES + +- please follow to the `schema.dbml` when you draft types related to db diff --git a/002_source/ionic_mobile/src/utils/getImage.ts b/002_source/ionic_mobile/src/utils/getImage.ts new file mode 100644 index 0000000..330fe1d --- /dev/null +++ b/002_source/ionic_mobile/src/utils/getImage.ts @@ -0,0 +1,6 @@ +import { POCKETBASE_URL } from '../constants'; + +export function getImage(collectionId: string, recordId: string, fileName: string) { + console.log({ collectionId, recordId, fileName }); + return `${POCKETBASE_URL}/api/files/${collectionId}/${recordId}/${fileName}`; +}