}>
diff --git a/src/features/Lessons/LessonPage/pexels-photo-302902.webp b/src/features/Lessons/LessonPage/pexels-photo-302902.webp
deleted file mode 100644
index 8db569c..0000000
Binary files a/src/features/Lessons/LessonPage/pexels-photo-302902.webp and /dev/null differ
diff --git a/src/features/Lessons/LessonPageEditor/Droppable.tsx b/src/features/Lessons/LessonPageEditor/Droppable.tsx
new file mode 100644
index 0000000..130d6cb
--- /dev/null
+++ b/src/features/Lessons/LessonPageEditor/Droppable.tsx
@@ -0,0 +1,52 @@
+import { DndContext, DragEndEvent, useDroppable } from "@dnd-kit/core";
+import { verticalListSortingStrategy, SortableContext } from "@dnd-kit/sortable";
+import SortableEditorItem from "./SortableEditorItem";
+import React from "react";
+import { LessonContent } from "../LessonPage";
+import { store } from "core/store/store";
+
+import {
+ restrictToVerticalAxis,
+ restrictToWindowEdges,
+} from "@dnd-kit/modifiers";
+import { lessonContents, onDragHandler } from "./lessonPageEditorSlice";
+
+const Droppable = ({ items }: { items: LessonContent[] }) => {
+ const droppableID = "editorComponentArea";
+ const { setNodeRef } = useDroppable({ id: droppableID });
+
+
+ const itemIDs = items.map((item) => item.id);
+
+ return (
+
+
+
+ {items.map((item) => (
+
+ ))}
+
+
+
+ );
+};
+
+function handleDragEnd(event: DragEndEvent) {
+ console.log("drag end",event);
+
+ if(!event.over) return;
+
+ const activeId = event.active.id;
+ const overId = event.over.id;
+
+
+
+ store.dispatch(onDragHandler({activeId, overId}));
+}
+
+
+export default Droppable;
diff --git a/src/features/Lessons/LessonPageEditor/SortableEditorItem.tsx b/src/features/Lessons/LessonPageEditor/SortableEditorItem.tsx
new file mode 100644
index 0000000..b280b33
--- /dev/null
+++ b/src/features/Lessons/LessonPageEditor/SortableEditorItem.tsx
@@ -0,0 +1,57 @@
+import React from "react";
+import { defaultAnimateLayoutChanges, useSortable } from "@dnd-kit/sortable";
+import { CSS } from "@dnd-kit/utilities";
+import { Converter, LessonContent } from "../LessonPage";
+import { HolderOutlined, DeleteOutlined } from "@ant-design/icons";
+import { Flex } from "antd";
+import { setLessonContent, deleteLessonContent } from "./lessonPageEditorSlice";
+import { useDispatch } from "react-redux";
+
+const animateLayoutChanges = (args: any) =>
+ args.isSorting || args.wasDragging ? defaultAnimateLayoutChanges(args) : true;
+
+const SortableEditorItem = (props: {item:LessonContent}) => {
+ const lnContent = props.item;
+ const {
+ attributes,
+ listeners,
+ setNodeRef,
+ transform,
+ transition
+ } = useSortable({ id: lnContent.id, animateLayoutChanges });
+
+ const dispatch = useDispatch();
+
+
+ return (
+
+
+
+
+ dispatch(
+ setLessonContent({
+ id: lnContent.id,
+ data: data,
+ })
+ )
+ }
+ />
+
+ {
+ console.log("delete", lnContent.id);
+ dispatch(deleteLessonContent(lnContent.id));
+ }}
+ />
+
+
+ );
+};
+
+export default SortableEditorItem;
diff --git a/src/features/Lessons/LessonPageEditor/index.tsx b/src/features/Lessons/LessonPageEditor/index.tsx
index 73f1d14..a004b8e 100644
--- a/src/features/Lessons/LessonPageEditor/index.tsx
+++ b/src/features/Lessons/LessonPageEditor/index.tsx
@@ -1,20 +1,65 @@
import { useNavigate, useParams } from "react-router-dom";
import {
- deleteLessonContent,
lessonContents,
lessonThumbnail,
setEditorActive,
- setLessonContent,
- setLessonThumbnailTitle,
} from "./lessonPageEditorSlice";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
-import { Converter, LessonContent } from "../LessonPage";
-import { Card, Flex, Typography } from "antd";
-import { DeleteOutlined } from "@ant-design/icons";
+import { LessonContent } from "../LessonPage";
+import { Card, Flex } from "antd";
import { Constants } from "../../../core/utils/utils";
import HeaderBar from "../../../core/components/Header";
-import img from "../LessonPage/pexels-photo-302902.webp";
+import Droppable from "./Droppable";
+import LessonPreviewCard from "../../../shared/components/MyLessonPreviewCard";
+import {
+ useGetLessonPreviewQuery,
+ useUpdateLessonPreviewTitleMutation,
+} from "core/services/lessons";
+import MyErrorResult from "shared/components/MyResult";
+import styles from "./styles.module.css";
+
+const PreviewCard: React.FC = () => {
+ const { lessonId } = useParams();
+
+ const { data, error, isLoading, refetch } = useGetLessonPreviewQuery(
+ lessonId as string,
+ {
+ refetchOnMountOrArgChange: true,
+ }
+ );
+
+ const [updateLessonPreviewTitle, {}] = useUpdateLessonPreviewTitleMutation();
+
+ if (error) return ;
+
+ return (
+ {
+ try {
+ const res = await updateLessonPreviewTitle({
+ lessonId: lessonId as string,
+ newTitle: newTitle,
+ }).unwrap();
+
+ if (res) {
+ refetch();
+ }
+ } catch (err) {
+ console.error(err);
+ }
+ }}
+ onThumbnailChanged={refetch}
+ />
+ );
+};
export default function LessonPageEditor() {
const { lessonId } = useParams();
@@ -51,65 +96,17 @@ export default function LessonPageEditor() {
/>
-
-
-
-
+
+
-
-
- dispatch(setLessonThumbnailTitle(event)),
- }}
- style={{
- width: "100%",
- }}
- >
- {lnThumbnail.title}
-
-
-
-
-
-
+
- {lnContents.map((lnContent) => (
-
-
- dispatch(
- setLessonContent({
- id: lnContent.id,
- data: data,
- })
- )
- }
- />
-
- {
- console.log("delete", lnContent.id);
- dispatch(deleteLessonContent(lnContent.id));
- }}
- />
-
- ))}
+
@@ -120,7 +117,7 @@ export default function LessonPageEditor() {
export function StandardEditorCompontent(type: number): LessonContent {
return {
- id: Math.floor(Math.random() * 100).toString(),
+ id: Math.floor(Math.random() * 10000).toString(),
position: 1,
type: type,
data: "Some data",
diff --git a/src/features/Lessons/LessonPageEditor/lessonPageEditorSlice.tsx b/src/features/Lessons/LessonPageEditor/lessonPageEditorSlice.tsx
index 45f41a2..94bc0c2 100644
--- a/src/features/Lessons/LessonPageEditor/lessonPageEditorSlice.tsx
+++ b/src/features/Lessons/LessonPageEditor/lessonPageEditorSlice.tsx
@@ -34,6 +34,27 @@ export const lessonPageEditorSlice = createSlice({
setLessonThumbnailTitle: (state, action) => {
state.lessonThumbnail.title = action.payload;
},
+ onDragHandler: (state, action) => {
+ const { activeId, overId } = action.payload as {
+ activeId: string;
+ overId: string;
+ };
+
+ if (activeId !== overId) {
+ let oldIndex = state.lessonContents.findIndex(
+ (item) => item.id === activeId
+ );
+ let newIndex = state.lessonContents.findIndex(
+ (item) => item.id === overId
+ );
+
+ state.lessonContents.splice(
+ newIndex,
+ 0,
+ state.lessonContents.splice(oldIndex, 1)[0]
+ );
+ }
+ },
},
selectors: {
editorActive: (state) => state.editorActive,
@@ -48,6 +69,7 @@ export const {
deleteLessonContent,
setLessonContent,
setLessonThumbnailTitle,
+ onDragHandler,
} = lessonPageEditorSlice.actions;
export const { editorActive, lessonContents, lessonThumbnail } =
diff --git a/src/features/Lessons/LessonPageEditor/styles.module.css b/src/features/Lessons/LessonPageEditor/styles.module.css
new file mode 100644
index 0000000..9b6a368
--- /dev/null
+++ b/src/features/Lessons/LessonPageEditor/styles.module.css
@@ -0,0 +1,10 @@
+.cardContainer {
+ width: 100%;
+}
+
+@media screen and (min-width: 750px) {
+ .cardContainer {
+ width: 100%;
+ max-width: 800px;
+ }
+}
diff --git a/src/features/Lessons/components.ts b/src/features/Lessons/components.ts
new file mode 100644
index 0000000..cb2a596
--- /dev/null
+++ b/src/features/Lessons/components.ts
@@ -0,0 +1,78 @@
+// Desc: This file contains the list of components that are used in the Lessons
+
+type ComponentGroup = {
+ category: string;
+ components: Component[];
+};
+
+export type Component = {
+ type: number;
+ name: string;
+ thumbnail?: string;
+ invertThumbnailAtDarkmode?: boolean;
+};
+
+const componentsGroups: ComponentGroup[] = [
+ {
+ category: "Common",
+ components: [
+ {
+ type: 0,
+ name: "Header",
+ thumbnail: "/editor/thumbnails/component_thumbnail_header.svg",
+ invertThumbnailAtDarkmode: true,
+ },
+ {
+ type: 1,
+ name: "Text",
+ thumbnail: "/editor/thumbnails/component_thumbnail_text.svg",
+ invertThumbnailAtDarkmode: true,
+ },
+ ],
+ },
+ {
+ category: "Media",
+ components: [
+ {
+ type: 0,
+ name: "Image",
+ thumbnail: "/editor/thumbnails/component_thumbnail_image.png",
+ },
+ {
+ type: 1,
+ name: "YouTube",
+ thumbnail: "/editor/thumbnails/component_thumbnail_youtube.png",
+ invertThumbnailAtDarkmode: true,
+ },
+ {
+ type: 1,
+ name: "Video",
+ thumbnail: "/editor/thumbnails/component_thumbnail_youtube.png",
+ invertThumbnailAtDarkmode: true,
+ },
+ ],
+ },
+ {
+ category: "HTML",
+ components: [
+ {
+ type: 0,
+ name: "Iframe",
+ thumbnail: "/editor/thumbnails/component_thumbnail_iframe.svg",
+ invertThumbnailAtDarkmode: true,
+ },
+ ],
+ },
+ {
+ category: "Special",
+ components: [
+ {
+ type: 0,
+ name: "Banner",
+ thumbnail: "/editor/thumbnails/component_thumbnail_banner.png",
+ },
+ ],
+ },
+];
+
+export { componentsGroups };
diff --git a/src/features/Lessons/index.tsx b/src/features/Lessons/index.tsx
index fcb2779..c948c96 100644
--- a/src/features/Lessons/index.tsx
+++ b/src/features/Lessons/index.tsx
@@ -1,63 +1,83 @@
-import { Button, Card, Flex, Segmented } from "antd";
+import { Button, Flex, Segmented } from "antd";
import MyBanner from "../../shared/components/MyBanner";
import { MyContainer } from "../../shared/components/MyContainer";
-
-import img1 from "./pexels-photo-1181625.webp";
-import img2 from "./pexels-photo-302894.webp";
import {
AppstoreOutlined,
BarsOutlined,
- CommentOutlined,
PlusOutlined,
} from "@ant-design/icons";
import Search, { SearchProps } from "antd/es/input/Search";
-import { Link } from "react-router-dom";
+import { useNavigate } from "react-router-dom";
import HeaderBar from "../../core/components/Header";
+import {
+ useCreateLessonMutation,
+ useGetLessonsQuery,
+} from "core/services/lessons";
+import MySpin from "shared/components/MySpin";
+import { Constants } from "core/utils/utils";
+import MyEmpty from "shared/components/MyEmpty";
+import MyErrorResult from "shared/components/MyResult";
+import LessonPreviewCard from "../../shared/components/MyLessonPreviewCard";
+
+const CreateLessonButton: React.FC = () => {
+ const navigate = useNavigate();
+ const [createLesson, { isLoading }] = useCreateLessonMutation();
+
+ const handleCreateLesson = async () => {
+ try {
+ const res = await createLesson({}).unwrap();
+
+ if (res && res.Id) {
+ navigate(
+ Constants.ROUTE_PATHS.LESSIONS.PAGE_EDITOR.replace(
+ ":lessonId",
+ res.Id
+ )
+ );
+ }
+ } catch (err) {
+ console.error(err);
+ }
+ };
-function ListItem({ img, title }: { img: string; title: string }) {
return (
-
-
-
-
-
-
-
- {title}
-
-
- 12 comments
-
-
-
-
-
+ }
+ onClick={handleCreateLesson}
+ loading={isLoading}
+ >
+ Create
+
);
-}
+};
-const data = [
- {
- img: img1,
- title: "How to clean the coffee machine",
- },
- {
- img: img2,
- title: "How to clean the coffee machine",
- },
- {
- img: img1,
- title: "How to clean the coffee machine",
- },
- {
- img: img2,
- title: "How to clean the coffee machine",
- },
-];
+const LessonList: React.FC = () => {
+ const { data, error, isLoading } = useGetLessonsQuery(undefined, {
+ refetchOnMountOrArgChange: true,
+ });
+
+ if (isLoading) return ;
+ if (error) return ;
+
+ if (!data || data.length === 0) return ;
+
+ return (
+ <>
+ {data.map((item, index) => (
+
+ ))}
+ >
+ );
+};
export default function Lessons() {
const onSearch: SearchProps["onSearch"] = (value, _e, info) =>
@@ -76,7 +96,7 @@ export default function Lessons() {
]}
/>
- }>Create
+
-
+
- {data.map((item, index) => (
-
- ))}
+
-