diff --git a/api/_client.ts b/api/_client.ts
index 052e60e..69e176c 100644
--- a/api/_client.ts
+++ b/api/_client.ts
@@ -74,10 +74,10 @@ export const get_test = async (id: number) => {
return response.data;
}
-export const get_user_tests = async () => {
+export const get_my_user_tests = async () => {
const response = await axiosInstance.get<{
data: UserTestResponse[];
- }>("/api/user-tests/");
+ }>("/api/user-tests/me/");
return response.data;
}
diff --git a/api/types.ts b/api/types.ts
index 4635304..3e33c8d 100644
--- a/api/types.ts
+++ b/api/types.ts
@@ -29,7 +29,7 @@ export interface QuestionResponse {
type: QuestionTypes;
difficulty: number;
- variants?: QuestionVariant[];
+ variants: QuestionVariant[];
correct_answers: number[]|string[];
category_id: number;
@@ -63,7 +63,8 @@ export interface TestResponse {
author_id: number;
author: UserResponse;
- questions: QuestionResponse[];
+ questions?: QuestionResponse[];
+ questions_count: number;
closed_at: string;
@@ -95,10 +96,9 @@ export interface UserTestResponse {
is_completed: boolean;
score: number;
is_available: boolean;
-
+
answers: AnswerResponse[];
-
created_at: string;
updated_at: string;
}
diff --git a/api/userTests.ts b/api/userTests.ts
index 0c7ea70..7f0e51a 100644
--- a/api/userTests.ts
+++ b/api/userTests.ts
@@ -1,17 +1,17 @@
import { useQuery } from "@tanstack/react-query";
-import { get_user_test, get_user_tests } from "./_client";
+import { get_my_user_tests, get_user_test } from "./_client";
-export const useTests = () => {
+export const useMyTests = () => {
return useQuery({
queryKey: ['user-tests'],
queryFn: async () => {
- const response = await get_user_tests();
+ const response = await get_my_user_tests();
return response.data;
}
})
}
-export const useTest = (id: number) => {
+export const useUserTest = (id: number) => {
return useQuery({
queryKey: ['test', id],
queryFn: async () => {
diff --git a/app/(tabs)/me.tsx b/app/(tabs)/me.tsx
index 3720926..8739078 100644
--- a/app/(tabs)/me.tsx
+++ b/app/(tabs)/me.tsx
@@ -1,9 +1,10 @@
-
-import useAuthContext from '@/components/providers/auth-provider/hook';
-import { Button, ButtonText } from '@/components/ui/button';
-import Content from '@/components/ui/content';
-import { Divider } from '@/components/ui/divider';
-import UserHeader from '@/components/user-header';
+import useAuthContext from "@/components/providers/auth-provider/hook";
+import { ButtonText } from "@/components/ui/button";
+import ButtonSection from "@/components/ui/button-section";
+import Content from "@/components/ui/content";
+import { Divider } from "@/components/ui/divider";
+import UserHeader from "@/components/user-header";
+import { router } from "expo-router";
export default function MeScreen() {
const { user, logout, isAuthorized } = useAuthContext();
@@ -12,9 +13,25 @@ export default function MeScreen() {
-
+
- {isAuthorized && }
+ {isAuthorized && (
+ <>
+ router.push('/me/user-tests')}>
+ My Tests
+
+
+
+
+ logout()}
+ variant="solid"
+ >
+ Logout
+
+ >
+ )}
);
-}
\ No newline at end of file
+}
diff --git a/app/(tabs)/tests.tsx b/app/(tabs)/tests.tsx
index 9ffce8e..1da2c00 100644
--- a/app/(tabs)/tests.tsx
+++ b/app/(tabs)/tests.tsx
@@ -3,11 +3,13 @@ import { useTests } from '@/api/tests';
import { TestResponse } from '@/api/types';
import useTaxonomyContext from '@/components/providers/taxonomy-provider/hook';
import Test from '@/components/test';
+import { Button, ButtonText } from '@/components/ui/button';
import Content from '@/components/ui/content';
import CustomSelect from '@/components/ui/custom-select';
import PaginationList from '@/components/ui/pagination-list';
import { ThemedText } from '@/components/ui/themed-text';
import getCategoryOptions from '@/utils/get-category-options';
+import { FontAwesome5 } from '@expo/vector-icons';
import { router } from 'expo-router';
import { useRef, useState } from 'react';
import { ScrollView, View } from 'react-native';
@@ -39,13 +41,17 @@ export default function TestsScreen() {
Tests
-
+
+
diff --git a/app/me/user-tests.tsx b/app/me/user-tests.tsx
new file mode 100644
index 0000000..abe1987
--- /dev/null
+++ b/app/me/user-tests.tsx
@@ -0,0 +1,61 @@
+import { useMyTests } from "@/api/userTests";
+import Content from "@/components/ui/content";
+import CustomSelect from "@/components/ui/custom-select";
+import UserTest from "@/components/user-test";
+import { getUserTestStatus, TestStatus } from "@/utils/get-user-test-status";
+import { router, Stack } from "expo-router";
+import { useMemo, useState } from "react";
+import { View } from "react-native";
+
+const filterSelectOptions = [
+ { label: TestStatus.Completed, value: TestStatus.Completed },
+ { label: TestStatus.InProgress, value: TestStatus.InProgress },
+ { label: TestStatus.Expired, value: TestStatus.Expired },
+];
+
+const MeUserTestsScreen = () => {
+ const { data: userTests } = useMyTests();
+ const [selectedStatus, setStatus] = useState("all");
+ const filteredUserTests = useMemo(() => {
+ if(!userTests) return [];
+ if(selectedStatus !== "all") {
+ return userTests.filter((userTest) => getUserTestStatus(userTest) === selectedStatus);
+ }
+ return userTests;
+ }, [userTests, selectedStatus]);
+
+ if(!userTests)
+ return (
+ <>
+
+
+
+
+ >
+ );
+
+ return (
+ <>
+
+
+
+ setStatus(value)}
+ />
+
+
+ {filteredUserTests.map((userTest) => (
+
+ router.push(`/user-tests/${userTest.id}`)} />
+
+ ))}
+
+ >
+ )
+}
+
+export default MeUserTestsScreen;
\ No newline at end of file
diff --git a/app/tests/[id].tsx b/app/tests/[id].tsx
index d45abbc..9b35559 100644
--- a/app/tests/[id].tsx
+++ b/app/tests/[id].tsx
@@ -5,11 +5,13 @@ import { Button, ButtonText } from "@/components/ui/button";
import Content from "@/components/ui/content";
import { Stack, useLocalSearchParams } from "expo-router";
import { View } from "react-native";
+import { useToast } from "react-native-toast-notifications";
const TestScreen = () => {
const { id: idParam } = useLocalSearchParams<{ id: string }>();
const id = +idParam;
const { data, isLoading } = useTest(id);
+ const toast = useToast();
if (!data)
return (
@@ -19,7 +21,9 @@ const TestScreen = () => {
>
);
- const handleStartTest = () => {};
+ const handleStartTest = () => {
+ toast.show("Test started!", { type: "success" });
+ };
return (
<>
@@ -34,7 +38,7 @@ const TestScreen = () => {
}
- {data.questions.map((question) => (
+ {data.questions?.map((question) => (
))}
diff --git a/app/user-tests/[id].tsx b/app/user-tests/[id].tsx
new file mode 100644
index 0000000..2291a1c
--- /dev/null
+++ b/app/user-tests/[id].tsx
@@ -0,0 +1,95 @@
+import { AnswerResponse, QuestionVariant } from "@/api/types";
+import { useUserTest } from "@/api/userTests";
+import Question from "@/components/question";
+import Answer from "@/components/ui/answer";
+import { Button, ButtonText } from "@/components/ui/button";
+import Content from "@/components/ui/content";
+import { Divider } from "@/components/ui/divider";
+import UserTest from "@/components/user-test";
+import { getUserTestStatus, TestStatus } from "@/utils/get-user-test-status";
+import getUserTestTitle from "@/utils/get-user-test-title";
+import { Stack, useLocalSearchParams } from "expo-router";
+import { View } from "react-native";
+
+const UserTestScreen = () => {
+ const { id: idParam } = useLocalSearchParams<{ id: string }>();
+ const id = +idParam;
+ const { data: userTest } = useUserTest(id);
+
+ if (!userTest)
+ return (
+ <>
+
+
+ >
+ );
+
+ const status = getUserTestStatus(userTest);
+ const displayCorrectAnswers = status !== TestStatus.InProgress;
+
+ const handleContinueTest = () => {
+
+ }
+ const isCorrectVariation = (answer: AnswerResponse, variant: QuestionVariant) => {
+ if(answer.is_correct) return true;
+
+ const isCorrectVariant = (answer.question.correct_answers as number[]).includes(variant.id);
+ const isUserAnsweredVariant = answer.answer ? (answer.answer as number[]).includes(variant.id) : false;
+
+ return isCorrectVariant === isUserAnsweredVariant;
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+ {status === TestStatus.InProgress && }
+
+
+
+
+ {userTest.answers.map((answer) => (
+
+
+
+ {answer.question.type !== 'text' && answer.question.variants.map((variant) => (
+
+
+
+ ))}
+ {answer.question.type === 'text' && (
+
+
+
+ )}
+
+
+ ))}
+
+
+
+ >
+ )
+}
+
+export default UserTestScreen;
\ No newline at end of file
diff --git a/components/test.tsx b/components/test.tsx
index c1d59a4..6f8767d 100644
--- a/components/test.tsx
+++ b/components/test.tsx
@@ -43,7 +43,7 @@ const Test = ({ test, onPress }: TestProps) => {
{test.author.username}
- {test.questions.length} Questions
+ {test.questions_count} Questions
diff --git a/components/ui/answer.tsx b/components/ui/answer.tsx
index 8cbaa29..ecee9d8 100644
--- a/components/ui/answer.tsx
+++ b/components/ui/answer.tsx
@@ -91,7 +91,7 @@ const Answer = ({
return style;
}, [displayCorrect, displayUserAnswer, isAnsweredByUser, isCorrectAnswer, isTextType, userAnsweredCorrectly]);
const textColor = useMemo(() => {
- if(stylePanel)
+ if(stylePanel === styles.correctAnswer || stylePanel === styles.incorrectAnswer)
return "#ffffff";
else
return undefined;
diff --git a/components/ui/button-section.tsx b/components/ui/button-section.tsx
new file mode 100644
index 0000000..88322af
--- /dev/null
+++ b/components/ui/button-section.tsx
@@ -0,0 +1,18 @@
+import { ComponentProps } from "react";
+import { Button } from "./button";
+
+import Panel from "./panel";
+
+type ButtonSectionProps = ComponentProps;
+
+const ButtonSection = ({children, ...rest }: ButtonSectionProps) => {
+ return (
+
+
+
+ )
+}
+
+export default ButtonSection;
\ No newline at end of file
diff --git a/components/ui/custom-select/index.tsx b/components/ui/custom-select/index.tsx
index cba7a90..36b8398 100644
--- a/components/ui/custom-select/index.tsx
+++ b/components/ui/custom-select/index.tsx
@@ -14,6 +14,7 @@ interface CustomSelectProps {
defaultValue?: string;
selectedValue?:string;
onValueChange?: (value: string) => void;
+ className?: string;
}
const CustomSelect = ({
@@ -22,6 +23,7 @@ const CustomSelect = ({
defaultValue,
selectedValue,
noneOption,
+ className,
placeholder = "Select option"
}: CustomSelectProps) => {
const optionsComputed = useMemo(() => {
@@ -33,7 +35,7 @@ const CustomSelect = ({
const backgroundColor = useThemeColor({ }, "background");
return (
-