Repository: gil392/Quizzer_Frontend
Pull request #8: 7 history page
Author: login: gil392
Created: 2025-04-16T19:30:10Z | Merged: 2025-04-27T20:43:59Z

to see it: path /lesson


Diff:
diff --git a/package.json b/package.json
index b1e3388..38f4a4e 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
   "dependencies": {
     "@emotion/react": "^11.14.0",
     "@emotion/styled": "^11.14.0",
+    "@mui/icons-material": "^7.0.2",
     "@mui/material": "^7.0.1",
     "@mui/styles": "^6.4.11",
     "react": "^18.3.1",
diff --git a/src/App.tsx b/src/App.tsx
index 0defc0e..9dcdc9c 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,24 +1,27 @@
-import { Navigate, Route, BrowserRouter as Router, Routes } from 'react-router-dom'
-import './App.css'
-import SummaryPage from './pages/Summary'
-import QuizPage from './pages/Quiz';
-import HomePage from './pages/home/Home'
-
+import {
+  Navigate,
+  Route,
+  BrowserRouter as Router,
+  Routes,
+} from "react-router-dom";
+import "./App.css";
+import SummaryPage from "./pages/Summary";
+import QuizPage from "./pages/Quiz";
+import HomePage from "./pages/home/Home";
+import LessonsPage from "./pages/lesson/LessonsPage/LessonsPage";
 
 function App() {
-
   return (
     <Router>
-            <Routes>
-                <Route path="/" element={<Navigate to="/home" replace />} />
-                <Route path="/home" element={<HomePage />} />
-                <Route path="/summary" element={<SummaryPage />} />
-                <Route path="/quiz" element={<QuizPage />}
-                />
-            </Routes>
+      <Routes>
+        <Route path="/" element={<Navigate to="/lesson" replace />} />
+        <Route path="/home" element={<HomePage />} />
+        <Route path="/summary" element={<SummaryPage />} />
+        <Route path="/quiz" element={<QuizPage />} />
+        <Route path="/lesson/*" element={<LessonsPage />} />
+      </Routes>
     </Router>
-    
-  )
+  );
 }
 
