import { useId } from 'react';
import { database, auth } from '../../firebase';
import { collection, getDocs, getDoc, query, where, orderBy, setDoc, doc, addDoc, deleteDoc, updateDoc, arrayUnion, Timestamp, arrayRemove, increment } from 'firebase/firestore';
/**
* @class FlashcardRepo
* @classdesc FlashcardRepo
* @description Repository for handling flashcard-related operations with Firebase.
*/
const FlashcardRepo = {
/**
* @memberof FlashcardRepo
* @function getCurrentUid
* @description Retrieves the current user's UID.
* @returns {string|null} The current user's UID or null if not logged in.
*/
getCurrentUid: function () {
const user = auth.currentUser;
return user ? user.uid : null;
},
/**
* @memberof FlashcardRepo
* @function getUserSubjects
* @description Fetches subjects associated with a specific user.
* @param {string} uid - The user's UID.
* @returns {Promise<Array>} A promise that resolves to an array of subjects.
*/
getUserSubjects: async function (uid) {
try {
const userDoc = await getDoc(doc(database, 'users', uid));
if (userDoc.exists()) {
return userDoc.data().subject || [];
} else {
console.error("User with the given UID not found.");
return [];
}
} catch (error) {
console.error("Error fetching user subjects:", error);
return [];
}
},
/**
* @memberof FlashcardRepo
* @function getUserFlashcardSets
* @description Retrieves flashcard sets owned by a specific user.
* @param {string} uid - The user's UID.
* @returns {Promise<Array>} A promise that resolves to an array of flashcard sets.
*/
getUserFlashcardSets: async function (uid) {
try {
const userDocRef = doc(database, 'users', uid);
const userDocSnapshot = await getDoc(userDocRef);
if (userDocSnapshot.exists()) {
const userData = userDocSnapshot.data();
if (userData && userData.ownedFlashcards) {
return userData.ownedFlashcards;
}
}
return [];
} catch (error) {
console.error("Error fetching user flashcard sets:", error);
return [];
}
},
/**
* @memberof FlashcardRepo
* @function getFlashcardSetById
* @description Fetches a flashcard set by its ID.
* @param {string} setId - The ID of the flashcard set.
* @returns {Promise<Object>} A promise that resolves to the flashcard set object.
*/
getFlashcardSetById: async function (setId) {
try {
const setDoc = await getDoc(doc(database, 'flashcardSets', setId));
const data = setDoc.data();
return {
name: data?.name || '',
subject: data?.subject || ''
};
} catch (error) {
console.error("Error fetching flashcard set:", error);
return null;
}
},
/**
* @memberof FlashcardRepo
* @function createFlashcardSet
* @description Creates a new flashcard set with given parameters.
* @param {Object} setDetails - The details of the flashcard set including name and subject.
* @returns {Promise<Object>} A promise that resolves to the new flashcard set's ID.
*/
createFlashcardSet: async function ({ name, subject }) {
try {
const flashcardId = doc(collection(database, 'flashcards')).id;
const commentId = doc(collection(database, 'comments')).id;
const questionId = doc(collection(database, 'questions')).id;
const scoreId = doc(collection(database, 'scores')).id;
const initialFlashcardItems = {
[flashcardId]: {
term: "Sample Term",
definition: "Sample Definition",
status: ""
},
};
const initialComments = {
[commentId]: {
uid: this.getCurrentUid(),
content: "Sample feedback content",
date: Timestamp.now()
},
};
const initialQuizItems = {
[questionId]: {
question: "Sample Question",
choices: ["Choice 1", "Choice 2", "Choice 3", "Choice 4"],
correctChoiceIndex: 0
},
};
const initialScore = {
[scoreId]: {
score: 0,
attempt: increment(0),
},
};
const setData = {
name: name,
createdAt: Timestamp.now(),
authorId: this.getCurrentUid(),
subject: subject,
sharedWith: [],
flashcardItems: initialFlashcardItems,
comments: initialComments
};
const newDocRef = await addDoc(collection(database, 'flashcardSets'), setData);
console.log("New flashcard set created with ID:", newDocRef.id);
const setQuizData = {
name: name,
createdAt: Timestamp.now(),
authorId: this.getCurrentUid(),
subject: subject,
sharedWith: [],
quizName: "Initial Quiz",
questionItems: initialQuizItems,
flashcardSetId: newDocRef.id
};
const newDocRefQuizzes = await addDoc(collection(database, 'quizzesCreation'), setQuizData);
console.log("New quiz set created with ID:", newDocRefQuizzes.id);
const uid = this.getCurrentUid();
if (uid) {
await this.addOwnedFlashcardSetToUser(uid, newDocRef.id);
await this.addOwnedQuizSetToUser(uid, newDocRefQuizzes.id);
}
return { flashcardSetId: newDocRef.id, quizSetId: newDocRefQuizzes.id };
} catch (error) {
console.error("Error creating flashcard set:", error);
throw error;
}
},
/**
* @memberof FlashcardRepo
* @function copyFlashcards
* @description Copies a flashcard set to a new user.
* @param {string} flashcardId - The ID of the flashcard set to copy.
* @param {string} userId - The ID of the user to copy the flashcard set to.
* @returns {Promise<string|null>} A promise that resolves to the new flashcard set ID or null if failed.
*/
copyFlashcards: async function (flashcardId, userId) {
try {
const originalFlashcardRef = doc(database, 'flashcardSets', flashcardId);
const originalFlashcardSnapshot = await getDoc(originalFlashcardRef);
if (!originalFlashcardSnapshot.exists()) {
console.error("Original flashcard set not found.");
return null;
}
const originalFlashcardData = originalFlashcardSnapshot.data();
const subject = originalFlashcardData.subject;
const newFlashcardData = {
...originalFlashcardData,
createdAt: Timestamp.now(),
};
const newFlashcardRef = await addDoc(collection(database, 'flashcardSets'), newFlashcardData);
if (subject) {
await this.addUserSubject(userId, subject);
}
const newFlashcardId = newFlashcardRef.id;
await this.addOwnedFlashcardSetToUser(userId, newFlashcardId);
console.log("New flashcard set created with ID:", newFlashcardId);
return newFlashcardId;
} catch (error) {
console.error("Error copying flashcards:", error);
throw error;
}
},
/**
* @memberof FlashcardRepo
* @function addOwnedFlashcardSetToUser
* @description Adds a flashcard set to the list of sets owned by a user.
* @param {string} uid - The user's UID.
* @param {string} flashcardSetId - The ID of the flashcard set.
* @returns {Promise<void>}
*/
addOwnedFlashcardSetToUser: async function (uid, flashcardSetId) {
try {
const userDocRef = doc(database, 'users', uid);
await updateDoc(userDocRef, {
ownedFlashcards: arrayUnion(flashcardSetId)
});
console.log(`FlashcardSet ID ${flashcardSetId} added to user with UID ${uid}`);
} catch (error) {
console.error("Error adding flashcardSet ID to user:", error);
throw error;
}
},
/**
* @memberof FlashcardRepo
* @function updateFlashcardSetName
* @description Updates the name of a specified flashcard set.
* @param {string} setId - The ID of the flashcard set.
* @param {string} newName - The new name for the flashcard set.
* @returns {Promise<void>}
*/
updateFlashcardSetName: async function (setId, newName) {
try {
const flashcardSetRef = doc(database, 'flashcardSets', setId);
const snap = await getDoc(flashcardSetRef);
if (snap.exists()) {
await updateDoc(flashcardSetRef, {
name: newName
});
console.log(`Flashcard set with ID ${setId} updated successfully with new name: ${newName}.`);
} else {
console.log(`Flashcard set with ID ${setId} not found.`);
}
} catch (error) {
console.error("Error updating flashcard set name:", error);
throw error;
}
},
fetchTopicName: async function (setId) {
try {
const flashcardSetRef = doc(database, 'flashcardSets', setId);
const snap = await getDoc(flashcardSetRef);
if (snap.exists()) {
const flashcardSetData = snap.data();
console.log(`Flashcard set with ID ${setId} fetched successfully.`);
return flashcardSetData.name;
} else {
console.log(`Flashcard set with ID ${setId} not found.`);
return null;
}
} catch (error) {
console.error("Error fetching topic name:", error);
throw error;
}
},
/**
* @memberof FlashcardRepo
* @function addFlashcardItem
* @description Adds a new flashcard item to a specified flashcard set.
* @param {string} setId - The ID of the flashcard set.
* @param {string} term - The term of the new flashcard.
* @param {string} definition - The definition of the new flashcard.
* @returns {Promise<string>} A promise that resolves to the ID of the newly added flashcard item.
*/
addFlashcardItem: async function (setId, term, definition) {
try {
const flashcardId = doc(collection(database, 'flashcards')).id;
const cardData = {
term: term,
definition: definition,
status: ""
};
const flashcardSetRef = doc(database, 'flashcardSets', setId);
const snap = await getDoc(flashcardSetRef);
const data = snap.data();
let flashcardItems = data.flashcardItems || {};
flashcardItems[flashcardId] = cardData;
console.log("Adding new flashcard:", flashcardItems);
await updateDoc(flashcardSetRef, {
flashcardItems: flashcardItems
});
return flashcardId;
} catch (error) {
console.error("fetch flashcard error", error);
throw error;
}
},
/**
* @memberof FlashcardRepo
* @function deleteFlashcard
* @description Deletes a specific flashcard from a flashcard set.
* @param {string} setId - The ID of the flashcard set.
* @param {string} flashcardIdToDelete - The ID of the flashcard to delete.
* @returns {Promise<void>}
*/
deleteFlashcard: async function (setId, flashcardIdToDelete) {
try {
const flashcardSetRef = doc(database, 'flashcardSets', setId);
const snap = await getDoc(flashcardSetRef);
const data = snap.data();
let flashcardItems = data.flashcardItems || {};
if (flashcardItems[flashcardIdToDelete]) {
delete flashcardItems[flashcardIdToDelete];
await updateDoc(flashcardSetRef, {
flashcardItems: flashcardItems
});
console.log(`Flashcard with ID ${flashcardIdToDelete} deleted successfully.`);
} else {
console.log(`Flashcard with ID ${flashcardIdToDelete} not found.`);
}
} catch (error) {
console.error("Error deleting flashcard", error);
throw error;
}
},
/**
* @memberof FlashcardRepo
* @function updateFlashcard
* @description Updates the term and definition of a specific flashcard in a set.
* @param {string} setId - The ID of the flashcard set.
* @param {string} flashcardIdToUpdate - The ID of the flashcard to update.
* @param {string} newTerm - The new term for the flashcard.
* @param {string} newDefinition - The new definition for the flashcard.
* @returns {Promise<void>}
*/
updateFlashcard: async function (setId, flashcardIdToUpdate, newTerm, newDefinition) {
try {
const flashcardSetRef = doc(database, 'flashcardSets', setId);
const snap = await getDoc(flashcardSetRef);
const data = snap.data();
let flashcardItems = data.flashcardItems || {};
if (flashcardItems[flashcardIdToUpdate]) {
flashcardItems[flashcardIdToUpdate] = {
term: newTerm,
definition: newDefinition
};
await updateDoc(flashcardSetRef, {
flashcardItems: flashcardItems
});
console.log(`Flashcard with ID ${flashcardIdToUpdate} updated successfully.`);
} else {
console.log(`Flashcard with ID ${flashcardIdToUpdate} not found.`);
}
} catch (error) {
console.error("Error updating flashcard", error);
throw error;
}
},
/**
* @memberof FlashcardRepo
* @function addUserSubject
* @description Adds a new subject to a user's list of subjects.
* @param {string} uid - The UID of the user.
* @param {string} newSubject - The new subject to add.
* @returns {Promise<void>}
*/
addUserSubject: async function (uid, newSubject) {
try {
const userRef = doc(database, 'users', uid);
await updateDoc(userRef, {
subject: arrayUnion(newSubject)
});
console.log("Subject added successfully.");
} catch (error) {
console.error("Error adding subject:", error);
throw error;
}
},
/**
* @memberof FlashcardRepo
* @function removeUidFromSharedWith
* @description Removes a user's UID from the 'sharedWith' list of a flashcard set.
* @param {string} setId - The ID of the flashcard set.
* @param {string} uid - The UID of the user to remove.
* @returns {Promise<void>}
*/
removeUidFromSharedWith: async function (setId, uid) {
try {
const setRef = doc(database, 'flashcardSets', setId);
await updateDoc(setRef, {
sharedWith: arrayRemove(uid)
});
} catch (error) {
console.error("Error removing UID from sharedWith:", error);
}
},
/**
* @memberof FlashcardRepo
* @function removeSetIdFromUser
* @description Removes a flashcard set ID from a user's list of owned flashcard sets.
* @param {string} uid - The UID of the user.
* @param {string} setId - The ID of the flashcard set to remove.
* @returns {Promise<void>}
*/
removeSetIdFromUser: async function (uid, setId) {
try {
const userRef = doc(database, 'users', uid);
await updateDoc(userRef, {
ownedFlashcards: arrayRemove(setId)
});
} catch (error) {
console.error("Error removing set ID from user:", error);
}
},
/**
* @memberof FlashcardRepo
* @function getUserImageURLByUid
* @description Retrieves the image URL of a user based on their UID.
* @param {string} uid - The UID of the user.
* @returns {Promise<string|null>} A promise that resolves to the user's image URL or null if not found.
*/
getUserImageURLByUid: async function (uid) {
try {
const userRef = doc(database, 'users', uid);
const userSnapshot = await getDoc(userRef);
if (userSnapshot.exists()) {
const userData = userSnapshot.data();
if (userData && userData.imageURL) {
return userData.imageURL;
} else {
console.error("User avatar URL not found for the given UID.");
return null;
}
} else {
console.error("User with the given UID not found.");
return null;
}
} catch (error) {
console.error("Error fetching user avatar URL by UID:", error);
throw error;
}
},
/**
* @memberof FlashcardRepo
* @function updateCardStatus
* @description Updates the status of a specific flashcard in a set.
* @param {string} setId - The ID of the flashcard set.
* @param {string} flashcardId - The ID of the flashcard to update.
* @param {string} newStatus - The new status for the flashcard.
* @returns {Promise<void>}
*/
updateCardStatus: async function (setId, flashcardId, newStatus) {
const flashcardSetRef = doc(database, 'flashcardSets', setId);
const snap = await getDoc(flashcardSetRef);
if (!snap.exists()) {
console.error("Flashcard set not found");
return;
}
const data = snap.data();
let flashcardItems = data.flashcardItems || {};
if (flashcardItems[flashcardId]) {
flashcardItems[flashcardId].status = newStatus;
await updateDoc(flashcardSetRef, {
flashcardItems: flashcardItems
});
} else {
console.error("Flashcard not found");
}
},
/**
* @memberof FlashcardRepo
* @function getSetIdByTopicName
* @description Retrieves the ID of a flashcard set by its topic name.
* @param {string} topicName - The name of the topic.
* @returns {Promise<string|null>} A promise that resolves to the flashcard set ID or null if not found.
*/
getSetIdByTopicName: async function (topicName) {
try {
const querySnapshot = await getDocs(query(collection(database, 'flashcardSets'), where('name', '==', topicName)));
if (!querySnapshot.empty) {
return querySnapshot.docs[0].id;
}
return null;
} catch (error) {
console.error("Error fetching set ID by topic name:", error);
return null;
}
},
/**
* @memberof FlashcardRepo
* @function getFlashcardItems
* @description Retrieves all flashcard items in a specific flashcard set.
* @param {string} setId - The ID of the flashcard set.
* @returns {Promise<Object>} A promise that resolves to an object containing flashcard items.
*/
getFlashcardItems: async function (setId) {
try {
const setRef = doc(database, 'flashcardSets', setId);
const setSnapshot = await getDoc(setRef);
const setData = setSnapshot.data();
const flashcardData = setData.flashcardItems || [];
return flashcardData;
} catch (error) {
console.error("Error getting flashcard items:", error);
throw error;
}
},
/**
* @memberof FlashcardRepo
* @function getCommentsWithUserData
* @description Retrieves all comments for a flashcard set along with user data.
* @param {string} setId - The ID of the flashcard set.
* @returns {Promise<Array>} A promise that resolves to an array of comments with user data.
*/
getCommentsWithUserData: async function (setId) {
try {
const setRef = doc(database, 'flashcardSets', setId);
const setSnapshot = await getDoc(setRef);
const setData = setSnapshot.data();
const commentsMap = setData.comments || {};
const commentsData = [];
for (let commentId in commentsMap) {
const comment = commentsMap[commentId];
const userRef = doc(database, 'users', comment.uid);
const userSnapshot = await getDoc(userRef);
const userData = userSnapshot.data();
commentsData.push({
content: comment.content,
date: comment.date,
like: comment.like,
username: userData.name,
imageURL: userData.imageURL,
commentId: commentId
});
}
return commentsData;
} catch (error) {
console.error("Error getting comments with user data:", error);
throw error;
}
},
/**
* @memberof FlashcardRepo
* @function updateLikesForComment
* @description Updates the like count for a specific comment in a flashcard set.
* @param {string} setId - The ID of the flashcard set.
* @param {string} commentId - The ID of the comment.
* @param {number} updatedLikes - The updated like count.
* @returns {Promise<void>}
*/
updateLikesForComment: async function (setId, commentId, updatedLikes) {
try {
const setRef = doc(database, 'flashcardSets', setId);
const fieldPath = `comments.${commentId}.like`;
await updateDoc(setRef, {
[fieldPath]: updatedLikes
});
console.log(`Successfully updated likes for comment ${commentId} to ${updatedLikes}.`);
} catch (error) {
console.error("Error updating likes for comment:", error);
throw error;
}
},
/**
* @memberof FlashcardRepo
* @function addComment
* @description Adds a new comment to a flashcard set.
* @param {string} setId - The ID of the flashcard set.
* @param {Object} commentData - The data of the new comment.
* @returns {Promise<void>}
*/
addComment: async function (setId, commentData) {
try {
const setRef = doc(database, 'flashcardSets', setId);
const snap = await getDoc(setRef);
const setData = snap.data();
if (setData && setData.comments) {
const updatedComments = {
...setData.comments,
[doc(collection(database, 'comments')).id]: commentData
};
await updateDoc(setRef, {
comments: updatedComments
});
console.log(`Successfully added a new comment to flashcard set with ID ${setId}.`);
} else {
console.error(`Flashcard set with ID ${setId} does not exist or has no 'comments' field.`);
}
} catch (error) {
console.error("Error adding comment:", error);
throw error;
}
},
/**
* @memberof FlashcardRepo
* @function getFlashcardItemsByStatus
* @description Retrieves flashcard items from a set filtered by a specific status.
* @param {string} setId - The ID of the flashcard set.
* @param {string} status - The status to filter by.
* @returns {Promise<Array>} A promise that resolves to an array of flashcards with the specified status.
*/
getFlashcardItemsByStatus: async function (setId, status) {
try {
const setRef = doc(database, 'flashcardSets', setId);
const setSnapshot = await getDoc(setRef);
const setData = setSnapshot.data();
const flashcardData = setData.flashcardItems || [];
// if flashcardData is an object, convert it to an array
const flashcardArray = Object.values(flashcardData);
//console.log('Converted flashcardData to array:', flashcardArray);
// filter flashcards based on the status field
const flashcardsWithStatus = flashcardArray.filter(flashcard => flashcard.status === status);
//console.log("Filtered flashcards are: ", flashcardsWithStatus);
return flashcardsWithStatus;
} catch (error) {
console.error("Error getting flashcard items:", error);
throw error;
}
},
// add owned quizzes into the users table using the flashcardSet id
/**
* @memberof FlashcardRepo
* @function addOwnedQuizSetToUser
* @description Adds a quiz set to a user's list of owned quizzes.
* @param {string} uid - The UID of the user.
* @param {string} quizSetId - The ID of the quiz set to add.
* @returns {Promise<void>}
*/
addOwnedQuizSetToUser: async function (uid, quizSetId) {
try {
const userDocRef = doc(database, 'users', uid);
await updateDoc(userDocRef, {
ownedQuizzes: arrayUnion(quizSetId)
});
console.log(`QuizSet ID ${quizSetId} added to user with UID ${uid}`);
} catch (error) {
console.error("Error adding quizset ID to user:", error);
throw error;
}
},
// add all question data to the database table called "flashcardSets"
/**
* @memberof FlashcardRepo
* @function addQuizQuestion
* @description Adds a new question to a quiz set.
* @param {string} setId - The ID of the quiz set.
* @param {string} question - The question text.
* @param {Array<string>} choices - An array of choices for the question.
* @param {number} correctChoiceIndex - The index of the correct choice.
* @returns {Promise<string>} A promise that resolves to the ID of the newly added question.
*/
addQuizQuestion: async function (setId, question, choices, correctChoiceIndex) {
try {
const questionId = doc(collection(database, 'questions')).id;
const questionData = {
question: question,
choices: choices,
correctChoice: correctChoiceIndex,
};
const flashcardSetRef = doc(database, 'quizzesCreation', setId); // Use 'flashcardSets' collection
const snap = await getDoc(flashcardSetRef);
const data = snap.data();
let questionItems = data.questionItems || {};
questionItems[questionId] = questionData;
console.log("Adding new question:", questionItems);
await updateDoc(flashcardSetRef, {
questionItems: questionItems
});
return questionId;
} catch (error) {
console.error("Error adding quiz question", error);
throw error;
}
},
// delete question from the flashcardSets table and from questionItems field
/**
* @memberof FlashcardRepo
* @function deleteQuestion
* @description Deletes a question from a quiz set.
* @param {string} setId - The ID of the quiz set.
* @param {string} questionIdToBeDeleted - The ID of the question to delete.
* @returns {Promise<void>}
*/
deleteQuestion: async function (setId, questionIdToBeDeleted) {
try {
const flashcardSetRef = doc(database, 'quizzesCreation', setId);
const snap = await getDoc(flashcardSetRef);
const data = snap.data();
let questionItems = data.questionItems || {};
if (questionItems[questionIdToBeDeleted]) {
delete questionItems[questionIdToBeDeleted];
await updateDoc(flashcardSetRef, {
questionItems: questionItems
});
console.log(`Flashcard with ID ${questionIdToBeDeleted} deleted successfully.`);
} else {
console.log(`Flashcard with ID ${questionIdToBeDeleted} not found.`);
}
} catch (error) {
console.error("Error deleting flashcard", error);
throw error;
}
},
// get all the question data from the database called quizSet
/**
* @memberof FlashcardRepo
* @function getQuestionItems
* @description Retrieves all question items from a quiz set.
* @param {string} setId - The ID of the quiz set.
* @returns {Promise<Object>} A promise that resolves to an object containing question items.
*/
getQuestionItems: async function (setId) {
try {
const setRef = doc(database, 'quizzesCreation', setId);
const setSnapshot = await getDoc(setRef);
const setData = setSnapshot.data();
const questionData = setData.questionItems || [];
return questionData;
} catch (error) {
console.error("Error getting flashcard items:", error);
throw error;
}
},
// update existing question as requested from the user
/**
* @memberof FlashcardRepo
* @function updateQuestion
* @description Updates an existing question in a quiz set.
* @param {string} setId - The ID of the quiz set.
* @param {string} questionToBeUpdated - The ID of the question to update.
* @param {string} newQuestion - The new question text.
* @param {Array<string>} newChoices - The new array of choices.
* @param {number} newCorrectChoiceIndex - The index of the new correct choice.
* @returns {Promise<void>}
*/
updateQuestion: async function (setId, questionToBeUpdated, newQuestion, newChoices, newCorrectChoiceIndex) {
try {
const flashcardSetRef = doc(database, 'quizzesCreation', setId);
const snap = await getDoc(flashcardSetRef);
const data = snap.data();
// this will retrieve from questionItems field on firebase
let questionItems = data.questionItems || {};
if (questionItems[questionToBeUpdated]) {
questionItems[questionToBeUpdated] = {
question: newQuestion,
choices: newChoices,
correctChoiceIndex: newCorrectChoiceIndex,
};
await updateDoc(flashcardSetRef, {
questionItems: questionItems
});
console.log(`Question with ID ${questionToBeUpdated} updated successfully.`);
} else {
console.log(`Question with ID ${questionToBeUpdated} not found.`);
}
} catch (error) {
console.error("Error updating flashcard", error);
throw error;
}
},
// get all the quiz titles from its flashcard sets
/**
* @memberof FlashcardRepo
* @function getQuizTitleFromFlashcardSet
* @description Retrieves all quiz titles from a flashcard set.
* @param {string} flashcardSetId - The ID of the flashcard set.
* @returns {Promise<Array<string>>} A promise that resolves to an array of quiz titles.
*/
getQuizTitleFromFlashcardSet: async function (flashcardSetId) {
try {
const quizzesRef = collection(database, 'quizzesCreation');
// Retrieve all quizzes from the flashcard set using the flashcard id
const querySnapshot = await getDocs(query(quizzesRef, where('flashcardSetId', '==', flashcardSetId)));
const quizTitles = [];
querySnapshot.forEach((doc) => {
const quizData = doc.data();
quizTitles.push(quizData.quizName);
});
return quizTitles;
} catch (error) {
console.error("Error getting quiz titles:", error);
throw error;
}
},
// get quiz id by using the quiz name
/**
* @memberof FlashcardRepo
* @function getQuizTitleId
* @description Retrieves the ID of a quiz by its title and associated flashcard set ID.
* @param {string} quizName - The name of the quiz.
* @param {string} flashcardSetId - The ID of the associated flashcard set.
* @returns {Promise<string|null>} A promise that resolves to the quiz ID or null if not found.
*/
getQuizTitleId: async function (quizName, flashcardSetId) {
try {
const querySnapshot = await getDocs(query(collection(database, 'quizzesCreation'),
where('quizName', '==', quizName),
where('flashcardSetId', '==', flashcardSetId)));
if (!querySnapshot.empty) {
return querySnapshot.docs[0].id;
}
return null;
} catch (error) {
console.error("Error fetching set ID by topic name:", error);
return null;
}
},
// get quiz id by using topic name
/**
* @memberof FlashcardRepo
* @function getQuizIdByTopicName
* @description Retrieves the ID of a quiz by its topic name.
* @param {string} topicName - The topic name of the quiz.
* @returns {Promise<string|null>} A promise that resolves to the quiz ID or null if not found.
*/
getQuizIdByTopicName: async function (topicName) {
try {
const querySnapshot = await getDocs(query(collection(database, 'quizzesCreation'), where('name', '==', topicName)));
if (!querySnapshot.empty) {
return querySnapshot.docs[0].id;
}
return null;
} catch (error) {
console.error("Error fetching set ID by topic name:", error);
return null;
}
},
// get the flashcardSet Id by the quiz Id
/**
* @memberof FlashcardRepo
* @function getSetIdByQuizId
* @description Retrieves the ID of the flashcard set associated with a quiz.
* @param {string} quizId - The ID of the quiz.
* @returns {Promise<string|null>} A promise that resolves to the flashcard set ID or null if not found.
*/
getSetIdByQuizId: async function (quizId) {
try {
const setDoc = await getDoc(doc(database, 'quizzesCreation', quizId));
const data = setDoc.data();
return data?.flashcardSetId || '';
} catch (error) {
console.error("Error fetching flashcard set:", error);
return null;
}
},
// this will create new quiz by referencing to the flashcardsets id
/**
* @memberof FlashcardRepo
* @function createNewQuiz
* @description Creates a new quiz associated with a flashcard set.
* @param {string} flashcardSetId - The ID of the flashcard set.
* @param {string} quizTitle - The title of the new quiz.
* @returns {Promise<string>} A promise that resolves to the ID of the newly created quiz.
*/
createNewQuiz: async function (flashcardSetId, quizTitle) {
try {
// Get the flashcard set reference
const flashcardSetRef = doc(database, 'flashcardSets', flashcardSetId);
// Get the current flashcard set data
const flashcardSetSnapshot = await getDoc(flashcardSetRef);
const flashcardSetData = flashcardSetSnapshot.data();
// Extract topic name and subject from flashcard set data
const flashcardTopicName = flashcardSetData.name;
const flashcardSubject = flashcardSetData.subject;
// Generate a new quiz ID
const quizId = doc(collection(database, 'quizzes')).id;
const scoreId = doc(collection(database, 'scores')).id;
// Create the initial quiz item
const initialQuizItems = {
[quizId]: {
question: "Sample Question",
choices: ["Choice 1", "Choice 2", "Choice 3", "Choice 4"],
correctChoiceIndex: 0,
},
};
const initialScore = {
[scoreId]: {
score: 0,
attempt: increment(0),
},
};
const setData = {
name: flashcardTopicName, //Add the flashcard topic name to the data
quizName: quizTitle, // Add a quiz title to each quiz
createdAt: Timestamp.now(),
authorId: this.getCurrentUid(),
subject: flashcardSubject,
flashcardSetId: flashcardSetId, // Add the flashcard set ID to the data
questionItems: initialQuizItems,
};
const newDocRef = await addDoc(collection(database, 'quizzesCreation'), setData);
console.log("New Quiz is created with ID:", newDocRef.id);
const uid = this.getCurrentUid();
if (uid) {
await this.addOwnedQuizSetToUser(uid, newDocRef.id);
}
return newDocRef.id;
} catch (error) {
console.error("Error creating flashcard set:", error);
throw error;
}
},
// this will update the quiz title in the database accordingly
/**
* @memberof FlashcardRepo
* @function updateQuizTitle
* @description Updates the title of a quiz.
* @param {string} quizId - The ID of the quiz to update.
* @param {string} newTitle - The new title of the quiz.
* @returns {Promise<void>}
*/
updateQuizTitle: async function (quizId, newTitle) {
try {
const quizSetRef = doc(database, 'quizzesCreation', quizId);
const snap = await getDoc(quizSetRef);
if (snap.exists()) {
// update the quiz title
await updateDoc(quizSetRef, {
quizName: newTitle
});
console.log(`Flashcard set with ID ${quizId} updated successfully with new name: ${newTitle}.`);
} else {
console.log(`Flashcard set with ID ${quizId} not found.`);
}
} catch (error) {
console.error("Error updating flashcard set name:", error);
throw error;
}
},
// this will delete a quiz from the database accordingly
/**
* @memberof FlashcardRepo
* @function deleteQuiz
* @description Deletes a quiz.
* @param {string} quizIdToBeDeleted - The ID of the quiz to be deleted.
* @param {string} uid - The UID of the user performing the action.
* @returns {Promise<void>}
*/
deleteQuiz: async function (quizIdToBeDeleted, uid) {
const quizSetRef = doc(database, 'quizzesCreation', quizIdToBeDeleted);
try {
// delete the selected quiz
await deleteDoc(quizSetRef);
console.log('Quiz is deleted successfully');
} catch (error) {
console.error('Error deleting quiz:', error);
throw error;
}
},
// this will remove the owned quiz from the user
/**
* @memberof FlashcardRepo
* @function removeOwnedQuizFromUser
* @description Removes an owned quiz from a user's list.
* @param {string} uid - The UID of the user.
* @param {string} quizIdToBeDeleted - The ID of the quiz to be removed.
* @returns {Promise<void>}
*/
removeOwnedQuizFromUser: async function (uid, quizIdToBeDeleted) {
try {
const userSetRef = doc(database, 'users', uid);
const snap = await getDoc(userSetRef);
// check if the users table exists with the id
if (snap.exists()) {
const data = snap.data();
if (Array.isArray(data['ownedQuizzes']) && data['ownedQuizzes'].includes(quizIdToBeDeleted)) {
// remove the item from the array
data['ownedQuizzes'] = data['ownedQuizzes'].filter(item => item !== quizIdToBeDeleted);
// update the document with the modified data
await updateDoc(userSetRef, { ownedQuizzes: data['ownedQuizzes'] });
console.log(`Quiz ID ${quizIdToBeDeleted} is removed from user ${uid} successfully.`);
} else {
console.log(`Quiz ID ${quizIdToBeDeleted} not found in ownedQuizzes.`);
}
} else {
console.log(`User with ID ${uid} does not exist.`);
}
} catch (error) {
console.error("Error deleting quiz", error);
throw error;
}
},
getFlashcardItemsByStatus: async function (setId, status) {
try {
const setRef = doc(database, 'flashcardSets', setId);
const setSnapshot = await getDoc(setRef);
const setData = setSnapshot.data();
const flashcardData = setData.flashcardItems || [];
// if flashcardData is an object, convert it to an array
const flashcardArray = Object.values(flashcardData);
//console.log('Converted flashcardData to array:', flashcardArray);
// filter flashcards based on the status field
const flashcardsWithStatus = flashcardArray.filter(flashcard => flashcard.status === status);
//console.log("Filtered flashcards are: ", flashcardsWithStatus);
return flashcardsWithStatus;
} catch (error) {
console.error("Error getting flashcard items:", error);
throw error;
}
},
// this will add the user score to the db as well as the attempt
updateScoreAndAddAttempt: async function (quizId, score) {
try {
// create the attempt data
const scoreData = {
attempt: 1, // initialize attempt to 1 for the first attempt
score: score, // the calculated score
submitAt: Timestamp.now(), // capture the time the user submitted the quiz
};
// get a reference to the quiz document in the 'quizzesCreation' collection
const quizRef = doc(database, 'quizzesCreation', quizId);
const snap = await getDoc(quizRef);
const data = snap.data();
let quizScore = data.quizScore || {};
// check if there are existing attempts
const existingAttempts = Object.values(quizScore).length;
// increment the attempt field by 1
if (existingAttempts > 0) {
scoreData.attempt = increment(existingAttempts);
}
// get a new ID for the attempt
const scoreId = doc(collection(database, 'score')).id;
// add the new score data to the quizScore object
quizScore[scoreId] = scoreData;
console.log("Adding new score to DB:", quizScore);
// update the quiz document with the updated quizScore
await updateDoc(quizRef, {
quizScore: quizScore
});
return scoreId;
} catch (error) {
console.error("Error adding score", error);
throw error;
}
},
//get score and attempt date
getQuizAttemptsForUser: async function (userId) {
const quizAttempts = [];
try {
//reference to the user's quiz scores
const scoresRef = collection(database, 'score');
const q = query(scoresRef, where("userId", "==", userId));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
const data = doc.data();
quizAttempts.push({
score: data.score,
submitAt: data.submitAt.toDate().toLocaleString(),
});
});
} catch (error) {
console.error("Error fetching user's quiz attempts:", error);
}
return quizAttempts;
},
};
export default FlashcardRepo;