add new hooks for fetching QuizCRQuestions and categories, update related components to use the new hooks, and refactor SelectCategory page to use the new API
```
This commit is contained in:
louiscklaw
2025-05-12 19:24:25 +08:00
parent 650127821b
commit a6170778cd
9 changed files with 186 additions and 66 deletions

View File

@@ -0,0 +1,30 @@
import { idCard } from 'ionicons/icons';
import { QuizCRQuestion } from '../types/QuizCRQuestion';
import { usePocketBase } from './usePocketBase';
import { QueryClient } from '@tanstack/react-query';
import PocketBase from 'pocketbase';
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity,
},
},
});
const fetchCRQuestions = async (cat_id: string, pb: PocketBase) => {
const response = await queryClient.fetchQuery({
queryKey: ['fetchData', cat_id],
staleTime: 60 * 1000,
queryFn: async () => {
return await pb.collection('QuizCRQuestions').getList<QuizCRQuestion>(1, 9999, {
filter: `cat_id = "${cat_id}"`,
$autoCancel: false,
});
},
});
return response;
};
export default fetchCRQuestions;

View File

@@ -0,0 +1,38 @@
// CR = ConnectiveRevision
import { usePocketBase } from './usePocketBase.tsx';
import { useQuery } from '@tanstack/react-query';
import IListeningPracticeCategory from '../interfaces/IListeningPracticeCategory.tsx';
const useListQuizCRCategories = () => {
const { user, pb } = usePocketBase();
return useQuery({
queryKey: [
'useListQuizConnectiveRevisionContent',
'feeds',
'all',
user?.id || '',
//
],
staleTime: 60 * 1000,
queryFn: async ({
queryKey,
}: {
queryKey: [
'useListQuizConnectiveRevisionContent',
'feeds',
'all',
string | null,
//
];
}) => {
console.log('calling useListQuizConnectiveRevisionContent');
return await pb.collection('LessonsCategories').getList<IListeningPracticeCategory>(1, 9999, {
sort: 'pos',
$autoCancel: false,
});
},
// enabled: !!user?.id,
});
};
export default useListQuizCRCategories;

View File

@@ -0,0 +1,26 @@
import { usePocketBase } from './usePocketBase.tsx';
import { useQuery } from '@tanstack/react-query';
import { QuizCRQuestion } from '../types/QuizCRQuestion.ts/index.ts';
const useListQuizCRQuestionByCRCategoryId = (CRCategoryId: string) => {
const { user, pb } = usePocketBase();
return useQuery({
queryKey: ['useListQuizCRQuestionByCRCategoryId', 'feeds', 'all', user?.id || '', CRCategoryId],
staleTime: 60 * 1000,
queryFn: async ({
queryKey,
}: {
queryKey: ['useListQuizCRQuestionByCRCategoryId', 'feeds', 'all', string | null, string];
}) => {
console.log('calling useListQuizCRQuestionByCRCategoryId');
return await pb.collection('QuizCRQuestions').getList<QuizCRQuestion>(1, 9999, {
filter: `cat_id = "${CRCategoryId}"`,
sort: 'id',
$autoCancel: false,
});
},
// enabled: !!user?.id && !!CRCategoryId,
});
};
export default useListQuizCRQuestionByCRCategoryId;

View File

@@ -5,7 +5,9 @@ interface IConnectivesRevisionCategory {
test_i: number;
cat_info: string;
cat_name: string;
content: IConnectivesRevisionQuestion[] | [];
content?: IConnectivesRevisionQuestion[] | [];
//
id: string;
}
export default IConnectivesRevisionCategory;

View File

@@ -1,3 +1,4 @@
// abonded
interface IQuestionMeta {
question_idx: number;
question_fh: string;

View File

@@ -155,7 +155,7 @@ const QuizContent: React.FC<IQuestionCard> = ({
>
{answer_list.map((connective, idx) => {
return (
<div>
<div key={idx}>
<IonButton
color={'dark'}
ref={button_refs[idx]}

View File

@@ -8,11 +8,15 @@ import { useMyIonQuizContext } from '../../contexts/MyIonQuiz';
import { listConectivesRevisionContent } from '../../public_data/listConectivesRevisionContent';
import { shuffleArray } from '../../utils/shuffleArray';
import QuizContent from './QuizContent';
import fetchCRQuestions from '../../hooks/fetchCRQuestions';
import { usePocketBase } from '../../hooks/usePocketBase';
function ConnectiveRevisionQuizRun() {
const router = useIonRouter();
const { p_route } = useParams<{ p_route: string }>();
const i_p_route = parseInt(p_route);
const { p_route: cat_id } = useParams<{ p_route: string }>();
// NOTE: abonded, should be updated with `i_cat_id` when done
const i_p_route = parseInt(cat_id);
const { setTabActive } = useAppStateContext();
const [question_list, setQuestionList] = useState<IQuestionJson[] | []>([]);
@@ -22,6 +26,9 @@ function ConnectiveRevisionQuizRun() {
const [isOpenCorrectAnswer, setIsOpenCorrectAnswer] = useState(false);
const [isOpenWrongAnswer, setIsOpenWrongAnswer] = useState(false);
const [answer_list, setAnswerList] = useState<string[]>(['but', 'and', 'or', 'of', 'with']);
const { user, pb } = usePocketBase();
const {
setConnectiveRevisionCurrentTest,
setConnectiveRevisionProgress,
@@ -79,19 +86,19 @@ function ConnectiveRevisionQuizRun() {
useEffect(() => {
(async () => {
const res_json = await listConectivesRevisionContent();
let temp_init_ans = res_json[i_p_route].init_ans;
const res_json = await fetchCRQuestions(cat_id, pb);
let temp_init_ans: string[] = res_json.items[0].init_answer;
setInitAnswer(temp_init_ans);
let temp = res_json[i_p_route].content;
let temp = res_json.items;
let shuffled_temp = shuffleArray(temp);
// let shuffled_temp = temp;
setQuestionList(shuffled_temp);
let question_meta_current = res_json[i_p_route].content[0];
let question_meta_current = res_json.items[0] as unknown as IQuestionMeta;
setCurrentQuestionMeta({
question_idx: current_question_idx,
...question_meta_current,
question_idx: current_question_idx,
});
})();
setTabActive(QUIZ_MAIN_MENU_LINK);
@@ -113,31 +120,7 @@ function ConnectiveRevisionQuizRun() {
total_questions_num={question_list.length}
answer_list={answer_list}
quiz_idx={i_p_route + 1}
//
/>
{/* */}
{/* <CorrectAnswerToast isOpen={isOpenCorrectAnswer} dismiss={() => setIsOpenCorrectAnswer(false)} /> */}
{/* */}
{/* <WrongAnswerToast
correct_answer={current_question_meta.modal_ans}
isOpen={isOpenWrongAnswer}
dismiss={() => setIsOpenWrongAnswer(false)}
/> */}
{/*
<IonToast
isOpen={isOpenCorrectAnswer}
message='This answer is correct'
onDidDismiss={() => setIsOpenCorrectAnswer(false)}
duration={1000 - 100}
color='success'
></IonToast>
<IonToast
isOpen={isOpenWrongAnswer}
message='This answer is wrong'
onDidDismiss={() => setIsOpenWrongAnswer(false)}
duration={1000 - 100}
color='danger'
></IonToast> */}
</IonContent>
</IonPage>
</>

View File

@@ -8,6 +8,7 @@ import { ConnectiveRevisionAllResult } from '../../contexts/ConnectiveRevisionRa
import { useMyIonQuizContext } from '../../contexts/MyIonQuiz';
import IConnectivesRevisionCategory from '../../interfaces/IConnectivesRevisionCategory';
import { listConectivesRevisionContent } from '../../public_data/listConectivesRevisionContent';
import useListQuizCRCategories from '../../hooks/useListQuizCRCategories';
function ConnectiveRevisionSelectCategory() {
const PAGE_TITLE = 'Connective Revision';
@@ -17,12 +18,12 @@ function ConnectiveRevisionSelectCategory() {
let [categories, setCategories] = useState<IConnectivesRevisionCategory[] | []>([]);
let { setTabActive, setConnectiveRevisionInProgress } = useAppStateContext();
useEffect(() => {
listConectivesRevisionContent().then((res_json) => {
setCategories(res_json);
setLoading(false);
});
}, []);
// useEffect(() => {
// listConectivesRevisionContent().then((res_json) => {
// setCategories(res_json);
// setLoading(false);
// });
// }, []);
let { loadConnectiveRevisionScoreBoard } = useMyIonQuizContext();
let [scoreboard_meta, setScoreboardMeta] = useState<ConnectiveRevisionAllResult>();
@@ -36,6 +37,14 @@ function ConnectiveRevisionSelectCategory() {
setTabActive(QUIZ_MAIN_MENU_LINK);
}, []);
let result = useListQuizCRCategories();
useEffect(() => {
if (result.status === 'success') {
setCategories(result.data.items);
setLoading(false);
}
}, [result]);
if (loading) return <LoadingScreen />;
if (!scoreboard_meta) return <LoadingScreen />;
@@ -87,36 +96,34 @@ function ConnectiveRevisionSelectCategory() {
</div>
<div>{'Question Bank'}</div>
<div style={{ width: '80vw' }}>
{categories
.map((item) => item.cat_name)
.map((item_name, idx) => (
<div style={{ margin: '0.9rem 0 0.9rem' }} key={idx}>
<IonButton
color="dark"
fill="outline"
expand="block"
onClick={() => {
setConnectiveRevisionInProgress(true);
router.push(`${CONNECTIVE_REVISION_LINK}/r/${idx}`, 'none', 'replace');
{categories.map((item, idx) => (
<div style={{ margin: '0.9rem 0 0.9rem' }} key={idx}>
<IonButton
color="dark"
fill="outline"
expand="block"
onClick={() => {
setConnectiveRevisionInProgress(true);
router.push(`${CONNECTIVE_REVISION_LINK}/r/${item.id}`, 'none', 'replace');
}}
>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%',
}}
>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%',
}}
>
<div>{item_name}</div>
<div>
{scoreboard_meta[idx.toString()] || 0}
{'%'}
</div>
<div>{item.cat_name}</div>
<div>
{scoreboard_meta[idx.toString()] || 0}
{'%'}
</div>
</IonButton>
</div>
))}
</div>
</IonButton>
</div>
))}
</div>
</div>
</IonContent>

View File

@@ -0,0 +1,33 @@
/**
* Represents a Connectives Revision Quiz Question
*/
export interface QuizCRQuestion {
/** Unique identifier */
id: string;
/** Creation timestamp */
created: Date;
/** Last update timestamp */
updated: Date;
/** First half of the question */
question_fh: string;
/** Second half of the question */
question_sh: string;
/** Modal answer text */
modal_ans: string;
/** Initial answer text */
init_answer: string[];
/** Category ID reference */
cat_id: string;
/** Additional options in JSON format */
options: Record<string, unknown>;
}
/**
* Expanded QuizCRQuestion with related category data
*/
export interface QuizCRQuestionExpanded extends QuizCRQuestion {
/** Category name */
cat_name?: string;
/** Category image */
cat_image?: string;
}