-export default App
+export default App;
diff --git a/src/components/EditableTitleWithActions.styles.ts b/src/components/EditableTitleWithActions.styles.ts
new file mode 100644
index 0000000..4fe381a
--- /dev/null
+++ b/src/components/EditableTitleWithActions.styles.ts
@@ -0,0 +1,29 @@
+import { createStyles } from "@mui/styles";
+
+const styles = createStyles({
+  root: {
+    display: "flex",
+    alignItems: "center",
+    justifyContent: "space-between",
+    flexGrow: 1,
+    overflow: "hidden",
+  },
+  textField: {
+    marginRight: "1rem",
+    flexGrow: 1,
+  },
+  typography: {
+    overflow: "hidden",
+    textOverflow: "ellipsis",
+    whiteSpace: "nowrap",
+    marginRight: "1rem",
+  },
+  iconsContainer: {
+    display: "flex",
+    alignItems: "center",
+    flexShrink: 0,
+    gap: "0.5rem",
+  },
+});
+
+export default styles;
diff --git a/src/components/EditabletitleWithActions.tsx b/src/components/EditabletitleWithActions.tsx
new file mode 100644
index 0000000..1d24d60
--- /dev/null
+++ b/src/components/EditabletitleWithActions.tsx
@@ -0,0 +1,102 @@
+import { FunctionComponent, useState } from "react";
+import { TextField, Box, Typography } from "@mui/material";
+import EditIcon from "@mui/icons-material/Edit";
+import CheckIcon from "@mui/icons-material/Check";
+import CloseIcon from "@mui/icons-material/Close";
+import DeleteIcon from "@mui/icons-material/Delete";
+import { GenericIconButton } from "./GenericIconButton";
+import styles from "./EditableTitleWithActions.styles";
+import { withStyles, WithStyles } from "@mui/styles";
+
+interface EditableTitleWithActionsProps extends WithStyles<typeof styles> {
+  title: string;
+  onSave: (newTitle: string) => void;
+  onDelete: () => void;
+  onEditModeChange?: (isEditing: boolean) => void;
+}
+
+const EditableTitleWithActions: FunctionComponent<
+  EditableTitleWithActionsProps
+> = ({
+  title,
+  onSave,
+  onDelete,
+  onEditModeChange,
+  classes,
+}: EditableTitleWithActionsProps) => {
+  const [isEditing, setIsEditing] = useState(false);
+  const [newTitle, setNewTitle] = useState(title);
+
+  const handleEditClick = (event: React.MouseEvent) => {
+    event.stopPropagation();
+    setIsEditing(true);
+    onEditModeChange?.(true);
+  };
+
+  const handleSaveClick = (event: React.MouseEvent) => {
+    event.stopPropagation();
+    onSave(newTitle);
+    setIsEditing(false);
+    onEditModeChange?.(false);
+  };
+
+  const handleCancelClick = (event: React.MouseEvent) => {
+    event.stopPropagation();
+    setNewTitle(title);
+    setIsEditing(false);
+    onEditModeChange?.(false);
+  };
+
+  const handleDeleteClick = (event: React.MouseEvent) => {
+    event.stopPropagation();
+    onDelete();
+  };
+
+  return (
+    <Box className={classes.root}>
+      {isEditing ? (
+        <TextField
+          value={newTitle}
+          onChange={(e) => setNewTitle(e.target.value)}
+          size="small"
+          className={classes.textField}
+        />
+      ) : (
+        <Typography variant="h6" className={classes.typography}>
+          {title}
+        </Typography>
+      )}
+      <Box className={classes.iconsContainer}>
+        {isEditing ? (
+          <>
+            <GenericIconButton
+              icon={<CheckIcon />}
+              title={"Save"}
+              onClick={handleSaveClick}
+            />
+            <GenericIconButton
+              icon={<CloseIcon />}
+              title={"Cancel"}
+              onClick={handleCancelClick}
+            />
+          </>
+        ) : (
+          <>
+            <GenericIconButton
+              icon={<EditIcon />}
+              title={"Edit"}
+              onClick={handleEditClick}
+            />
+            <GenericIconButton
+              icon={<DeleteIcon />}
+              title={"Delete"}
+              onClick={handleDeleteClick}
+            />
+          </>
+        )}
+      </Box>
+    </Box>
+  );
+};
+
+export default withStyles(styles)(EditableTitleWithActions);
diff --git a/src/components/GenericIconButton.tsx b/src/components/GenericIconButton.tsx
new file mode 100644
index 0000000..d5f7c09
--- /dev/null
+++ b/src/components/GenericIconButton.tsx
@@ -0,0 +1,26 @@
+import React, { FunctionComponent } from "react";
+import { IconButton, Tooltip } from "@mui/material";
+
+interface GenericIconButtonProps {
+  title: string;
+  icon: React.ReactElement;
+  onClick?: (event: React.MouseEvent) => void;
+}
+
+export const GenericIconButton: FunctionComponent<GenericIconButtonProps> = ({
+  title,
+  icon,
+  onClick,
+}) => {
+  return (
+    <Tooltip title={title} arrow>
+      <IconButton
+        aria-label={title}
+        style={{ outline: "none" }}
+        onClick={onClick}
+      >
+        {icon}
+      </IconButton>
+    </Tooltip>
+  );
+};
diff --git a/src/hooks/usePopupNavigation.ts b/src/hooks/usePopupNavigation.ts
new file mode 100644
index 0000000..df88532
--- /dev/null
+++ b/src/hooks/usePopupNavigation.ts
@@ -0,0 +1,34 @@
+import { useState, useEffect } from "react";
+import { useNavigate, useLocation } from "react-router-dom";
+
+export const usePopupNavigation = (
+  basePath: string,
+  popupPath: string,
+  onClosePopup?: () => void
+) => {
+  const [isPopupOpen, setIsPopupOpen] = useState(false);
+  const navigate = useNavigate();
+  const location = useLocation(); // current URL path
+
+  useEffect(() => {
+    const isPopupOpen = location.pathname === `${basePath}/${popupPath}`;
+    setIsPopupOpen(isPopupOpen);
+  }, [location, basePath, popupPath]);
+
+  useEffect(() => {
+    !isPopupOpen && onClosePopup?.();
+  }, [isPopupOpen]);
+
+  const openPopup = () => {
+    setIsPopupOpen(true);
+    navigate(`${basePath}/${popupPath}`);
+  };
+
+  const closePopup = () => {
+    setIsPopupOpen(false);
+    navigate(basePath);
+    onClosePopup?.();
+  };
+
+  return { isPopupOpen, openPopup, closePopup };
+};
diff --git a/src/pages/lesson/LessonInfo/LessonInfo.styles.ts b/src/pages/lesson/LessonInfo/LessonInfo.styles.ts
new file mode 100644
index 0000000..a81ba59
--- /dev/null
+++ b/src/pages/lesson/LessonInfo/LessonInfo.styles.ts
@@ -0,0 +1,41 @@
+import { makeStyles, createStyles } from "@mui/styles";
+
+const useStyles = makeStyles(() =>
+  createStyles({
+    container: {
+      display: "flex",
+      gap: "2vw",
+      maxWidth: "100%",
+      overflowX: "hidden",
+    },
+    leftBox: {
+      flex: 6,
+      minWidth: 0, // allows box to shrink if needed, to let overflow work
+    },
+    rightBox: {
+      flex: 4,
+      minWidth: 0, // allows box to shrink if needed, to let overflow work
+    },
+    title: {
+      fontSize: "1.2rem",
+      fontWeight: "bold",
+      marginBottom: "0.5vh",
+    },
+    card: {
+      width: "100%",
+    },
+    collapseContent: {
+      width: "100%",
+    },
+    noQuizzesText: {
+      marginBottom: "1rem",
+    },
+    buttonsContainer: {
+      display: "flex",
+      gap: "1rem",
+      justifyContent: "center",
+    },
+  })
+);
+
+export default useStyles;
diff --git a/src/pages/lesson/LessonInfo/LessonInfo.tsx b/src/pages/lesson/LessonInfo/LessonInfo.tsx
new file mode 100644
index 0000000..b4e920e
--- /dev/null
+++ b/src/pages/lesson/LessonInfo/LessonInfo.tsx
@@ -0,0 +1,135 @@
+import Box from "@mui/material/Box";
+import {
+  LessonData,
+  QuizData,
+  QuizSettings,
+} from "../../../services/backend/types";
+import { useState, useEffect } from "react";
+import {
+  deleteQuiz,
+  getQuizzes,
+  updateQuiz,
+} from "../../../services/backend/service";
+import QuizItem from "../QuizItem/QuizItem";
+import {
+  Card,
+  CardContent,
+  CardActions,
+  Button,
+  Typography,
+  Collapse,
+} from "@mui/material";
+import { useNavigate } from "react-router-dom";
+import useStyles from "./LessonInfo.styles";
+
+interface LessonInfoProps {
+  lesson: LessonData;
+  onClose: () => void;
+}
+
+const LessonInfo: React.FC<LessonInfoProps> = ({ lesson, onClose }) => {
+  const classes = useStyles();
+  const [quizzes, setQuizzes] = useState<QuizData[]>([]);
+  const [isSummaryExpanded, setIsSummaryExpanded] = useState(true); // Default to true (show summary)
+
+  const navigate = useNavigate();
+  /** TODO: Issue 14 (Itay)- get these settings not hard-coded */
+  const quizSettings: QuizSettings = {
+    checkType: "onSubmit",
+    isRandomOrder: true,
+    maxQuestionCount: 10,
+    solvingTimeMs: 60000,
+  };
+
+  const onCreateQuiz = () => {
+    navigate("/quiz", { state: { lessonData: lesson, quizSettings } });
+  };
+
+  useEffect(() => {
+    const fetchQuizzesByLessonId = async () => {
+      try {
+        const response = await getQuizzes(lesson._id);
+        setQuizzes(response);
+      } catch (error) {
+        console.error("Error fetching lessons:", error);
+      }
+    };
+
+    fetchQuizzesByLessonId();
+  }, []);
+
+  const handleDeleteQuiz = async (quizId: string) => {
+    await deleteQuiz(quizId);
+    setQuizzes((prevQuizzes) =>
+      prevQuizzes.filter((quiz) => quiz._id !== quizId)
+    );
+  };
+
+  const handleUpdateQuiz = async (quiz: QuizData) => {
+    await updateQuiz(quiz._id, quiz);
+    setQuizzes((prevQuizzes) =>
+      prevQuizzes.map((q) => (q._id === quiz._id ? quiz : q))
+    );
+  };
+
+  return (
+    <Box className={classes.container}>
+      <Box className={classes.leftBox}>
+        <Typography className={classes.title}>{lesson.title}</Typography>
+        <Card className={classes.card}>
+          <CardActions>
+            <Button
+              size="small"
+              onClick={() => setIsSummaryExpanded(!isSummaryExpanded)}
+            >
+              {isSummaryExpanded ? "Hide Summary" : "Show Summary"}
+            </Button>
+          </CardActions>
+          <Collapse
+            in={isSummaryExpanded}
+            timeout="auto"
+            unmountOnExit
+            className={classes.collapseContent}
+          >
+            <CardContent>
+              <Typography variant="body2">{lesson.summary}</Typography>
+            </CardContent>
+          </Collapse>
+        </Card>
+      </Box>
+
+      <Box className={classes.rightBox}>
+        {quizzes.length > 0 ? (
+          quizzes.map((quiz) => (
+            <QuizItem
+              key={quiz._id}
+              quiz={quiz}
+              deleteQuiz={() => handleDeleteQuiz(quiz._id)}
+              updateQuizTitle={(newTitle: string) => {
+                handleUpdateQuiz({ ...quiz, title: newTitle });
+              }}
+            />
+          ))
+        ) : (
+          <Typography
+            variant="body2"
+            color="text.secondary"
+            className={classes.noQuizzesText}
+          >
+            No quizzes available for this lesson.
+          </Typography>
+        )}
+        <Box className={classes.buttonsContainer}>
+          <Button variant="outlined" color="inherit" onClick={onCreateQuiz}>
+            Create New Quiz
+          </Button>
+          <Button variant="outlined" color="inherit" onClick={onClose}>
+            Close
+          </Button>
+        </Box>
+      </Box>
+    </Box>
+  );
+};
+
+export default LessonInfo;
diff --git a/src/pages/lesson/LessonItem/LessonItem.styles.ts b/src/pages/lesson/LessonItem/LessonItem.styles.ts
new file mode 100644
index 0000000..f4b81b2
--- /dev/null
+++ b/src/pages/lesson/LessonItem/LessonItem.styles.ts
@@ -0,0 +1,35 @@
+import { createStyles } from "@mui/styles";
+
+const styles = createStyles({
+  lessonItem: {
+    padding: "2px",
+    margin: "1px 0",
+    boxShadow: "2px 2px 5px rgba(0, 0, 0, 0.2)",
+    borderRadius: "2px",
+    transition: "0.3s",
+    width: "40vw",
+    marginTop: "2vh",
+    marginBottom: "2vh",
+    backgroundColor: "#fae1e4",
+  },
+  lessonItemHover: {
+    "&:hover": {
+      boxShadow: "4px 4px 10px rgba(0, 0, 0, 0.3)",
+      backgroundColor: "#ffc0cb",
+      cursor: "pointer",
+    },
+  },
+  flexContainer: {
+    display: "flex",
+    justifyContent: "space-between",
+    alignItems: "center",
+    paddingLeft: "0.5vw",
+  },
+  successRateText: {
+    marginTop: "0.5rem",
+    paddingLeft: "0.5vw",
+    textAlign: "left",
+  },
+});
+
+export default styles;
diff --git a/src/pages/lesson/LessonItem/LessonItem.tsx b/src/pages/lesson/LessonItem/LessonItem.tsx
new file mode 100644
index 0000000..400b3a5
--- /dev/null
+++ b/src/pages/lesson/LessonItem/LessonItem.tsx
@@ -0,0 +1,44 @@
+import Box from "@mui/material/Box";
+import { LessonData } from "../../../services/backend/types";
+import EditableTitleWithActions from "../../../components/EditabletitleWithActions";
+import { FunctionComponent, useState } from "react";
+import { Typography } from "@mui/material";
+import styles from "./LessonItem.styles";
+import withStyles, { WithStyles } from "@mui/styles/withStyles/withStyles";
+
+interface LessonItemProps extends WithStyles<typeof styles> {
+  lesson: LessonData;
+  onLessonDeleted: (lessonId: string) => void;
+  updateLessonTitle: (newTitle: string) => void;
+  openLesson: () => void;
+}
+
+const LessonItem: FunctionComponent<LessonItemProps> = (
+  props: LessonItemProps
+) => {
+  const [isEditing, setIsEditing] = useState(false);
+  const classes = props.classes;
+
+  return (
+    <Box
+      className={`${classes.lessonItem} ${
+        !isEditing ? classes.lessonItemHover : ""
+      }`}
+      onClick={() => !isEditing && props.openLesson()}
+    >
+      <Box className={classes.flexContainer}>
+        <EditableTitleWithActions
+          title={props.lesson.title}
+          onSave={(newTitle) => props.updateLessonTitle(newTitle)}
+          onDelete={() => props.onLessonDeleted(props.lesson._id)}
+          onEditModeChange={(isEditing) => setIsEditing(isEditing)}
+        />
+      </Box>
+      <Typography className={classes.successRateText}>
+        Success rate: 100%
+      </Typography>
+    </Box>
+  );
+};
+
+export default withStyles(styles)(LessonItem);
diff --git a/src/pages/lesson/LessonsPage/LessonsPage.styles.ts b/src/pages/lesson/LessonsPage/LessonsPage.styles.ts
new file mode 100644
index 0000000..da3acee
--- /dev/null
+++ b/src/pages/lesson/LessonsPage/LessonsPage.styles.ts
@@ -0,0 +1,19 @@
+import { createStyles } from "@mui/styles";
+
+const styles = createStyles({
+  headerContainer: {
+    display: "flex",
+    justifyContent: "space-between",
+    alignItems: "center",
+    marginBottom: "1rem",
+  },
+  title: {
+    flexGrow: 1,
+    textAlign: "center",
+  },
+  noLessonsText: {
+    marginBottom: "1rem",
+  },
+});
+
+export default styles;
diff --git a/src/pages/lesson/LessonsPage/LessonsPage.tsx b/src/pages/lesson/LessonsPage/LessonsPage.tsx
new file mode 100644
index 0000000..c92583f
--- /dev/null
+++ b/src/pages/lesson/LessonsPage/LessonsPage.tsx
@@ -0,0 +1,114 @@
+import { FunctionComponent, useEffect, useState } from "react";
+import { LessonData } from "../../../services/backend/types";
+import LessonItem from "../LessonItem/LessonItem";
+import {
+  deleteLesson,
+  getLessons,
+  updateLesson,
+} from "../../../services/backend/service";
+import LessonInfo from "../LessonInfo/LessonInfo";
+import Typography from "@mui/material/Typography";
+import { usePopupNavigation } from "../../../hooks/usePopupNavigation";
+import { Add } from "@mui/icons-material";
+import Box from "@mui/material/Box";
+import { useNavigate } from "react-router-dom";
+import { GenericIconButton } from "../../../components/GenericIconButton";
+import styles from "./LessonsPage.styles";
+import { withStyles, WithStyles } from "@mui/styles";
+
+interface LessonsPageProps extends WithStyles<typeof styles> {}
+
+const LessonsPage: FunctionComponent<LessonsPageProps> = (
+  props: LessonsPageProps
+) => {
+  const navigate = useNavigate();
+  const { classes } = props;
+  const [lessons, setLessons] = useState<LessonData[]>([]);
+  const [selectedLesson, setSelectedLesson] = useState<LessonData | null>(null);
+  const { openPopup, closePopup } = usePopupNavigation("/lesson", "info", () =>
+    setSelectedLesson(null)
+  );
+
+  useEffect(() => {
+    const fetchLessons = async () => {
+      try {
+        const response = await getLessons();
+        setLessons(response);
+      } catch (error) {
+        console.error("Error fetching lessons:", error);
+      }
+    };
+
+    fetchLessons();
+  }, []);
+
+  const handleLessonDeleted = async (lessonId: string) => {
+    await deleteLesson(lessonId);
+    setLessons((prevLessons) =>
+      prevLessons.filter((lesson) => lesson._id !== lessonId)
+    );
+  };
+
+  const handleUpdateLesson = async (lesson: LessonData) => {
+    await updateLesson(lesson._id, lesson);
+    setLessons((prevLessons) =>
+      prevLessons.map((lessonToCheck) =>
+        lessonToCheck._id === lesson._id ? lesson : lessonToCheck
+      )
+    );
+  };
+
+  const openLesson = (lesson: LessonData) => {
+    setSelectedLesson(lesson);
+    openPopup();
+  };
+
+  return (
+    <>
+      {selectedLesson !== null ? (
+        <LessonInfo lesson={selectedLesson} onClose={closePopup} />
+      ) : (
+        <>
+          <Box className={classes.headerContainer}>
+            <Typography variant="h4" className={classes.title}>
+              Lessons
+            </Typography>
+            {selectedLesson === null && (
+              <GenericIconButton
+                icon={<Add />}
+                onClick={() => {
+                  navigate("/home");
+                }}
+                title="Create a new lesson"
+              />
+            )}
+          </Box>
+
+          {lessons.length > 0 ? (
+            lessons.map((lesson) => (
+              <LessonItem
+                key={lesson._id}
+                lesson={lesson}
+                onLessonDeleted={handleLessonDeleted}
+                openLesson={() => openLesson(lesson)}
+                updateLessonTitle={(newTitle: string) => {
+                  handleUpdateLesson({ ...lesson, title: newTitle });
+                }}
+              />
+            ))
+          ) : (
+            <Typography
+              variant="body2"
+              color="text.secondary"
+              className={classes.noLessonsText}
+            >
+              No existing lessons.
+            </Typography>
+          )}
+        </>
+      )}
+    </>
+  );
+};
+
+export default withStyles(styles)(LessonsPage);
diff --git a/src/pages/lesson/QuizItem/QuizItem.styles.ts b/src/pages/lesson/QuizItem/QuizItem.styles.ts
new file mode 100644
index 0000000..fe75358
--- /dev/null
+++ b/src/pages/lesson/QuizItem/QuizItem.styles.ts
@@ -0,0 +1,23 @@
+import { createStyles } from "@mui/styles";
+
+const styles = createStyles({
+  container: {
+    marginBottom: "1rem",
+    border: "1px solid #ccc",
+    borderRadius: "8px",
+    padding: "1rem",
+  },
+  header: {
+    display: "flex",
+    justifyContent: "space-between",
+    alignItems: "center",
+  },
+  accordionDetails: {
+    marginBottom: "0.5rem",
+    display: "flex",
+    flexDirection: "column",
+    alignItems: "flex-start",
+  },
+});
+
+export default styles;
diff --git a/src/pages/lesson/QuizItem/QuizItem.tsx b/src/pages/lesson/QuizItem/QuizItem.tsx
new file mode 100644
index 0000000..a51cab4
--- /dev/null
+++ b/src/pages/lesson/QuizItem/QuizItem.tsx
@@ -0,0 +1,53 @@
+import Box from "@mui/material/Box";
+import {
+  Accordion,
+  AccordionSummary,
+  AccordionDetails,
+  Typography,
+} from "@mui/material";
+import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
+import { QuizData } from "../../../services/backend/types";
+import EditableTitleWithActions from "../../../components/EditabletitleWithActions";
+import styles from "./QuizItem.styles";
+import withStyles, { WithStyles } from "@mui/styles/withStyles/withStyles";
+
+interface QuizItemProps extends WithStyles<typeof styles> {
+  quiz: QuizData;
+  deleteQuiz: () => void;
+  updateQuizTitle: (newTitle: string) => void;
+}
+
+const QuizItem = ({
+  quiz,
+  deleteQuiz,
+  updateQuizTitle,
+  classes,
+}: QuizItemProps) => {
+  return (
+    <Box className={classes.container}>
+      <Box className={classes.header}>
+        <EditableTitleWithActions
+          title={quiz.title}
+          onSave={(newTitle) => updateQuizTitle(newTitle)}
+          onDelete={deleteQuiz}
+        />
+      </Box>
+      <Accordion>
+        <AccordionSummary expandIcon={<ExpandMoreIcon />}>
+          <Typography>Attempts</Typography>
+        </AccordionSummary>
+        <AccordionDetails>
+          {["Attempt 1", "Nice attempt"].map((attempt, index) => (
+            <Box key={index} className={classes.accordionDetails}>
+              <Typography variant="body1">
+                {index + 1}. {attempt}
+              </Typography>
+            </Box>
+          ))}
+        </AccordionDetails>
+      </Accordion>
+    </Box>
+  );
+};
+
+export default withStyles(styles)(QuizItem);
diff --git a/src/services/backend/service.ts b/src/services/backend/service.ts
index d6da988..4045e53 100644
--- a/src/services/backend/service.ts
+++ b/src/services/backend/service.ts
@@ -1,107 +1,235 @@
-import { LessonData, QuizData, QuizSettings, QuizAnswerSubmittion, QuizResult } from "./types";
+import {
+  LessonData,
+  QuizData,
+  QuizSettings,
+  QuizAnswerSubmittion,
+  QuizResult,
+} from "./types";
 
 const backendUrl = import.meta.env.VITE_BACKEND_URL;
 
 export async function getQuizById(quizId: string): Promise<QuizData> {
-    try {
-        const response = await fetch(`${backendUrl}/quiz/${quizId}`, {
-            method: "GET",
-        });
+  try {
+    const response = await fetch(`${backendUrl}/quiz/${quizId}`, {
+      method: "GET",
+    });
 
-        if (!response.ok) {
-            throw new Error("Failed to fetch quiz");
-        }
+    if (!response.ok) {
+      throw new Error("Failed to fetch quiz");
+    }
 
-        const data = await response.json();
+    const data = await response.json();
 
-        return data;
-    } catch (error) {
-        console.error("Error fetching quiz:", error);
-        throw error;
-    }
+    return data;
+  } catch (error) {
+    console.error("Error fetching quiz:", error);
+    throw error;
+  }
 }
 
 export async function generateLesson(videoUrl: string): Promise<LessonData> {
-    try {
-        const response = await fetch(`${backendUrl}/lesson`, {
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify({
-                videoUrl,
-            }),
-        });
-
-        if (!response.ok) {
-            throw new Error('Failed to generate lesson');
-        }
-
-        const data = await response.json();
-        console.log('Lesson generated:', data);
-
-        return data;
-
-    } catch (error) {
-        console.error('Error generating lesson:', error);
-
-        throw error;
+  try {
+    const response = await fetch(`${backendUrl}/lesson`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify({
+        videoUrl,
+      }),
+    });
+
+    if (!response.ok) {
+      throw new Error("Failed to generate lesson");
     }
+
+    const data = await response.json();
+    console.log("Lesson generated:", data);
+
+    return data;
+  } catch (error) {
+    console.error("Error generating lesson:", error);
+
+    throw error;
+  }
 }
 
