import React, { useEffect, useState } from 'react';
import FlashcardRepo from '../repositories/FlashcardRepo';
import { useParams, useNavigate } from 'react-router-dom';
import {
Box,
Drawer,
Button,
List,
Divider,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
TextField,
IconButton,
Menu,
MenuItem,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Typography,
} from '@mui/material';
import QuizIcon from '@mui/icons-material/Quiz';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import ListAltIcon from '@mui/icons-material/ListAlt';
/**
* @class QuizList
* @classdesc QuizList - A functional React component that provides an interface for displaying and interacting with a list of quizzes.
*
* @param {Object} props - Properties passed to the component including newQuizAdded.
* @returns {React.Component} A component displaying a list of quizzes.
*/
function QuizList({ newQuizAdded }) {
const [state, setState] = React.useState({
left: false,
});
const navigate = useNavigate();
const [quizList, setQuizList] = useState([]); // store the quiz title that retreived from database
const { setId, quizId } = useParams(); // retrieve the flashcard set in order to go to a certain quiz
const [currentQuiz, setCurrentQuiz] = useState(null); // capture the current quizID in order to show by def
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
const [currentlyEditingTitle, setCurrentlyEditingTitle] = useState(null); // store the quiz title for user editting
const [editedTitle, setEditedTitle] = useState(''); // store the user input which is the edited quiz title
const [isEditDialogOpen, setEditDialogOpen] = useState(false);
const [deleteQuiz, setDeleteQuiz] = useState(null);
const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [isDrawerOpen, setIsDrawerOpen] = useState(false); // state to keep the drawer open/close
useEffect(() => {
const fetchQuizTitle = async () => {
try {
const quizData = await FlashcardRepo.getQuizTitleFromFlashcardSet(setId);
console.log("Fetching Quiz Title:", quizData);
// check if newQuizAdded is provided and it's not already in quizList
if (newQuizAdded && !quizList.includes(newQuizAdded)) {
setQuizList(prevList => [...prevList, newQuizAdded]);
// reset the newQuizAdded prop
// this is crucial to prevent continuous addition of the same quiz title
newQuizAdded = null;
} else {
setQuizList(quizData);
}
setQuizList(quizData);
} catch (error) {
console.error("Failed to fetch flashcards:", error);
}
};
fetchQuizTitle();
}, [setId, quizId, navigate, newQuizAdded]);
// navigate to quiz page by passing flashcardSet ID as parameter
/**
* @memberof QuizList
* @function handleQuizTitleClick
* @description Handles click event on a quiz title, navigating to the quiz page.
* @param {string} quizName - Name of the quiz to navigate to.
*/
const handleQuizTitleClick = async (quizName) => {
console.log("The after rendered Set ID: ", setId);
console.log("The after rendered Set ID: ", quizId);
try {
const quizId = await FlashcardRepo.getQuizTitleId(quizName, setId);
//const setId = await FlashcardRepo.getSetIdByQuizId(quizId);
if (quizId && setId) {
console.log("Your Set Id is: ", setId);
console.log("Your Quiz Id is: ", quizId);
console.log("Navigating to: ", `/quizFlash/${setId}/quiz/${quizId}`);
navigate(`/quizFlash/${setId}/quiz/${quizId}`);
} else {
console.error("Unable to fetch set ID for topic:", quizName);
}
} catch (error) {
console.error("Error in handleFlashcardClick:", error);
}
};
/**
* @memberof QuizList
* @function toggleDrawer
* @description Toggles the visibility of the navigation drawer.
* @param {string} anchor - Drawer anchor position.
* @param {boolean} open - Indicates if the drawer should be open or closed.
*/
const toggleDrawer = (anchor, open) => (event) => {
if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) {
return;
}
// check if the dialog is open, and if yes, do not close the drawer
if (isEditDialogOpen || isDeleteDialogOpen) {
return;
}
setIsDrawerOpen(open);
setState({ ...state, [anchor]: open });
};
// capture the quiz title data when the user clicked
/**
* @memberof QuizList
* @function handleClick
* @description Handles click event for quiz options, setting state for current quiz.
* @param {Object} event - The triggered event.
* @param {string} title - Title of the quiz for which options are being opened.
*/
const handleClick = (event, title) => {
// prevent the drawer from closing when the MoreVertIcon is clicked
event.stopPropagation();
setCurrentlyEditingTitle(title);
setEditedTitle(title);
setDeleteQuiz(title);
setAnchorEl(event.currentTarget);
console.log("You have clicked on: ", title);
};
// set the Anchor to null
/**
* @memberof QuizList
* @function handleClose
* @description Closes the options menu by setting its anchor element to null.
*/
const handleClose = () => {
setAnchorEl(null);
};
// handle open the edit dialog
/**
* @memberof QuizList
* @function handleOpenEditDialog
* @description Opens the edit dialog for editing a quiz title.
*/
const handleOpenEditDialog = () => {
setEditDialogOpen(true);
};
// handle close the edit dialog
/**
* @memberof QuizList
* @function handleCloseEditDialog
* @description Closes the edit dialog for editing a quiz title.
*/
const handleCloseEditDialog = () => {
setEditDialogOpen(false);
};
// handle open the deleting dialog
/**
* @memberof QuizList
* @function handleOpenDeleteDialog
* @description Opens the delete confirmation dialog for a selected quiz.
*/
const handleOpenDeleteDialog = () => {
setDeleteDialogOpen(true);
};
// handle close the deleting dialog
/**
* @memberof QuizList
* @function handleCloseDeleteDialog
* @description Closes the delete confirmation dialog.
*/
const handleCloseDeleteDialog = () => {
setDeleteDialogOpen(false);
};
// this handler will edit the quiz title
/**
* @memberof QuizList
* @function handleEditQuizTitle
* @description Handles the updating of a quiz title, including updating the database and local state.
*/
const handleEditQuizTitle = async () => {
console.log('Rename is clicked!');
if (editedTitle.trim() === "" || !currentlyEditingTitle) {
console.log('Empty edited name or no topic being edited, exiting');
return;
}
try {
const quizId = await FlashcardRepo.getQuizTitleId(currentlyEditingTitle, setId);
console.log('Quiz ID by quiz title:', quizId);
// Update the Firebase database
await FlashcardRepo.updateQuizTitle(quizId, editedTitle.trim());
// Update the local state
const updatedQuizTitle = quizList.map(t => t === currentlyEditingTitle ? editedTitle.trim() : t);
console.log('Updated Quiz Title:', updatedQuizTitle);
setQuizList(updatedQuizTitle);
setCurrentlyEditingTitle(null);
setEditedTitle('');
setIsDrawerOpen(true); // keep the drawer open
} catch (error) {
console.error("Error editing flashcard set name:", error);
}
handleCloseEditDialog();
};
//delete the question and update the question data (setQuizData(updateQuiz))
/**
* @memberof QuizList
* @function handleDeleteQuiz
* @description Deletes a quiz from the database and updates the local state to reflect this change.
*/
const handleDeleteQuiz = async () => {
const uid = FlashcardRepo.getCurrentUid();
// get the selected quiz id for the deletion
const quizIdToDelete = await FlashcardRepo.getQuizTitleId(deleteQuiz, setId);
console.log("This is the quiz id you want to delete?: ", quizIdToDelete);
if (deleteQuiz) {
try {
await FlashcardRepo.deleteQuiz(quizIdToDelete);
await FlashcardRepo.removeOwnedQuizFromUser(uid, quizIdToDelete);
//update the quiz list so the deleted quiz doesnt show in the drawer
const updatedQuiz = quizList.filter(quiz => quiz !== deleteQuiz);
setQuizList(updatedQuiz);
setDeleteQuiz(null);
setIsDrawerOpen(true); // keep the drawer open
} catch (error) {
console.error("Failed to delete flashcard:", error);
}
setDeleteDialogOpen(false);
}
};
const list = (anchor) => (
<Box
sx={{ width: anchor === 'top' || anchor === 'bottom' ? 'auto' : 250 }}
role="presentation"
onClick={toggleDrawer(anchor, false)}
onKeyDown={toggleDrawer(anchor, false)}
>
<h3>List of Quiz</h3>
<Divider />
<List>
{quizList.map((quizTitle) => (
<ListItem key={quizTitle} disablePadding>
<ListItemButton onClick={() => handleQuizTitleClick(quizTitle)}>
<ListItemIcon>
<QuizIcon />
</ListItemIcon>
<ListItemText primary={quizTitle} />
</ListItemButton>
<IconButton
id="quiz-dropdown-button"
aria-controls={open ? 'quiz-dropdown-menu' : undefined}
aria-haspopup="true"
aria-expanded={open ? 'true' : undefined}
onClick={(event) => handleClick(event, quizTitle)}>
<MoreVertIcon />
</IconButton>
</ListItem>
))}
<Menu
id="quiz-dropdown-menu"
aria-labelledby="quiz-dropdown-button"
anchorEl={anchorEl}
open={open}
onClose={handleClose}>
<MenuItem
onClick={() => {
handleOpenEditDialog();
handleClose();
}}>
Rename
</MenuItem>
<MenuItem onClick={() => {
handleOpenDeleteDialog();
handleClose();
}}>
Delete
</MenuItem>
</Menu>
</List>
</Box>
);
return (
<div>
{['left'].map((anchor) => (
<React.Fragment key={anchor}>
<Button
variant="contained"
onClick={toggleDrawer(anchor, true)}
startIcon={<ListAltIcon />}
>Quiz List</Button>
<Drawer
anchor={anchor}
open={state[anchor] || isDrawerOpen || isEditDialogOpen || isDeleteDialogOpen}
onClose={toggleDrawer(anchor, false)}
>
{list(anchor)}
</Drawer>
</React.Fragment>
))}
<Dialog open={isEditDialogOpen} onClose={handleCloseEditDialog}>
<DialogTitle>Confirmation</DialogTitle>
<DialogContent>
<TextField
autoFocus
margin="dense"
label="Enter a New Quiz Title"
fullWidth
value={editedTitle}
onChange={(e) => setEditedTitle(e.target.value)}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleCloseEditDialog} color="primary">
Cancel
</Button>
<Button onClick={handleEditQuizTitle} color="primary">
Save
</Button>
</DialogActions>
</Dialog>
<Dialog open={isDeleteDialogOpen} onClose={handleCloseDeleteDialog}>
<DialogTitle>Confirmation</DialogTitle>
<DialogContent>
<Typography>Are you sure you want to delete this quiz?</Typography>
</DialogContent>
<DialogActions>
<Button onClick={handleCloseDeleteDialog} color="primary">
Cancel
</Button>
<Button
onClick={handleDeleteQuiz}
color="primary">
Confirm
</Button>
</DialogActions>
</Dialog>
</div>
);
}
export default QuizList;