(user.demographicInformation?.timezone || moment.tz.guess());
+ const [isPreferredTopicsOpen, setIsPreferredTopicsOpen] = useState(false);
+
const {groups} = useGroups();
const {users} = useUsers();
@@ -156,6 +163,7 @@ function UserProfile({user, mutateUser}: Props) {
profilePicture,
desiredLevels,
preferredGender,
+ preferredTopics,
demographicInformation: {
phone,
country,
@@ -350,18 +358,41 @@ function UserProfile({user, mutateUser}: Props) {
{preferredGender && ["developer", "student"].includes(user.type) && (
<>
-
-
-
+
+
+
+
+
+
+
+
+
+
+ setIsPreferredTopicsOpen(false)}
+ selectTopics={setPreferredTopics}
+ initialTopics={preferredTopics || []}
+ />
>
)}
diff --git a/src/resources/topics.ts b/src/resources/topics.ts
new file mode 100644
index 00000000..424dbbbf
--- /dev/null
+++ b/src/resources/topics.ts
@@ -0,0 +1,55 @@
+const topics = [
+ "Education",
+ "Technology",
+ "Environment",
+ "Health and Fitness",
+ "Globalization",
+ "Engineering",
+ "Work and Careers",
+ "Travel and Tourism",
+ "Culture and Traditions",
+ "Social Issues",
+ "Arts and Entertainment",
+ "Climate Change",
+ "Social Media",
+ "Sustainable Development",
+ "Health Care",
+ "Immigration",
+ "Artificial Intelligence",
+ "Consumerism",
+ "Online Shopping",
+ "Energy",
+ "Oil and Gas",
+ "Poverty and Inequality",
+ "Cultural Diversity",
+ "Democracy and Governance",
+ "Mental Health",
+ "Ethics and Morality",
+ "Population Growth",
+ "Science and Innovation",
+ "Poverty Alleviation",
+ "Cybersecurity and Privacy",
+ "Human Rights",
+ "Social Justice",
+ "Food and Agriculture",
+ "Cyberbullying and Online Safety",
+ "Linguistic Diversity",
+ "Urbanization",
+ "Artificial Intelligence in Education",
+ "Youth Empowerment",
+ "Disaster Management",
+ "Mental Health Stigma",
+ "Internet Censorship",
+ "Sustainable Fashion",
+ "Indigenous Rights",
+ "Water Scarcity",
+ "Social Entrepreneurship",
+ "Privacy in the Digital Age",
+ "Sustainable Transportation",
+ "Gender Equality",
+ "Automation and Job Displacement",
+ "Digital Divide",
+ "Education Inequality",
+];
+
+export default topics;
diff --git a/src/utils/exams.be.ts b/src/utils/exams.be.ts
index 01b67116..be8d1837 100644
--- a/src/utils/exams.be.ts
+++ b/src/utils/exams.be.ts
@@ -1,7 +1,7 @@
import {collection, getDocs, query, where, setDoc, doc, Firestore, getDoc} from "firebase/firestore";
import {shuffle} from "lodash";
-import {Difficulty, Exam, InstructorGender, Variant} from "@/interfaces/exam";
-import {Stat, User} from "@/interfaces/user";
+import {Difficulty, Exam, InstructorGender, SpeakingExam, Variant, WritingExam} from "@/interfaces/exam";
+import {DeveloperUser, Stat, StudentUser, User} from "@/interfaces/user";
import {Module} from "@/interfaces";
export const getExams = async (
@@ -28,9 +28,10 @@ export const getExams = async (
})),
) as Exam[];
- const variantExams: Exam[] = filterByVariant(allExams, variant);
- const genderedExams: Exam[] = filterByInstructorGender(variantExams, instructorGender);
- const difficultyExams: Exam[] = await filterByDifficulty(db, genderedExams, module, userId);
+ let exams: Exam[] = filterByVariant(allExams, variant);
+ exams = filterByInstructorGender(exams, instructorGender);
+ exams = await filterByDifficulty(db, exams, module, userId);
+ exams = await filterByPreference(db, exams, module, userId);
if (avoidRepeated === "true") {
const statsQ = query(collection(db, "stats"), where("user", "==", userId));
@@ -40,12 +41,12 @@ export const getExams = async (
id: doc.id,
...doc.data(),
})) as unknown as Stat[];
- const filteredExams = difficultyExams.filter((x) => !stats.map((s) => s.exam).includes(x.id));
+ const filteredExams = exams.filter((x) => !stats.map((s) => s.exam).includes(x.id));
- return filteredExams.length > 0 ? filteredExams : difficultyExams;
+ return filteredExams.length > 0 ? filteredExams : exams;
}
- return difficultyExams;
+ return exams;
};
const filterByInstructorGender = (exams: Exam[], instructorGender?: InstructorGender) => {
@@ -70,3 +71,25 @@ const filterByDifficulty = async (db: Firestore, exams: Exam[], module: Module,
const filteredExams = exams.filter((exam) => exam.difficulty === difficulty);
return filteredExams.length === 0 ? exams : filteredExams;
};
+
+const filterByPreference = async (db: Firestore, exams: Exam[], module: Module, userID?: string) => {
+ if (!["speaking", "writing"].includes(module)) return exams;
+
+ if (!userID) return exams;
+ const userRef = await getDoc(doc(db, "users", userID));
+ if (!userRef.exists()) return exams;
+
+ const user = {...userRef.data(), id: userRef.id} as StudentUser | DeveloperUser;
+ if (!["developer", "student"].includes(user.type)) return exams;
+ if (!user.preferredTopics || user.preferredTopics.length === 0) return exams;
+
+ const userTopics = user.preferredTopics;
+ const topicalExams = exams.filter((e) => {
+ const exam = e as WritingExam | SpeakingExam;
+ const topics = exam.exercises.map((x) => x.topic).filter((x) => !!x) as string[];
+
+ return topics.some((topic) => userTopics.map((x) => x.toLowerCase()).includes(topic.toLowerCase()));
+ });
+
+ return topicalExams.length > 0 ? shuffle(topicalExams) : exams;
+};