+export async function generateQuiz(
+  lessonId: string,
+  settings?: QuizSettings
+): Promise<QuizData> {
+  try {
+    const response = await fetch(`${backendUrl}/quiz`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify({
+        lessonId,
+        settings,
+      }),
+    });
+
+    if (!response.ok) {
+      throw new Error("Failed to generate quiz");
+    }
+
+    const data = await response.json();
+    console.log("Quiz generated:", data);
 
-export async function generateQuiz(lessonId: string, settings?: QuizSettings): Promise<QuizData> {
-    try {
-        const response = await fetch(`${backendUrl}/quiz`, {
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify({
-                lessonId,
-                settings,
-            }),
-        });
+    return data;
+  } catch (error) {
+    console.error("Error generating quiz:", error);
 
-        if (!response.ok) {
-            throw new Error('Failed to generate quiz');
-        }
+    throw error;
+  }
+}
+
+export async function submitQuiz(
+  data: QuizAnswerSubmittion
+): Promise<QuizResult> {
+  try {
+    const response = await fetch(`${backendUrl}/quiz/submit`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify(data),
+    });
+
+    if (!response.ok) {
+      throw new Error("Failed to submit quiz");
+    }
 
-        const data = await response.json();
-        console.log('Quiz generated:', data);
+    const result = await response.json();
+    console.log("Quiz submitted:", result);
 
-        return data;
+    return result;
+  } catch (error) {
+    console.error("Error submitting quiz:", error);
 
-    } catch (error) {
-        console.error('Error generating quiz:', error);
+    throw error;
+  }
+}
 
-        throw error;
+export async function getLessons(): Promise<LessonData[]> {
+  try {
+    const response = await fetch(`${backendUrl}/lesson/`, {
+      method: "GET",
+      headers: {
+        "Content-Type": "application/json",
+      },
+    });
+
+    if (!response.ok) {
+      throw new Error("Failed to fetch lessons");
     }
+
+    const data = await response.json();
+    console.log("Lessons fetched:", data);
+
+    return data;
+  } catch (error) {
+    console.error("Error fetching lessons:", error);
+
+    throw error;
+  }
 }
 
-export async function submitQuiz(data: QuizAnswerSubmittion): Promise<QuizResult> {
-    try {
-        const response = await fetch(`${backendUrl}/quiz/submit`, {
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify(data),
-        });
+export async function updateLesson(
+  lessonId: string,
+  updatedData: Partial<LessonData>
+): Promise<LessonData> {
+  return updateItem<LessonData>(lessonId, updatedData, "lesson", "lesson");
+}
 
-        if (!response.ok) {
-            throw new Error('Failed to submit quiz');
-        }
+export async function getQuizzes(lessonId: string): Promise<QuizData[]> {
+  try {
+    const response = await fetch(`${backendUrl}/quiz?lessonId=${lessonId}`, {
+      method: "GET",
+      headers: {
+        "Content-Type": "application/json",
+      },
+    });
+
+    if (!response.ok) {
+      throw new Error("Failed to fetch quizzes");
+    }
 
-        const result = await response.json();
-        console.log('Quiz submitted:', result);
+    const data = await response.json();
+    console.log("Quizzes fetched:", data);
 
-        return result;
+    return data;
+  } catch (error) {
+    console.error("Error fetching quizzes:", error);
+    throw error;
+  }
+}
 
-    } catch (error) {
-        console.error('Error submitting quiz:', error);
+export async function deleteItem(
+  itemId: string,
+  path: string,
+  itemType: String
+): Promise<void> {
+  try {
+    const response = await fetch(`${backendUrl}/${path}/${itemId}`, {
+      method: "DELETE",
+      headers: {
+        "Content-Type": "application/json",
+      },
+    });
+
+    if (!response.ok) {
+      throw new Error(`Failed to delete ${itemType}`);
+    }
 
-        throw error;
+    console.log(`${itemType} with ID ${itemId} deleted successfully.`);
+  } catch (error) {
+    console.error(`Error deleting ${itemType}:`, error);
+    throw error;
+  }
+}
+
+export async function deleteLesson(lessonId: string): Promise<void> {
+  return deleteItem(lessonId, "lesson", "lesson");
+}
+
+export async function deleteQuiz(quizId: string): Promise<void> {
+  return deleteItem(quizId, "quiz", "quiz");
+}
+
+export async function updateItem<T>(
+  itemId: string,
+  updatedData: Partial<T>,
+  path: string,
+  itemType: string
+): Promise<T> {
+  try {
+    const response = await fetch(`${backendUrl}/${path}/${itemId}`, {
+      method: "PUT",
+      headers: {
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify(updatedData),
+    });
+
+    if (!response.ok) {
+      throw new Error(`Failed to update ${itemType}`);
     }
 
+    const data = await response.json();
+    console.log(`${itemType} with ID ${itemId} updated successfully:`, data);
+
+    return data;
+  } catch (error) {
+    console.error(`Error updating ${itemType}:`, error);
+    throw error;
+  }
+}
+
+export async function updateQuiz(
+  quizId: string,
+  updatedData: Partial<QuizData>
+): Promise<QuizData> {
+  return updateItem<QuizData>(quizId, updatedData, "quiz", "quiz");
 }
diff --git a/src/services/backend/types.ts b/src/services/backend/types.ts
index 395857e..20cf26b 100644
--- a/src/services/backend/types.ts
+++ b/src/services/backend/types.ts
@@ -1,47 +1,47 @@
 export type QuizSettings = {
-    checkType: 'onSubmit' | 'onSelectAnswer';
-    isRandomOrder: boolean;
-    maxQuestionCount: number;
-    solvingTimeMs: number;
+  checkType: "onSubmit" | "onSelectAnswer";
+  isRandomOrder: boolean;
+  maxQuestionCount: number;
+  solvingTimeMs: number;
 };
 
 export type LessonData = {
-    _id: string;
-    title: string;
-    summary: string;
+  _id: string;
+  title: string;
+  summary: string;
 };
 
 export type QuizData = {
+  _id: string;
+  title: string;
+  lessonId: string;
+  questions: {
     _id: string;
-    tille: string;
-    lessonId: string;
-    questions: {
-        _id: string;
-        text: string;
-        answers: string[];
-    }[],
-    settings: QuizSettings;
+    text: string;
+    answers: string[];
+  }[];
+  settings: QuizSettings;
 };
 
 export type QuizAnswerSubmittion = {
-    quizId: string;
-    questions: QuestionAnswerSubmittion[];
+  quizId: string;
+  questions: QuestionAnswerSubmittion[];
 };
 
 export type QuestionAnswerSubmittion = {
-    questionId: string;
-    selectedAnswer: string;
+  questionId: string;
+  selectedAnswer: string;
 };
 
 export type QuizResult = {
-    quizId: string;
-    results: QuestionResults[];
-    score: number;
+  quizId: string;
+  results: QuestionResults[];
+  score: number;
 };
 
 export type QuestionResults = {
-    questionId: string;
-    selectedAnswer: string;
-    correctAnswer: string;
-    isCorrect: boolean;
+  questionId: string;
+  selectedAnswer: string;
+  correctAnswer: string;
+  isCorrect: boolean;
 };


Reviews:
user: login: tomerProg
body: you missed alot, 

1. search input (find only by title but put the rer something to mach the mock)
2. lesson card should be pinkier like the mock
3. where is the success rate (put trash value but so the ui will have it ready)
4. opening lesson should be a page of itself, i try to go back and i go to the create lesson instead of the history page
5. the page for single lesson is not orginaized (the attempts should be in the right and in the left should be the title and summary like we talked about. i know its not in the mocks and you had free hand but tou cant just put it under it in stack

optional:
the attempts list (the whole card with the title) have big font size maybr we would want to decrease it little bit
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-18T13:39:53Z
user: login: tomerProg
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-26T19:27:50Z
user: login: tomerProg
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-04-26T23:12:12Z
user: login: tomerProg
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-26T23:13:03Z
user: login: gil392
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-27T07:51:12Z
user: login: gil392
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-27T07:52:18Z
user: login: gil392
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-27T07:52:56Z
user: login: gil392
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-27T08:02:16Z
user: login: gil392
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-27T08:15:18Z
user: login: tomerProg
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-27T20:43:25Z

Review comments:
diff_hunk: @@ -1,24 +1,27 @@
-import { Navigate, Route, BrowserRouter as Router, Routes } from 'react-router-dom'
-import './App.css'
-import SummaryPage from './pages/Summary'
-import QuizPage from './pages/Quiz';
-import HomePage from './pages/home/Home'
-
+import {
+  Navigate,
+  Route,
+  BrowserRouter as Router,
+  Routes,
+} from "react-router-dom";
+import "./App.css";
+import SummaryPage from "./pages/Summary";
+import QuizPage from "./pages/Quiz";
+import HomePage from "./pages/home/Home";
+import LessonsPage from "./pages/lesson/LessonsPage";
 
 function App() {
-
   return (
     <Router>
-            <Routes>
-                <Route path="/" element={<Navigate to="/home" replace />} />
-                <Route path="/home" element={<HomePage />} />
-                <Route path="/summary" element={<SummaryPage />} />
-                <Route path="/quiz" element={<QuizPage />}
-                />
-            </Routes>
+      <Routes>
+        <Route path="/" element={<Navigate to="/home" replace />} />
+        <Route path="/home" element={<HomePage />} />
+        <Route path="/summary" element={<SummaryPage />} />
+        <Route path="/quiz" element={<QuizPage />} />
+        <Route path="/lesson" element={<LessonsPage />} />
path: src/App.tsx
user: login: tomerProg
body: that supose to be the home page, am i right?
created_at: 2025-04-18T11:50:14Z
author_association: COLLABORATOR
diff_hunk: @@ -1,24 +1,27 @@
-import { Navigate, Route, BrowserRouter as Router, Routes } from 'react-router-dom'
-import './App.css'
-import SummaryPage from './pages/Summary'
-import QuizPage from './pages/Quiz';
-import HomePage from './pages/home/Home'
-
+import {
+  Navigate,
+  Route,
+  BrowserRouter as Router,
+  Routes,
+} from "react-router-dom";
+import "./App.css";
+import SummaryPage from "./pages/Summary";
+import QuizPage from "./pages/Quiz";
+import HomePage from "./pages/home/Home";
+import LessonsPage from "./pages/lesson/LessonsPage";
 
 function App() {
-
   return (
     <Router>
-            <Routes>
-                <Route path="/" element={<Navigate to="/home" replace />} />
-                <Route path="/home" element={<HomePage />} />
-                <Route path="/summary" element={<SummaryPage />} />
-                <Route path="/quiz" element={<QuizPage />}
-                />
-            </Routes>
+      <Routes>
+        <Route path="/" element={<Navigate to="/home" replace />} />
+        <Route path="/home" element={<HomePage />} />
+        <Route path="/summary" element={<SummaryPage />} />
+        <Route path="/quiz" element={<QuizPage />} />
+        <Route path="/lesson" element={<LessonsPage />} />
path: src/App.tsx
user: login: tomerProg
body: replace the navigation so it will redirect there
created_at: 2025-04-18T11:50:35Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,31 @@
+import { makeStyles, createStyles } from "@mui/styles";
+
+const useStyles = makeStyles(() =>
+  createStyles({
path: src/components/EditableActions.styles.ts
user: login: tomerProg
body: not what i ment, i wanted to use only createStyles. but because of you i search more about how you need to style with mui. maby we will use the style property and pass it literal object from the style.ts file.
created_at: 2025-04-26T19:27:50Z
author_association: COLLABORATOR
path: src/components/EditableActions.tsx
user: login: tomerProg
body: seems like it is editable title, not just action
created_at: 2025-04-26T19:31:55Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,99 @@
+import { useState } from "react";
+import { TextField, Box, Typography } from "@mui/material";
+import EditIcon from "@mui/icons-material/Edit";
+import CheckIcon from "@mui/icons-material/Check";
+import CloseIcon from "@mui/icons-material/Close";
+import DeleteIcon from "@mui/icons-material/Delete";
+import { GenericIconButton } from "./GenericIconButton";
+import useStyles from "./EditableActions.styles";
+
+type EditableActionsProps = {
+  title: string;
+  onSave: (newTitle: string) => void;
+  onDelete: () => void;
+  onEditModeChange?: (isEditing: boolean) => void;
+};
+
+const EditableActions = ({
path: src/components/EditableActions.tsx
user: login: tomerProg
body: type for FunctionComponent
created_at: 2025-04-26T19:32:48Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,99 @@
+import { useState } from "react";
+import { TextField, Box, Typography } from "@mui/material";
+import EditIcon from "@mui/icons-material/Edit";
+import CheckIcon from "@mui/icons-material/Check";
+import CloseIcon from "@mui/icons-material/Close";
+import DeleteIcon from "@mui/icons-material/Delete";
+import { GenericIconButton } from "./GenericIconButton";
+import useStyles from "./EditableActions.styles";
+
+type EditableActionsProps = {
+  title: string;
+  onSave: (newTitle: string) => void;
+  onDelete: () => void;
+  onEditModeChange?: (isEditing: boolean) => void;
+};
+
+const EditableActions = ({
+  title,
+  onSave,
+  onDelete,
+  onEditModeChange,
+}: EditableActionsProps) => {
+  const classes = useStyles();
+  const [isEditing, setIsEditing] = useState(false);
+  const [newTitle, setNewTitle] = useState(title);
+
+  const handleEditClick = (event: React.MouseEvent) => {
+    event.stopPropagation();
+    setIsEditing(true);
+    onEditModeChange?.(true);
path: src/components/EditableActions.tsx
user: login: tomerProg
body: what? is it possible? my advice give it default value of function that does nothing.
created_at: 2025-04-26T19:34:11Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,26 @@
+import React from "react";
+import { IconButton, Tooltip } from "@mui/material";
+
+interface GenericIconButtonProps {
+  title: string;
+  icon: React.ReactElement;
+  onClick?: (event: React.MouseEvent) => void;
+}
+
+export const GenericIconButton: React.FC<GenericIconButtonProps> = ({
path: src/components/GenericIconButton.tsx
user: login: tomerProg
body: i preffer using the type FunctionComponent without the `React.`
created_at: 2025-04-26T19:35:58Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,26 @@
+import React from "react";
+import { IconButton, Tooltip } from "@mui/material";
+
+interface GenericIconButtonProps {
+  title: string;
+  icon: React.ReactElement;
+  onClick?: (event: React.MouseEvent) => void;
+}
+
+export const GenericIconButton: React.FC<GenericIconButtonProps> = ({
path: src/components/GenericIconButton.tsx
user: login: tomerProg
body: not must!
created_at: 2025-04-26T19:36:11Z
author_association: COLLABORATOR
path: package.json
user: login: tomerProg
body: npm i @mui/styles
created_at: 2025-04-26T19:38:43Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,34 @@
+import { useState, useEffect } from "react";
+import { useNavigate, useLocation } from "react-router-dom";
+
+export const usePopupNavigation = (
+  basePath: string,
+  popupPath: string,
+  onClosePopup?: () => void
path: src/hooks/usePopupNavigation.ts
user: login: tomerProg
body: use default function to avoid the ? symbol
created_at: 2025-04-26T19:41:04Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,144 @@
+import Box from "@mui/material/Box";
+import {
+  LessonData,
+  QuizData,
+  QuizSettings,
+} from "../../../services/backend/types";
+import { useState, useEffect } from "react";
+import {
+  deleteQuiz,
+  getQuizzes,
+  updateQuiz,
+} from "../../../services/backend/service";
+import QuizItem from "../QuizItem/QuizItem";
+import {
+  Card,
+  CardContent,
+  CardActions,
+  Button,
+  Typography,
+  Collapse,
+} from "@mui/material";
+import { useNavigate } from "react-router-dom";
+import useStyles from "./LessonInfo.styles";
+
+interface LessonInfoProps {
+  lesson: LessonData;
+  onClose: () => void;
+}
+
+const LessonInfo: React.FC<LessonInfoProps> = ({ lesson, onClose }) => {
+  const [quizzes, setQuizzes] = useState<QuizData[]>([]);
+  const [isSummaryExpanded, setIsSummaryExpanded] = useState(true); // Default to true (show summary)
+
+  const navigate = useNavigate();
+  /** TODO: Issue 14 (Itay)- get these settings not hard-coded */
+  const quizSettings: QuizSettings = {
+    checkType: "onSubmit",
+    isRandomOrder: true,
+    maxQuestionCount: 10,
+    solvingTimeMs: 60000,
+  };
+
+  const onCreateQuiz = () => {
+    navigate("/quiz", { state: { lessonData: lesson, quizSettings } });
+  };
+
+  useEffect(() => {
+    const fetchQuizzesByLessonId = async () => {
+      try {
+        const response = await getQuizzes(lesson._id);
+        setQuizzes(response);
+      } catch (error) {
+        console.error("Error fetching lessons:", error);
+      }
+    };
+
+    fetchQuizzesByLessonId();
+  }, []);
+
+  const handleDeleteQuiz = async (quizId: string) => {
+    await deleteQuiz(quizId);
+    setQuizzes((prevQuizzes) =>
+      prevQuizzes.filter((quiz) => quiz._id !== quizId)
+    );
+  };
+
+  const handleUpdateQuiz = async (quiz: QuizData) => {
+    await updateQuiz(quiz._id, quiz);
+    setQuizzes((prevQuizzes) =>
+      prevQuizzes.map((q) => (q._id === quiz._id ? quiz : q))
+    );
+  };
+
+  const classes = useStyles();
+
+  return (
+    <Box className={classes.container}>
+      <Box className={classes.leftBox}>
+        <Typography
+          sx={{
+            fontSize: "1.2rem", // Your desired font size
+            fontWeight: "bold", // Your desired font weight
+            marginBottom: "0.5vh", // Your desired margin
+          }}
path: src/pages/lesson/LessonInfo/LessonInfo.tsx
user: login: tomerProg
body: in styles file
created_at: 2025-04-26T22:57:30Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,41 @@
+import Box from "@mui/material/Box";
+import { LessonData } from "../../../services/backend/types";
+import EditableActions from "../../../components/EditableActions";
+import { useState } from "react";
+import { Typography } from "@mui/material";
+import useStyles from "./LessonItem.styles";
+
+type LessonItemProps = {
+  lesson: LessonData;
+  onLessonDeleted: (lessonId: string) => void;
+  updateLessonTitle: (newTitle: string) => void;
+  openLesson: () => void;
+};
+
+const LessonItem = (props: LessonItemProps) => {
path: src/pages/lesson/LessonItem/LessonItem.tsx
user: login: tomerProg
body: type of  FunctionComponent<LessonItempProps>
created_at: 2025-04-26T22:59:18Z
author_association: COLLABORATOR
diff_hunk: @@ -1,88 +1,215 @@
-import { LessonData, QuizData, QuizSettings, QuizAnswerSubmittion, QuizResult } from "./types";
+import {
+  LessonData,
+  QuizData,
+  QuizSettings,
+  QuizAnswerSubmittion,
+  QuizResult,
+} from "./types";
+
+const backendUrl = import.meta.env.VITE_BACKEND_URL;
+export async function generateLesson(videoUrl: string): Promise<LessonData> {
+  try {
+    const response = await fetch(`${backendUrl}/lesson`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify({
+        videoUrl,
+      }),
+    });
+
+    if (!response.ok) {
+      throw new Error("Failed to generate lesson");
+    }
 
-const backendUrl = import.meta.env.VITE_BACKEND_URL; 
+    const data = await response.json();
+    console.log("Lesson generated:", data);
 
-export async function generateLesson(videoUrl: string): Promise<LessonData> {
-    try {
-        const response = await fetch(`${backendUrl}/lesson`, {
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify({
-                videoUrl,
-            }),
-        });
-
-        if (!response.ok) {
-            throw new Error('Failed to generate lesson');
-        }
-
-        const data = await response.json();
-        console.log('Lesson generated:', data);
-
-        return data;
-
-    } catch (error) {
-        console.error('Error generating lesson:', error);
-
-        throw error; 
+    return data;
+  } catch (error) {
+    console.error("Error generating lesson:", error);
+
+    throw error;
+  }
+}
+
+export async function generateQuiz(
+  lessonId: string,
+  settings: QuizSettings
+): Promise<QuizData> {
+  try {
+    const response = await fetch(`${backendUrl}/quiz`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify({
+        lessonId,
+        settings,
+      }),
+    });
+
+    if (!response.ok) {
+      throw new Error("Failed to generate quiz");
     }
+
+    const data = await response.json();
+    console.log("Quiz generated:", data);
+
+    return data;
+  } catch (error) {
+    console.error("Error generating quiz:", error);
+
+    throw error;
+  }
 }
 
+export async function submitQuiz(
+  data: QuizAnswerSubmittion
+): Promise<QuizResult> {
+  try {
+    const response = await fetch(`${backendUrl}/quiz/submit`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify(data),
+    });
+
+    if (!response.ok) {
+      throw new Error("Failed to submit quiz");
+    }
+
+    const result = await response.json();
+    console.log("Quiz submitted:", result);
+
+    return result;
+  } catch (error) {
+    console.error("Error submitting quiz:", error);
 
-export async function generateQuiz(lessonId: string, settings: QuizSettings): Promise<QuizData> {
-    try {
-        const response = await fetch(`${backendUrl}/quiz`, {
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify({
-                lessonId,
-                settings,
-            }),
-        });
-
-        if (!response.ok) {
-            throw new Error('Failed to generate quiz');
-        }
-
-        const data = await response.json();
-        console.log('Quiz generated:', data);
-
-        return data; 
-
-    } catch (error) {
-        console.error('Error generating quiz:', error);
-        
-        throw error; 
+    throw error;
+  }
+}
+
+export async function getLessons(): Promise<LessonData[]> {
+  try {
+    const response = await fetch(`${backendUrl}/lesson/`, {
+      method: "GET",
+      headers: {
+        "Content-Type": "application/json",
+      },
+    });
+
+    if (!response.ok) {
+      throw new Error("Failed to fetch lessons");
     }
+
+    const data = await response.json();
+    console.log("Lessons fetched:", data);
+
+    return data;
+  } catch (error) {
+    console.error("Error fetching lessons:", error);
+
+    throw error;
+  }
+}
+
+export async function updateLesson(
+  lessonId: string,
+  updatedData: Partial<LessonData>
+): Promise<LessonData> {
+  return updateItem<LessonData>(lessonId, updatedData, "lesson", "lesson");
 }
 
-export async function submitQuiz(data: QuizAnswerSubmittion): Promise<QuizResult> {
-    try {
-        const response = await fetch(`${backendUrl}/quiz/submit`, {
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify(data),
-        });
+export async function getQuizzes(lessonId: string): Promise<QuizData[]> {
+  try {
+    const response = await fetch(`${backendUrl}/quiz?lessonId=${lessonId}`, {
+      method: "GET",
+      headers: {
+        "Content-Type": "application/json",
+      },
+    });
+
+    if (!response.ok) {
+      throw new Error("Failed to fetch quizzes");
+    }
 
-        if (!response.ok) {
-            throw new Error('Failed to submit quiz');
-        }
+    const data = await response.json();
+    console.log("Quizzes fetched:", data);
 
-        const result = await response.json();
-        console.log('Quiz submitted:', result);
+    return data;
+  } catch (error) {
+    console.error("Error fetching quizzes:", error);
+    throw error;
+  }
+}
 
-        return result; 
+export async function deleteItem(
+  itemId: string,
+  path: string,
+  itemType: String
path: src/services/backend/service.ts
user: login: tomerProg
body: 1. dont do generic function for all deletes, you dont know how suddenly we choose to delete only by the lesson id and then the functions not the same
2. path and item are the same (also by its meaning, because we doing REST api the item type should be its path so you can delete one of the and use it as the same (i think you shoul delete the path)

choose one of this options
created_at: 2025-04-26T23:10:42Z
author_association: COLLABORATOR
path: src/services/backend/service.ts
user: login: tomerProg
body: can i ask why dont we use axios, just like in class and real life, it is more helpful when creating the client with base url and interceptors for tokens
created_at: 2025-04-26T23:12:05Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,41 @@
+import Box from "@mui/material/Box";
+import { LessonData } from "../../../services/backend/types";
+import EditableActions from "../../../components/EditableActions";
+import { useState } from "react";
+import { Typography } from "@mui/material";
+import useStyles from "./LessonItem.styles";
+
+type LessonItemProps = {
+  lesson: LessonData;
+  onLessonDeleted: (lessonId: string) => void;
+  updateLessonTitle: (newTitle: string) => void;
+  openLesson: () => void;
+};
+
+const LessonItem = (props: LessonItemProps) => {
path: src/pages/lesson/LessonItem/LessonItem.tsx
user: login: tomerProg
body: not must!
created_at: 2025-04-26T23:13:03Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,99 @@
+import { useState } from "react";
+import { TextField, Box, Typography } from "@mui/material";
+import EditIcon from "@mui/icons-material/Edit";
+import CheckIcon from "@mui/icons-material/Check";
+import CloseIcon from "@mui/icons-material/Close";
+import DeleteIcon from "@mui/icons-material/Delete";
+import { GenericIconButton } from "./GenericIconButton";
+import useStyles from "./EditableActions.styles";
+
+type EditableActionsProps = {
+  title: string;
+  onSave: (newTitle: string) => void;
+  onDelete: () => void;
+  onEditModeChange?: (isEditing: boolean) => void;
+};
+
+const EditableActions = ({
+  title,
+  onSave,
+  onDelete,
+  onEditModeChange,
+}: EditableActionsProps) => {
+  const classes = useStyles();
+  const [isEditing, setIsEditing] = useState(false);
+  const [newTitle, setNewTitle] = useState(title);
+
+  const handleEditClick = (event: React.MouseEvent) => {
+    event.stopPropagation();
+    setIsEditing(true);
+    onEditModeChange?.(true);
path: src/components/EditableActions.tsx
user: login: gil392
body: It means that if the function doesn't exist, don't run it. But if it does exist, run it with the value "true". It's better than to send a default function.
created_at: 2025-04-27T07:51:12Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,34 @@
+import { useState, useEffect } from "react";
+import { useNavigate, useLocation } from "react-router-dom";
+
+export const usePopupNavigation = (
+  basePath: string,
+  popupPath: string,
+  onClosePopup?: () => void
path: src/hooks/usePopupNavigation.ts
user: login: gil392
body: Explained in above comment why it's not best practice to change it like you suggested.
created_at: 2025-04-27T07:52:18Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,41 @@
+import Box from "@mui/material/Box";
+import { LessonData } from "../../../services/backend/types";
+import EditableActions from "../../../components/EditableActions";
+import { useState } from "react";
+import { Typography } from "@mui/material";
+import useStyles from "./LessonItem.styles";
+
+type LessonItemProps = {
+  lesson: LessonData;
+  onLessonDeleted: (lessonId: string) => void;
+  updateLessonTitle: (newTitle: string) => void;
+  openLesson: () => void;
+};
+
+const LessonItem = (props: LessonItemProps) => {
path: src/pages/lesson/LessonItem/LessonItem.tsx
user: login: gil392
body: You told me in previous note in this same PR to get rid of the React.FC, so I'm confused 😂
created_at: 2025-04-27T07:52:56Z
author_association: OWNER
diff_hunk: @@ -1,88 +1,215 @@
-import { LessonData, QuizData, QuizSettings, QuizAnswerSubmittion, QuizResult } from "./types";
+import {
+  LessonData,
+  QuizData,
+  QuizSettings,
+  QuizAnswerSubmittion,
+  QuizResult,
+} from "./types";
+
+const backendUrl = import.meta.env.VITE_BACKEND_URL;
+export async function generateLesson(videoUrl: string): Promise<LessonData> {
+  try {
+    const response = await fetch(`${backendUrl}/lesson`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify({
+        videoUrl,
+      }),
+    });
+
+    if (!response.ok) {
+      throw new Error("Failed to generate lesson");
+    }
 
-const backendUrl = import.meta.env.VITE_BACKEND_URL; 
+    const data = await response.json();
+    console.log("Lesson generated:", data);
 
-export async function generateLesson(videoUrl: string): Promise<LessonData> {
-    try {
-        const response = await fetch(`${backendUrl}/lesson`, {
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify({
-                videoUrl,
-            }),
-        });
-
-        if (!response.ok) {
-            throw new Error('Failed to generate lesson');
-        }
-
-        const data = await response.json();
-        console.log('Lesson generated:', data);
-
-        return data;
-
-    } catch (error) {
-        console.error('Error generating lesson:', error);
-
-        throw error; 
+    return data;
+  } catch (error) {
+    console.error("Error generating lesson:", error);
+
+    throw error;
+  }
+}
+
+export async function generateQuiz(
+  lessonId: string,
+  settings: QuizSettings
+): Promise<QuizData> {
+  try {
+    const response = await fetch(`${backendUrl}/quiz`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify({
+        lessonId,
+        settings,
+      }),
+    });
+
+    if (!response.ok) {
+      throw new Error("Failed to generate quiz");
     }
+
+    const data = await response.json();
+    console.log("Quiz generated:", data);
+
+    return data;
+  } catch (error) {
+    console.error("Error generating quiz:", error);
+
+    throw error;
+  }
 }
 
+export async function submitQuiz(
+  data: QuizAnswerSubmittion
+): Promise<QuizResult> {
+  try {
+    const response = await fetch(`${backendUrl}/quiz/submit`, {
+      method: "POST",
+      headers: {
+        "Content-Type": "application/json",
+      },
+      body: JSON.stringify(data),
+    });
+
+    if (!response.ok) {
+      throw new Error("Failed to submit quiz");
+    }
+
+    const result = await response.json();
+    console.log("Quiz submitted:", result);
+
+    return result;
+  } catch (error) {
+    console.error("Error submitting quiz:", error);
 
-export async function generateQuiz(lessonId: string, settings: QuizSettings): Promise<QuizData> {
-    try {
-        const response = await fetch(`${backendUrl}/quiz`, {
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify({
-                lessonId,
-                settings,
-            }),
-        });
-
-        if (!response.ok) {
-            throw new Error('Failed to generate quiz');
-        }
-
-        const data = await response.json();
-        console.log('Quiz generated:', data);
-
-        return data; 
-
-    } catch (error) {
-        console.error('Error generating quiz:', error);
-        
-        throw error; 
+    throw error;
+  }
+}
+
+export async function getLessons(): Promise<LessonData[]> {
+  try {
+    const response = await fetch(`${backendUrl}/lesson/`, {
+      method: "GET",
+      headers: {
+        "Content-Type": "application/json",
+      },
+    });
+
+    if (!response.ok) {
+      throw new Error("Failed to fetch lessons");
     }
+
+    const data = await response.json();
+    console.log("Lessons fetched:", data);
+
+    return data;
+  } catch (error) {
+    console.error("Error fetching lessons:", error);
+
+    throw error;
+  }
+}
+
+export async function updateLesson(
+  lessonId: string,
+  updatedData: Partial<LessonData>
+): Promise<LessonData> {
+  return updateItem<LessonData>(lessonId, updatedData, "lesson", "lesson");
 }
 
-export async function submitQuiz(data: QuizAnswerSubmittion): Promise<QuizResult> {
-    try {
-        const response = await fetch(`${backendUrl}/quiz/submit`, {
-            method: 'POST',
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify(data),
-        });
+export async function getQuizzes(lessonId: string): Promise<QuizData[]> {
+  try {
+    const response = await fetch(`${backendUrl}/quiz?lessonId=${lessonId}`, {
+      method: "GET",
+      headers: {
+        "Content-Type": "application/json",
+      },
+    });
+
+    if (!response.ok) {
+      throw new Error("Failed to fetch quizzes");
+    }
 
-        if (!response.ok) {
-            throw new Error('Failed to submit quiz');
-        }
+    const data = await response.json();
+    console.log("Quizzes fetched:", data);
 
-        const result = await response.json();
-        console.log('Quiz submitted:', result);
+    return data;
+  } catch (error) {
+    console.error("Error fetching quizzes:", error);
+    throw error;
+  }
+}
 
-        return result; 
+export async function deleteItem(
+  itemId: string,
+  path: string,
+  itemType: String
path: src/services/backend/service.ts
user: login: gil392
body: Not seeing a problem here. For now it's the same implementation, so a generic function is the best option. If there is another implementation needed in the future, either expand this function (like I did with updateItem) or create a non-generic one for that specific use case. But for now it's good to have it, and deleteQuiz and deleteLesson do exist as their own functions that use it. 

If you want, I can make deleteItem not "export" so outside places can't use it.
created_at: 2025-04-27T08:02:16Z
author_association: OWNER
path: src/components/EditableActions.tsx
user: login: gil392
body: Will rename to EditableTitleWithActions
created_at: 2025-04-27T08:15:18Z
author_association: OWNER

PR comments:
user: login: tomerProg
created_at: 2025-04-18T13:41:14Z
body: i didnt went over the files fix it then i will go over them
author_association: COLLABORATOR
user: login: gil392
created_at: 2025-04-18T19:23:21Z
body: > you missed alot,
> 
> 1. search input (find only by title but put the rer something to mach the mock)
> 2. lesson card should be pinkier like the mock
> 3. where is the success rate (put trash value but so the ui will have it ready)
> 4. opening lesson should be a page of itself, i try to go back and i go to the create lesson instead of the history page
> 5. the page for single lesson is not orginaized (the attempts should be in the right and in the left should be the title and summary like we talked about. i know its not in the mocks and you had free hand but tou cant just put it under it in stack
> 
> optional: the attempts list (the whole card with the title) have big font size maybr we would want to decrease it little bit

1 is in the issue "filter quizzes", 5 isrelated to the attempts issue, not really for this stage. Fixed the rest.

author_association: OWNER